news 2026/10/9 9:07:10

Spring Bean生命周期详解:从实例化到销毁的完整链路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Bean生命周期详解:从实例化到销毁的完整链路

面试官:Spring Bean 生命周期详解?

面试被问到Spring Bean生命周期,很多人第一反应是背那套“实例化→属性填充→初始化→销毁”四段论,然后面试官追问一句“BeanPostProcessor是在哪一步介入的?”就卡壳了。再追问“构造方法抛异常了,Bean还会走初始化吗?”“原型Bean的生命周期和单例有什么本质区别?”基本就只剩尴尬了。

这篇文章我结合多年面试候选人和被面试的经验,把Bean生命周期从头到尾拆一遍,不光讲标准流程,还把常被追问的机制原理(比如三级缓存、循环依赖、代理对象生成时机)一并说清楚。文章面向无论是刚学Spring的初学者,还是准备跳槽的初中级Java工程师,都能拿走一套能直接用的答题框架。

1. 先建立整体认知:生命周期不是一条线,是两个阶段

1.1 不要背反了:真正被管理的只有“完整链路”

很多人理解Bean生命周期,是站在“对象”的视角去看:new出来、塞属性、初始化、用完销毁。这个理解没有错,但不够准确。Spring里说的Bean生命周期,严格来说是从BeanDefinition被解析注册开始,到Bean实例被创建、强化、初始化,最后被销毁回收的整个过程。它不是一个单纯的对象存活过程,而是包含了“定义”和“使用”两大阶段。

我习惯把Bean生命周期拆成两条线:

  • BeanDefinition阶段:Spring读取配置(XML、注解、JavaConfig),把每一个<bean>或@Bean包装成一个BeanDefinition对象,注册到BeanFactory。这一步很多人忽略,但它决定了后面实例化时的“图纸”长什么样。
  • Bean实例阶段:从getBean()触发开始,实例化、填充属性、执行Aware回调、经过BeanPostProcessor处理、执行初始化方法,最后放入单例池(或返回原型Bean)。

面试时如果你能主动把生命周期拆成这两个阶段,面试官会立刻觉得你不是在背八股,而是真的理解了Spring的设计意图。

1.2 网上流传的“生命周期流程图”为什么总是对不上

网上的生命周期图五花八门,有的把BeanNameAware放在属性填充前面,有的把InitializingBean放在init-method后面,有的干脆画了一大堆箭头。原因是Spring的版本一直在演进,从2.x到5.x,BeanPostProcessor的注册时机、注解驱动后的AutowiredAnnotationBeanPostProcessor介入方式都有差异,但没有本质性的流程变化。

我的建议是,不要试图背某一幅图,而是背下面这个经过验证的标准调用顺序:

BeanDefinition加载与解析 ↓ 实例化前(InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation) ↓ 构造方法实例化(推测构造方法,处理循环依赖时提前暴露早期引用) ↓ 实例化后(MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition) ↓ 属性填充(populateBean,处理@Autowired、@Resource、@Value) ↓ Aware回调(BeanNameAware → BeanClassLoaderAware → BeanFactoryAware) ↓ BeanPostProcessor.postProcessBeforeInitialization ↓ InitializingBean.afterPropertiesSet ↓ 自定义init-method ↓ BeanPostProcessor.postProcessAfterInitialization(AOP代理在此生成) ↓ Bean就绪,放入单例池 ↓ 容器关闭时:@PreDestroy → DisposableBean.destroy → 自定义destroy-method

这个链路就是核心主线。下文我会把每个节点掰开揉碎讲清楚,特别是那些容易被忽略但面试官一定追问的点。

2. 核心节点逐个拆解:每个步骤背后的设计意图

2.1 实例化不等于“new一个对象”:构造方法的选择逻辑

很多人以为Bean实例化就是用反射newInstance(),其实里面大有文章。Spring在实例化一个Bean时,会根据BeanDefinition里记录的构造信息来决定创建方式,优先级大体是:

  1. 如果指定了constructor-arg参数或@ConstructorProperties,走对应的构造方法。
  2. 如果没有指定,且类只有一个构造方法,直接用这个构造方法。
  3. 如果有多个构造方法,Spring会进行构造方法推测(constructor inference),根据容器内可用Bean的类型和数量,选一个“最适合”的构造方法。

这里有个高频面试点:循环依赖时,Spring为什么只对“单例、允许提前暴露”的Bean做三级缓存处理?构造方法注入的循环依赖为什么解决不了?答案在于:Spring通过三级缓存放的是“早期实例化的引用”,但这个引用必须是从构造方法返回之后才有的。如果是构造方法注入,A的构造方法需要B,B的构造方法又需要A,两边都在构造方法阶段,谁都没法提前暴露,那就死锁了。

踩坑提示:如果你面试时说“Spring可以解决所有循环依赖”,一定会被扣分。正确的说法是:Spring通过三级缓存解决的是“单例Bean + 属性注入(setter/字段注入)”场景下的循环依赖,构造方法注入的循环依赖目前无法解决(除非用@Lazy延迟代理)。

2.2 属性填充(populateBean):不是简单setter

属性填充阶段,Spring会遍历Bean的属性元信息,把所有需要注入的属性逐个处理。这个阶段主要干三件事:

  • 根据属性名/类型进行自动装配(byName/byType,以及注解驱动的@Autowired等)。
  • 处理@Value占位符和SpEL表达式,把配置值注入进去。
  • 处理@Resource、@Inject等JSR标准注解。

这个阶段的幕后主力是InstantiationAwareBeanPostProcessor的postProcessProperties()方法,比如AutowiredAnnotationBeanPostProcessor就是在这里把@Autowired标记的字段或方法注入的。

一个容易被忽视的细节:populateBean之前有个postProcessAfterInstantiation方法,如果它返回false,Spring会直接跳过整个属性填充阶段。这个钩子在实际项目中很少用,但理解它的位置有助于回答“我想拦截Bean属性设置怎么办”这类问题。

2.3 Aware回调:让Bean感知容器

Aware是一组标记接口,Spring在属性填充完成后,会依次回调。常见的Aware接口包括:

Aware接口回调内容典型用途
BeanNameAware注入Bean在容器中的名字获取当前Bean的id
BeanClassLoaderAware注入加载当前Bean的ClassLoader做类加载操作
BeanFactoryAware注入当前BeanFactory手动获取其他Bean
ApplicationContextAware注入ApplicationContext获取环境、发布事件等

注意一个细节:ApplicationContextAware是由ApplicationContextAwareProcessor这个特殊的BeanPostProcessor回调的,它会在所有普通Bean的postProcessBeforeInitialization阶段被触发。所以实际时序里,如果你同时实现了BeanFactoryAware和ApplicationContextAware,前者的回调在populateBean之后立刻执行,而后者要等到第一个BeanPostProcessor处理时才触发。

面试时提到Aware时,顺带说一句“这组回调的核心是让Bean能拿到容器的运行时信息,但副作用是让Bean和Spring容器耦合了,现代写法更推荐通过@Autowired或ObjectProvider来获取”,会让面试官觉得你有架构意识。

2.4 BeanPostProcessor:整个生命周期里最强大的扩展点

BeanPostProcessor是整个生命周期里最强大、最常考、也最容易被绕晕的环节。它有两个方法:

Object postProcessBeforeInitialization(Object bean, String beanName) Object postProcessAfterInitialization(Object bean, String beanName)

这两个方法一个在afterPropertiesSet之前调用,一个在init-method之后调用。注意参数里的返回值是Object,意味着你可以在任意一步把传入的Bean替换成另一个对象(比如代理对象),Spring后面用的就是你的返回值。

几个关键点:

  • AOP代理就是在postProcessAfterInitialization阶段生成的。AbstractAutoProxyCreator(AnnotationAwareAspectJAutoProxyCreator的父类)在这里检查Bean是否需要被代理,如果需要就返回代理对象。
  • postProcessBeforeInitialization里的典型应用是ApplicationContextAwareProcessor和InitDestroyAnnotationBeanPostProcessor(负责处理@PostConstruct和@PreDestroy)。
  • BeanPostProcessor本身也是Bean,但它们必须在其他普通Bean实例化前被提前实例化并注册,所以Spring在refresh()容器时会先主动调用registerBeanPostProcessors,这也是为什么你自定义的BeanPostProcessor不能依赖其他普通Bean的原因。

我在面试中常问的一个变形题是:“如果把BeanPostProcessor的调用顺序搞反了会发生什么?”比如把postProcessAfterInitialization里做代理,然后在postProcessBeforeInitialization里提前使用代理对象,会导致目标方法没有被增强。回答这个问题能验证候选人是否理解了执行顺序的不可逆性。

2.5 初始化两大金刚:InitializingBean和init-method

初始化阶段有两个入口:

  • InitializingBean.afterPropertiesSet():Bean实现了该接口后被回调。
  • 自定义init-method:XML中init-method属性、@Bean(initMethod = "init")注解指定的方法。

执行顺序是先afterPropertiesSet,再init-method。Spring官方文档给的推荐是尽量不用接口,而是用@PostConstruct或init-method,因为接口方式会让业务代码侵入Spring API。

这里有个容易混淆的点:@PostConstruct到底在哪个阶段执行?它并不在InitializingBean里面,而是由CommonAnnotationBeanPostProcessor(属于InitDestroyAnnotationBeanPostProcessor)在postProcessBeforeInitialization阶段反射调用。所以实际顺序是:

@PostConstruct → InitializingBean.afterPropertiesSet → 自定义init-method

我实测验证过多版本Spring,这个顺序在Spring 4.x和Spring 5.x(包括Spring Boot 2.x/3.x)里保持一致。

3. 三级缓存与循环依赖:生命周期里最烧脑的设计

3.1 三级缓存分别存了什么

三级缓存(DefaultSingletonBeanRegistry)是理解Spring单例Bean生命周期的关键。三级缓存对应三个Map:

// 一级缓存:存放创建完成的成品Bean Map<String, Object> singletonObjects // 二级缓存:存放提前暴露的早期单例Bean(还没完成属性填充) Map<String, Object> earlySingletonObjects // 三级缓存:存放ObjectFactory,用于生成早期Bean的代理对象 Map<String, ObjectFactory<?>> singletonFactories

为什么要三级缓存而不是两级?这个问题的标准回答是:三级缓存是为了解决“A循环依赖B,而A还需要被AOP代理”的情况。

具体推演一下场景:A依赖B,B依赖A,同时A需要被AOP增强。

  1. A实例化后被放入三级缓存,此时是一个原始的A对象(未填充属性、未代理)。
  2. A填充属性时发现依赖B,于是去创建B。
  3. B实例化后填充属性时发现依赖A,于是从三级缓存中拿到A的ObjectFactory,调用getObject()。
  4. 如果A需要代理,ObjectFactory在这一步会生成A的代理对象(提前代理);如果不需要代理,就返回原始A对象。
  5. B拿到A的引用(可能是代理),继续完成自己的创建,最终放入一级缓存。
  6. A从B的创建过程中返回,继续完成自己的创建。

如果把三级缓存换成两级(直接存早期对象),那A在被B引用时拿到的就是“原始未代理”的对象,后续A即使被代理了,B里面持有的也还是原始对象,代理逻辑就失效了。三级缓存里的ObjectFactory把“是否代理”的决定延迟到真正被引用的那一刻,这是关键设计。

3.2 面试时如何回答“三级缓存”问题

我建议用“是什么→为什么→怎么做”的结构:

  • 先讲三级缓存分别是什么。
  • 再讲为什么需要三级缓存:解决循环依赖 + 保证代理对象的一致性。
  • 最后用一个具体例子(A/B相互依赖 + A需要代理)把创建过程说一遍。

这里还有一个加分项:SpringBoot 2.6.0之后默认allowCircularReferences=false,循环依赖默认被禁止,但很多老项目会手动改回来。能把这个演进说出来,证明你不是只看过老博客。

实操提醒:Spring Boot 2.6及以上版本如果启动日志出现“The dependencies of some of the beans in the application context form a cycle”,优先检查是否真的需要循环依赖,如果确实需要,可以设置spring.main.allow-circular-references=true,但更推荐重构代码消除循环依赖。

4. 销毁阶段:比你想的更讲究

4.1 销毁顺序与各个回调

容器关闭时(比如Spring Boot的优雅停机、context.close()),Spring会调用doClose(),紧接着通过destroySingletons()销毁所有单例Bean。销毁阶段的回调顺序:

@PreDestroy → DisposableBean.destroy → 自定义destroy-method

这个顺序刚好和初始化阶段相反,但有一点容易忽略:销毁只针对单例Bean,原型Bean不会自动销毁。原型Bean每次getBean都是新实例,容器不持有它们的引用,自然也无法管理它们的销毁,需要业务代码自己负责清理。

4.2 常见的销毁场景与坑

  • 数据库连接池、线程池的Bean销毁:通常通过@PreDestroy或destroy-method来关闭资源。如果Bean没被正确销毁,可能导致连接泄漏。
  • Shutdown Hook:Spring Boot中通过registerShutdownHook()注册JVM关闭钩子,确保ApplicationContext能正常关闭,执行所有销毁逻辑。
  • @Scope("prototype")的Bean即使指定了destroy-method也不会执行,这个很多人实测过会踩坑。

如果说初始化阶段是面试的重头戏,那销毁阶段就是检验候选人是否真正有生产经验的分水岭。只要你能主动说出“原型Bean不执行销毁回调”这一点,面试官对你的实战印象分就会高不少。

5. 完整时序图:用代码级视角串一遍

为了让大家有一个整体画面,我把整个getBean过程串成一段伪代码,方便面试前快速回顾:

// 简化版的 AbstractBeanFactory.doGetBean 逻辑(非源码,便于理解) protected Object doGetBean(String name) { // 0. 先从单例池(一级缓存)查 Object sharedInstance = getSingleton(name); if (sharedInstance != null) { return getObjectForBeanInstance(sharedInstance, name); } // 1. 检查该 BeanDefinition 是否存在 // 2. 如果存在循环依赖(正在创建),走三级缓存获取早期引用 // 3. 创建 Bean(单例) if (mbd.isSingleton()) { sharedInstance = getSingleton(name, () -> { return createBean(name, mbd, args); }); } // 4. 如果 Bean 是 FactoryBean,再进行加工 return getObjectForBeanInstance(sharedInstance, name); } // 简化的 createBean → doCreateBean 流程 protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) { // 5. 实例化(构造方法) BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args); Object bean = instanceWrapper.getWrappedInstance(); // 6. 提前暴露:将 ObjectFactory 放入三级缓存(仅单例且允许循环引用) addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); // ---- 此时其他 Bean 可以从三级缓存拿到早期引用 ---- // 7. 属性填充 populateBean(beanName, mbd, instanceWrapper); // 8. 初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); // 9. 注册 DisposableBean(如果配置了销毁方法) registerDisposableBeanIfNecessary(beanName, bean, mbd); return exposedObject; }

面试时能把这段“逻辑”讲清楚,远比背一篇源码译文更打动面试官。不需要你逐行记源码,但核心思路(先查缓存、再创建、提前暴露、填充、初始化、注册销毁)必须烂熟于心。

6. 手写Spring:为什么推荐你用最小实现理解生命周期

6.1 自己写一个Mini容器需要哪些核心类

很多人在“手写Spring”面试环节露怯,其实不必慌。用最小实现拆解生命周期,只需要四个核心类:

  • AnnotationConfigApplicationContext(或自己定义的MiniApplicationContext):负责扫包、注册BeanDefinition、refresh。
  • DefaultSingletonBeanRegistry:维护一级缓存(单例池),实现getSingleton逻辑。
  • AbstractBeanFactory:实现getBean的模板流程。
  • AbstractAutowireCapableBeanFactory:实现doCreateBean,包含实例化、属性填充、初始化。

如果你能写出上面这套骨架,面试官对你的源码理解程度基本就放心了。

6.2 手写过程中的关键决策点

我建议你在练习时重点关注三个决策点:

  1. Aware回调怎么触发:在initializeBean前判断Bean是否实现BeanNameAware等接口,主动调用对应方法。
  2. BeanPostProcessor链表如何管理:需要有一个List<BeanPostProcessor>,按序执行postProcessBeforeInitialization和postProcessAfterInitialization,返回值替换原Bean。
  3. 单例池何时放Bean:单例Bean必须在doCreateBean完成初始化后放入一级缓存,而不是实例化后立刻放入,否则拿到的是“半成品”。

我写过一个约300行的小型容器,实测能覆盖@Component扫描、@Autowired字段注入、单例池、BeanPostProcessor扩展点和@PostConstruct回调。写完之后再回看Spring源码,很多之前不懂的抽象就通了——手写不是造轮子,是给自己装一块“调试器”。

继续这个话题,如果面试官当场要求“画一下Bean生命周期”,你该画什么?很多人习惯画那种一大张的流程图,标满各种接口名,反而让面试官抓不住重点。我的经验是:用一条竖向时序线,从左到右分成“准备阶段、创建阶段、使用阶段、销毁阶段”四个区块,中间标注关键的BeanPostProcessor介入点,就够了。

面试官想听到的不是你会背多少接口,而是你能不能在合适的粒度上,用自己的话把“对象从无到有再到销毁”这件事讲清楚。所以下面我换一种“白话版”的表达方式,把上面所有细节浓缩成一个故事,帮你用得起来。

7. 白话版总述:用一段话说清楚Bean的一生

你可以这样回答面试官:

Spring在启动时会扫描配置,把每一个Bean的定义信息(BeanDefinition)注册进容器。当代码里第一次getBean()时,容器先查单例池,没有的话开始创建。创建分三步:第一步,通过构造方法把对象new出来,此时它还是个“素人”;第二步,把依赖的属性、配置值填进去,就像给房子通水电;第三步,就是各种“开光仪式”——先回调一堆Aware接口让Bean知道自己在哪个容器、叫什么名字,然后执行BeanPostProcessor的before方法,接着执行@PostConstruct、afterPropertiesSet、自定义init方法,最后执行BeanPostProcessor的after方法。如果这个Bean需要被AOP代理,after这一步就会返回一个代理对象。到此,成品Bean被放进一级缓存(单例池),供后续使用。容器关闭时,再按@PreDestroy、DisposableBean、自定义destroy方法的顺序做清理,资源该关的关、该释放的释放。

这一段讲下来,面试官基本就会觉得“这个人有底子”。

8. 高频追问与避坑实录

8.1 追问一:@Autowired 是在哪一步注入的?

在属性填充(populateBean)阶段,由AutowiredAnnotationBeanPostProcessor的postProcessProperties方法处理。它先通过MetadataReader读取Bean类上所有@Autowired、@Value、@Inject标注的字段和方法,然后逐个解析依赖并注入。

8.2 追问二:BeanPostProcessor和InitializingBean的执行顺序能互换吗?

不能。BeanPostProcessor的postProcessBeforeInitialization在afterPropertiesSet之前执行,postProcessAfterInitialization在init-method之后执行。Spring源码中initializeBean方法的调用关系是写死的:

// AbstractAutowireCapableBeanFactory.initializeBean 内部 Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } invokeInitMethods(beanName, wrappedBean, mbd); // 这里先后调 afterPropertiesSet 和 init-method wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

8.3 追问三:原型Bean的生命周期和单例有什么区别?

区别有两点:一是原型Bean默认不走销毁回调,容器不管理它的销毁;二是原型Bean拿到的每次都是新实例,没有一级缓存。但原型Bean依然会走实例化、属性填充、Aware、BeanPostProcessor、初始化这些正向流程。

补充一个冷门考点:@Lookup方法注入和ObjectProvider<T>就是用来解决“单例Bean中依赖原型Bean”问题的,而不是把单例Bean改成原型。

8.4 追问四:Spring Boot中Bean生命周期有没有额外的阶段?

Spring Boot本身没有改变Bean生命周期的核心流程,但引入了ApplicationContextInitializer、EnvironmentPostProcessor、自动配置类(@Configuration中@Bean)等额外环节。它们发生在容器刷新之前或配置加载阶段,不影响Bean“出生到销毁”的骨架。

8.5 追问五:自循环依赖(A依赖它自己)是怎么处理的?

自循环依赖如果通过属性注入,Spring的三级缓存是能处理的:A实例化后放入三级缓存,填充属性时发现依赖自己,从三级缓存拿回早期引用,注入后继续完成初始化。但如果通过构造方法注入,依然会报错。

9. Spring AOP与生命周期:代理时机全解析

9.1 代理对象生成不在属性填充,而在初始化之后

AOP自动代理的核心是AnnotationAwareAspectJAutoProxyCreator,它也是一个BeanPostProcessor。在postProcessAfterInitialization中,它会检查当前Bean是否匹配@Aspect里定义的切点表达式,如果匹配就生成代理对象返回。

这里有个实际场景可以帮大家理解:如果Bean内部方法是this调用方式,比如this.doSomething(),代理不生效,因为this指向的是原始对象,而不是代理对象。这就是“自调用不走AOP”的经典坑。解决办法是注入自身代理、使用AopContext.currentProxy()或拆分调用链。

9.2 三级缓存里的提前代理是怎么回事

循环依赖时,Bean还没走到postProcessAfterInitialization,但为了让循环依赖的另一方能拿到正确的引用,三级缓存存的ObjectFactory会提前执行getEarlyBeanReference()。如果Bean需要被代理,这里会创建代理对象放入二级缓存。这个“提前代理”的对象,与后面初始化完成后生成的代理对象,必须是同一个,否则循环依赖的引用不一致。

Spring通过AbstractAutoProxyCreator里的earlyProxyReferences集合来记录哪些Bean已经提前代理过,后面postProcessAfterInitialization看到该Bean已经提前代理,就不会再生成一个新代理,保证对象一致性。

避坑提示:当你在业务代码里通过 @Autowired 注入一个被循环依赖+AOP的Bean时,拿到的其实是“提前代理”的那个对象。如果这个代理在提前代理时状态不全(部分依赖还没注入),可能在特定时间点访问它的部分字段会得到null。生产环境遇到这种问题,别先怀疑三级缓存,多半是设计上就不该有循环依赖。

10. 实战建议:怎样把生命周期知识变成面试加分项

到这里,核心内容讲得差不多了。我最后分享几个直接可用的建议,帮你把理论知识转成面试现场的输出能力。

10.1 准备一张“记忆卡”

把你自己的答案浓缩成一张卡片,上面写五条:

  1. 生命周期分BeanDefinition和Bean实例两个阶段。
  2. 正向流程:实例化→属性填充→Aware→Before初始化→InitializingBean→init-method→After初始化。
  3. 销毁流程:@PreDestroy→DisposableBean→destroy-method。
  4. BeanPostProcessor是核心扩展点,AOP代理在After初始化。
  5. 三级缓存解决单例+属性注入的循环依赖,构造方法循环依赖无解。

面试前抽5分钟过一遍这张卡就够。

10.2 讲给“外行”听,练就通俗表达

如果你能用“房子装修”的类比把生命周期讲给非技术朋友听(打地基是实例化、走水电是属性填充、装窗帘是BeanPostProcessor、入住是放入单例池、退租是销毁),说明你已经内化了这套知识。我在准备面试时,就是靠这个办法把Spring源码从“背过”变成“理解”的。

10.3 动手写一个最小容器

强烈建议找个周末,自己写一个仅支持单例Bean、@Autowired字段注入、BeanPostProcessor的迷你容器。写的过程中你会遇到很多“原来是这个原因”的顿悟时刻,比如为什么构造方法注入无法解决循环依赖、为什么单例池放的是初始化后的Bean等等。这比刷十篇源码解析都管用。

11. 一些体制外的经验感悟

做了这么多年Java开发,我越来越觉得Spring的Bean生命周期这个知识点,表面上是面试八股,实际上是一个工程师对“容器化思维”理解深浅的分水岭。能把生命周期讲明白的人,通常对依赖注入、AOP、代理、作用域这些概念都会有自己的体系化理解;而只会背流程的人,一旦被追问到“为什么需要三级缓存”“AOP代理到底在哪一步生成”,很快就会漏出短板。

我个人最深的体会是,面试时不要追求“一次把所有细节说完”,而是先抛主干,再根据面试官的追问方向展开。比如提到BeanPostProcessor时,停一下,观察对方是否对AOP代理感兴趣;提到三级缓存时,主动提一句“它解决的是单例+属性注入下的循环依赖”,既显得严谨,又留了对话空间。抓住主动权,远比被动填空更有感染力。

如果你正在准备面试,把这篇文章读透,再关上屏幕用自己的话复述一遍,最好能写成一页笔记。等你什么时候能把生命周期讲得像讲日常生活一样自然,这个知识点才算真正长在你身上了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/9 9:06:46

COMSOL流固耦合井筒应力仿真:原理、搭建与工程应用

搞钻井和完井的朋友应该都有体会&#xff0c;井筒周围那圈岩石的应力状态&#xff0c;直接决定了这口井能不能安全地钻下去。我刚工作那年第一次独立做井壁稳定性评价&#xff0c;用的还是纯弹性模型&#xff0c;结果现场反馈说计算出来的安全泥浆密度窗口跟实钻情况差了快一个…

作者头像 李华
网站建设 2026/10/9 9:04:55

Agent-Reach技术实战:让AI智能体真正触达外部世界

1. Agent-Reach 到底在解决什么问题&#xff1f;最近圈子里聊得最多的词&#xff0c;除了各种大模型的新版本&#xff0c;就是Agent-Reach了。说白了这俩字拆开看&#xff1a;Agent 是智能体&#xff0c;Reach 是“触达、够到”&#xff0c;合起来讲的是一件事——AI 智能体到底…

作者头像 李华
网站建设 2026/10/9 9:03:14

SSM与Spring Boot彻底讲透:区别、联系与迁移方案

很多人学到Java后端的时候&#xff0c;都会经历一个特别拧巴的阶段&#xff1a;先照着教程搭了个SSM项目&#xff0c;配置文件写了一堆&#xff0c;好不容易跑起来&#xff0c;然后又听说现在企业里都在用Spring Boot&#xff0c;于是开始怀疑人生——SSM是不是白学了&#xff…

作者头像 李华
网站建设 2026/10/9 9:03:09

用MATLAB实现傅里叶变换轮廓提取与三维重建:从频域滤波到相位解包裹

1. 从“傅里叶变换轮廓”这个说法聊起&#xff1a;它到底在解决什么问题 我最早接触到“傅里叶变换轮廓”这个词&#xff0c;是在一次图像处理大作业的题目里。当时第一反应是&#xff1a;傅里叶变换和轮廓提取有什么关系&#xff1f;轮廓不应该是梯度、边缘检测算子&#xff0…

作者头像 李华
网站建设 2026/10/9 9:02:58

杭电OJ 2036~2045刷题攻略:贪心、递推与计算几何入门

1. 为什么是2036~2045&#xff1a;这道题号区间的含金量如果你在杭电OJ&#xff08;HDU Online Judge&#xff09;上刷过题&#xff0c;大概率见过这个区间。对很多老ACMer来说&#xff0c;2036到2045这段题号&#xff0c;几乎就是“大一入门必刷清单”的代名词。我的记忆里&am…

作者头像 李华
网站建设 2026/10/9 9:02:41

制造业进销存系统选型指南:从BOM到委外核销的适配之道

做机械零部件的老张&#xff0c;去年花小两万买了一套口碑不错的进销存系统&#xff0c;用了三个月&#xff0c;仓库账跟实物就对不上了。问题不在软件的基本功能&#xff0c;而是他大量订单要外发加工&#xff0c;系统里根本没有“委外发料—回料核销”这条业务链&#xff0c;…

作者头像 李华