免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

阿里云面试:Spring 中 Bean 的生命周期是怎样的?

阿里云面试:Spring 中 Bean 的生命周期是怎样的? 一、写在前面Spring 的 Bean 生命周期几乎是每一场 Java 后端面试必问的内容尤其在中高级岗位和阿里云相关研发岗位中这个问题往往会从「你能说出几个阶段」一路追问到「BeanPostProcessor 和 BeanFactoryPostProcessor 有什么区别」「循环依赖为什么三级缓存可以解决」「AOP 代理是在哪一步生成的」。如果你只是背下「实例化、属性填充、初始化、销毁」这几个词通常很难扛过后续追问。本篇文章会用超过两万字的篇幅完整拆解 Spring Bean 从定义、加载、实例化、属性填充、初始化、使用到销毁的全部过程重点覆盖 BeanPostProcessor、BeanFactoryPostProcessor、Aware 系列接口、InitializingBean、DisposableBean、循环依赖三级缓存、AOP 代理生成时机等高频考点。文章默认以 Spring Framework 5.x 和 Spring Boot 2.x/3.x 中常见的 AnnotationConfigApplicationContext 流程为主线同时兼顾 XmlWebApplicationContext 等传统场景的差异。阅读建议如果你正在准备面试可以先通读一遍生命周期总览再重点掌握「扩展点」「源码流程」「循环依赖」三部分如果你是在工作中排查 Bean 初始化异常可以直接跳到初始化阶段和异常分析小节。二、先理解 Bean 和 IoC 容器的关系在讨论 Bean 的生命周期之前首先要明确一个前提所谓 Bean 的生命周期本质上是 IoC 容器对普通 Java 对象的管理过程。一个普通的 Java 对象比如直接new User()创建出来的实例其生命周期非常简单就是由开发者自己负责创建、使用和垃圾回收。而在 Spring 容器中对象不再由调用方直接 new 出来而是由容器统一创建、装配、增强、管理和销毁。这种控制权的反转就是 IoC也叫控制反转而容器中被管理的对象就是 Bean。Spring 之所以要把对象的创建过程做得如此复杂是因为容器需要在这个过程的不同阶段插入扩展点。例如细粒度地读取配置元数据、在对象创建前后增加代理逻辑、在属性赋值前后做校验或转换、在 Bean 初始化完成后注册到容器、在容器关闭时释放资源等。理解了这一点你就会明白生命周期流程虽然步骤很多但每一步存在的目的都是为了满足某一种真实业务场景的扩展需求。因此在面试中回答 Bean 生命周期问题时不建议一上来就机械地背阶段名称。更推荐的开场方式是先说明 Bean 生命周期就是 Spring IoC 容器对 Bean 从定义到销毁的全流程管理其复杂性的根本原因在于 Spring 需要在创建 Bean 的每个关键节点都预留可扩展空间从而支持框架能力如 AOP、属性解析、事件监听以及与第三方组件的集成。三、Bean 生命周期总览一张图看懂核心阶段Spring Bean 的完整生命周期从宏观上可以划分为以下六个大阶段Bean 定义阶段容器通过扫描、解析配置类或 XML 等方式将类的元数据封装为 BeanDefinition并注册到容器中。实例化阶段容器根据 BeanDefinition通过构造器或工厂方法创建 Bean 的原始 Java 实例。属性填充阶段容器解析并注入 Bean 的依赖属性包括 Autowired、Value、Resource 等标注的字段或方法。初始化阶段执行 Aware 接口回调、BeanPostProcessor 的前置处理、初始化方法、BeanPostProcessor 的后置处理等。使用阶段Bean 已经处于可用状态可以从容器中获取并执行业务逻辑。销毁阶段容器关闭时执行 PreDestroy、DisposableBean 接口或自定义销毁方法释放资源。如果进一步把初始化阶段和实例化阶段拆开来看就能得到面试中更常用的细化版本。下面是一条约等于「标准答案」的完整流程链路1. 扫描并解析 BeanDefinition 2. 执行 BeanFactoryPostProcessor 3. 实例化前的检查与依赖准备 4. 调用构造器实例化 Bean 5. 合并 BeanDefinition 并处理循环依赖的提前曝光 6. 属性填充 7. 调用 Aware 系列接口 8. 执行 BeanPostProcessor#postProcessBeforeInitialization 9. 执行初始化方法 10. 执行 BeanPostProcessor#postProcessAfterInitialization 11. 注册销毁回调 12. Bean 可投入业务使用 13. 容器关闭时执行销毁回调这条链路覆盖了绝大多数面试官关心的要点。但要注意这里每一条都可以继续深入。比如第 4 步是谁创建的 Bean、创建时如何选择构造器、第 6 步如果出现循环依赖怎么办、第 10 步为什么常常是 AOP 代理对象的生成位置这些都是后续章节要展开的内容。接下来我会按照这条链路逐段展开并解释每一步的源码入口和扩展机制。四、BeanDefinition生命周期的起点Bean 的生命周期并不是从new对象开始的而是从 Bean 定义信息的注册开始。Spring 通过 BeanDefinition 描述一个 Bean 的完整元数据包括Bean 的 Class 类型Bean 名称和别名作用域即 scope常见的有 singleton 和 prototype懒加载配置即 lazyInit依赖关系即 dependsOn自动装配模式初始化方法和销毁方法名称是否是主候选 Bean即 primary工厂 Bean 名称和工厂方法名称等。在 Java Config 时代Spring 通过 ConfigurationClassPostProcessor 扫描 Configuration、Component、Service、Repository、Controller 等注解把符合条件的类转换为 BeanDefinition并注册到 BeanDefinitionRegistry。以 AnnotationConfigApplicationContext 为例其构造过程中会先注册几个内置的 BeanFactoryPostProcessor 和 BeanPostProcessor再扫描用户配置类解析出所有 BeanDefinition。BeanDefinition 本质上是一个接口常用的实现包括 GenericBeanDefinition、AnnotatedGenericBeanDefinition、ScannedGenericBeanDefinition 等。每个 BeanDefinition 携带的信息决定了后续 Bean 创建时的行为。例如 scope 为 singleton 时容器会在启动阶段完成创建而 scope 为 prototype 时每次获取都会走一次完整的创建流程。面试中如果有面试官问你「Bean 的生命周期从哪里开始」比较稳妥的回答是从 BeanDefinition 的解析和注册开始而并非从 new 对象开始。因为只有先有了 Bean 的完整定义信息容器才能决定如何创建、如何装配、如何初始化以及如何销毁这个 Bean。五、BeanFactoryPostProcessor修改 Bean 定义的窗口BeanFactoryPostProcessor 是 Spring 提供的一个非常强大的扩展点它的作用是在 BeanDefinition 注册完成之后、Bean 实例创建之前读取并修改容器中的 BeanDefinition。它与 BeanPostProcessor 的区别是面试中的超高频问题。BeanFactoryPostProcessor 的核心接口定义如下FunctionalInterface public interface BeanFactoryPostProcessor { void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; }从这个接口可以看出它处理的对象是 BeanFactory更准确地说是 BeanDefinition 所在的可配置 Bean 工厂。因此它可以新注册 BeanDefinition也可以修改已有 BeanDefinition 的属性。典型实现有ConfigurationClassPostProcessor负责解析 Configuration、ComponentScan、Import、Bean 等注解是 Java Config 时代最重要的 BeanFactoryPostProcessor。PropertySourcesPlaceholderConfigurer负责解析 ${...} 占位符并将其替换为配置文件或系统环境中的实际值。CustomAutowireConfigurer用于注册自定义的自动装配限定注解。而 BeanPostProcessor 处理的对象是已经实例化出来的 Bean 实例它可以在 Bean 的初始化方法前后添加自定义逻辑。两者的核心区别可以总结为作用对象不同BeanFactoryPostProcessor 操作 BeanDefinitionBeanPostProcessor 操作 Bean 实例。执行时机不同BeanFactoryPostProcessor 在 Bean 实例化之前执行BeanPostProcessor 在 Bean 实例化之后、初始化前后执行。典型应用不同BeanFactoryPostProcessor 常用于配置解析、占位符替换、动态注册 BeanDefinitionBeanPostProcessor 常用于属性代理、AOP 增强、初始化后包装。在一次容器启动过程中BeanFactoryPostProcessor 通常先于 BeanPostProcessor 被实例化并执行。这是因为只有先把 BeanDefinition 准备好后续才能按照这些定义去创建 Bean并在创建过程中应用各种 BeanPostProcessor。六、实例化Bean 对象是如何被创建出来的当所有 BeanDefinition 准备完成后容器开始按需创建 Bean 实例。对于单例且非懒加载的 BeanSpring 通常在容器刷新阶段就会主动创建对于懒加载 Bean则会在第一次获取时创建对于原型 Bean则每次获取都会重新创建。实例化的核心入口位于 AbstractAutowireCapableBeanFactory#createBeanInstance 方法。这一阶段的核心任务是选择一个合适的构造器或工厂方法创建出 Bean 的原始实例但此时还没有进行任何属性赋值。构造器的选择逻辑大致如下如果 BeanDefinition 中指定了 factoryMethod则使用工厂方法创建实例。如果通过 Supplier 方式注册了 Bean则调用 Supplier 获取实例。如果存在使用 Autowired 注解标记的构造器则优先使用该构造器。如果没有特殊标注则优先使用无参构造器。如果只有一个构造器则使用该构造器。如果存在多个构造器且都没有明确标注则尝试使用默认的无参构造器如果无参构造器不存在则可能根据参数匹配规则选择构造器或抛出异常。在 JDK 反射中创建对象通常使用 Constructor.newInstance 或 JDK 17 以后优化过的调用方式。Spring 在构造器选择完成后还会通过 BeanWrapper 对实例进行包装。BeanWrapper 是 Spring 提供的对象包装工具它能够方便地对对象属性进行读取和写入后续属性填充阶段也依赖 BeanWrapper 完成。有面试官可能会问Spring 创建 Bean 时到底使用的是反射还是 CGLIB这里需要区分场景。对于普通的 Bean 创建Spring 使用 JDK 反射调用构造器而 CGLIB 通常用于创建代理对象或使用 Configuration 时创建配置类的增强子类。因此「Bean 实例化默认使用反射AOP 代理或 Configuration 增强时才可能使用 CGLIB」是一个比较准确的表述。七、三级缓存与循环依赖循环依赖是 Bean 生命周期中的重点和难点。所谓循环依赖指的是两个或多个 Bean 相互依赖例如 A 依赖 BB 又依赖 A。在 Java 中如果直接用构造器 new A(new B(new A(...)))往往会形成无限递归最终抛出栈溢出异常。Spring 通过三级缓存机制巧妙地解决了单例 Bean 的 setter 注入或字段注入形式的循环依赖。三级缓存是面试中的必考内容一定要把概念说清楚。Spring 在 DefaultSingletonBeanRegistry 中定义了三个缓存 Map/** 一级缓存保存完全初始化完成的单例 Bean */ private final MapString, Object singletonObjects new ConcurrentHashMap(256); /** 二级缓存保存提前曝光的半成品 Bean通常已经完成实例化但尚未完成属性填充和初始化 */ private final MapString, Object earlySingletonObjects new ConcurrentHashMap(16); /** 三级缓存保存 ObjectFactory用于延迟生成提前引用通常用于 AOP 代理 */ private final MapString, ObjectFactory? singletonFactories new HashMap(16);三个缓存的作用分别如下一级缓存 singletonObjects存放已经完成整个生命周期、可以直接对外提供的 Bean。二级缓存 earlySingletonObjects存放已经创建但尚未完成属性填充和初始化的早期 Bean 引用目的是避免重复通过 ObjectFactory 创建代理。三级缓存 singletonFactories存放可以生成早期 Bean 引用的工厂。默认工厂通常会调用 getEarlyBeanReference如果 Bean 需要 AOP 代理会在这里提前生成代理对象。解决 A 依赖 B、B 依赖 A 的典型流程如下容器创建 A实例化完成后在属性填充前把 A 的 ObjectFactory 放入三级缓存提前曝光。开始为 A 填充属性发现依赖 B于是去创建 B。创建 B 时实例化 B 后填充 B 的属性时发现依赖 A。容器从三级缓存中找到 A 的 ObjectFactory调用后得到 A 的早期引用并放入二级缓存同时从三级缓存移除。B 完成属性填充和初始化进入一级缓存随后 A 也完成后续流程进入一级缓存。这里有一个重要的细节一级缓存储存的对象是真正可用的 Bean而二级缓存储存的对象可能是一个半成品甚至是 AOP 代理对象。也就是说如果 A 被代理了B 中注入的 A 引用其实是代理对象而不是原始 A 实例。三级缓存的意义在于它把「是否提前生成代理」这个决策延迟到真正需要的时候避免所有 Bean 都被无条件生成早期代理。面试中常问的延伸问题是为什么构造器注入的循环依赖无法解决原因是构造器注入发生在实例化阶段而 Spring 的三级缓存只对「已经完成实例化但尚未填充属性」的对象提前曝光。构造器循环依赖时A 还无法完成实例化A 的 ObjectFactory 也无法注册自然无法提前暴露 A 供 B 使用。因此构造器循环依赖会抛出 BeanCurrentlyInCreationException。另一个常见追问是二级缓存是否已经足够为什么还要三级缓存从解决循环依赖本身来说二级缓存确实也能解决简单的字段注入循环依赖。三级缓存的核心价值在于配合 AOP通过 ObjectFactory 延迟执行 getEarlyBeanReference只有当真的发生循环依赖时才提前生成代理对象没有循环依赖时Bean 仍然在初始化完成后统一生成代理。这样既能保证代理对象的一致性又能避免不必要的提前代理。八、属性填充依赖注入的核心动作Bean 实例创建完成后还是一个「空壳」对象内部字段大多还没有赋值。属性填充阶段的任务就是根据 BeanDefinition 中的元数据、Autowired、Value、Resource 等注解把依赖对象或配置值注入到 Bean 中。属性填充的核心入口是 AbstractAutowireCapableBeanFactory#populateBean 方法。在具体填充前Spring 会再次遍历所有 InstantiationAwareBeanPostProcessor执行其 postProcessAfterInstantiation 方法。如果该方法返回 false则直接跳过后续属性填充这种情况比较少见通常用于特殊框架的自定义控制。按注入类型来看主要有两种方式按名称自动装配byName根据属性名查找依赖 Bean。按类型自动装配byType根据属性类型查找依赖 Bean如果存在多个候选则可能结合 Qualifier、Primary 或名称匹配来确定。在 Java Config 时代Autowired 默认采用 byType 方式。Autowired 不仅可以标注在字段上也可以标注在构造器、setter 方法和普通方法参数上。标注在构造器上时注入发生在实例化阶段标注在字段上时注入发生在属性填充阶段。Resource 默认按名称注入并可以通过 name 属性指定 Bean 名称Value 则主要用来注入普通值、占位符和 SpEL 表达式。属性填充过程大致如下判断注入模型例如 AUTOWIRE_NO、AUTOWIRE_BY_NAME、AUTOWIRE_BY_TYPE。执行 InstantiationAwareBeanPostProcessor#postProcessProperties处理 Autowired、Value、Resource 等注解。如果需要按名称或类型自动装配则进行相应解析。通过反射获取字段或方法将依赖对象写入 Bean。需要特别强调的是属性填充是循环依赖问题最容易出现的阶段。因为当 A 填充字段时需要 B而 B 填充字段时又需要 A此时容器必须依赖上一节介绍的三级缓存机制才能顺利完成。九、Aware 系列接口让 Bean 感知容器能力属性填充完成后Bean 已经具备基本的依赖对象但很多时候 Bean 还希望了解容器本身的信息例如自己的名称、所在容器、类加载器等。Spring 通过一组 Aware 接口允许 Bean 在初始化早期获取容器内部资源。常见 Aware 接口及其作用BeanNameAware让 Bean 获取自己在容器中的 beanName常用于日志、监控或按名称做差异化处理。BeanFactoryAware让 Bean 获取当前 BeanFactory可以通过它按名称获取其他 Bean。ApplicationContextAware让 Bean 获取 ApplicationContext它是 BeanFactory 的超集额外提供事件、国际化和资源加载等能力。MessageSourceAware获取国际化消息源用于解析 i18n 消息。ApplicationEventPublisherAware获取事件发布器用于发布 Spring 事件。ResourceLoaderAware获取资源加载器用于加载 classpath、文件系统等资源。EnvironmentAware获取 Environment用于读取 profile、系统属性和环境变量配置。EmbeddedValueResolverAware获取字符串值解析器用于解析 ${...} 占位符和 SpEL 表达式。BeanClassLoaderAware获取类加载器某些场景需要用其动态加载类。ServletContextAware仅在 Web 应用中使用用于获取 ServletContext 对象。下面是一个同时实现多个 Aware 接口的自定义 Bean 示例可以看到容器会在初始化早期主动回调这些 setter 方法Component public class ContainerAwareBean implements BeanNameAware, BeanFactoryAware, ApplicationContextAware { private String beanName; private BeanFactory beanFactory; private ApplicationContext applicationContext; Override public void setBeanName(String name) { this.beanName name; } Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory beanFactory; } Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext applicationContext; } }不过要提醒的是Aware 回调通常不是日常开发的首选方案。能用依赖注入解决的尽量通过 Autowired 获取 BeanFactory 或 ApplicationContext避免让业务 Bean 与 Spring 容器过度耦合也更便于单元测试。十、BeanPostProcessorBean 初始化的核心扩展点BeanPostProcessor 是整个 Bean 生命周期中扩展能力最强的接口之一也是面试中从初级追问到高级的分水岭。它的作用是在 Bean 初始化方法执行的前后各插入一个回调让开发者可以在所有 Bean 的创建过程中统一加入逻辑。核心接口定义如下public interface BeanPostProcessor { Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }简单来说postProcessBeforeInitialization在 Bean 的初始化方法调用之前执行。此时 Bean 已经完成属性填充但 PostConstruct、InitializingBean 等方法还未执行。postProcessAfterInitialization在 Bean 的初始化方法调用之后执行。此时 Bean 已经完成初始化可以正常使用但这个阶段的返回值才是容器最终持有的对象。这里有一个非常关键的细节两个方法都可以返回一个新的对象来替换原来的 Bean。如果返回 null后续流程将不再继续。Spring AOP 正是利用 postProcessAfterInitialization 返回代理对象从而完成切面增强。因此如果你在自定义 BeanPostProcessor 中不小心修改或吞掉 Bean可能会导致容器出现不可预期的问题。BeanPostProcessor 的典型应用场景包括AOP 代理AbstractAutoProxyCreator 在初始化后创建代理对象。参数校验在 Bean 初始化前对字段执行自定义校验逻辑。属性增强为特定类型的 Bean 统一注入某个上下文信息。监控统计统计 Bean 初始化耗时帮助排查启动性能问题。下面是一个极简的日志型 BeanPostProcessor 示例Component public class LoggingBeanPostProcessor implements BeanPostProcessor { Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println(准备初始化 Bean beanName); return bean; } }面试中常问为什么 BeanPostProcessor 能作用于所有 Bean因为 Spring 在容器启动时会提前创建并注册所有 BeanPostProcessor等后续普通 Bean 创建时AbstractAutowireCapableBeanFactory#initializeBean 会统一遍历这些后置处理器因此它们天然具备全局生效能力。十一、InitializingBean 与 init-method初始化回调当 Bean 完成属性填充、Aware 回调和 BeanPostProcessor 的前置处理后Spring 会进入真正的初始化方法调用阶段。这一阶段支持三种初始化回调方式PostConstruct 注解由 CommonAnnotationBeanPostProcessor 处理在构造后、初始化逻辑中较早执行。InitializingBean 接口实现 afterPropertiesSet 方法。init-method / Bean(initMethod xxx)在 XML 或 Java Config 中声明的自定义初始化方法。它们的执行顺序通常是PostConstruct → InitializingBean#afterPropertiesSet → 自定义 init-method。后两者会被统一抽象到 AbstractAutowireCapableBeanFactory#invokeInitMethods 中完成调用。public class InitOrderBean implements InitializingBean { public InitOrderBean() { System.out.println(0. 构造方法); } PostConstruct public void postConstruct() { System.out.println(1. PostConstruct); } Override public void afterPropertiesSet() throws Exception { System.out.println(2. InitializingBean#afterPropertiesSet); } public void customInit() { System.out.println(3. 自定义 init-method); } } Configuration public class AppConfig { Bean(initMethod customInit) public InitOrderBean initOrderBean() { return new InitOrderBean(); } }运行后输出顺序如下0. 构造方法 1. PostConstruct 2. InitializingBean#afterPropertiesSet 3. 自定义 init-method这个输出结果可以作为面试中说明初始化顺序的直观证据。它清楚地表明初始化回调并不是发生在对象创建的同时而是在实例化和属性填充全部完成之后。十二、AOP 代理是在哪一步生成的AOP 代理生成时机是这道面试题中最容易被忽略、但又很能拉开差距的追问。先说结论在绝大多数情况下Spring AOP 代理对象是在 BeanPostProcessor#postProcessAfterInitialization 阶段、也就是初始化方法执行之后生成的。具体过程是Bean 完成实例化、属性填充和初始化。AbstractAutoProxyCreator 作为 BeanPostProcessor 检查当前 Bean 是否命中切点。如果命中切点则根据目标类是否实现接口选择 JDK 动态代理或 CGLIB 生成代理对象。用代理对象替换原始 Bean返回给容器并放入一级缓存。所以容器最终暴露给调用方的 Bean常常并不是最初 new 出来的那个实例而是一个经过包装的代理对象。这也是为什么调试 Spring AOP 时打印对象类名会看到类似 com.sun.proxy.$Proxy0 或带有 $$SpringCGLIB$$ 的类名。循环依赖场景下还有一个特殊时机如果发生循环依赖且 Bean 命中 AOP 切点Spring 会在三级缓存的 getEarlyBeanReference 方法中提前生成代理对象从而保证 B 中注入的 A 引用和最终容器中的 A 代理对象是同一个对象避免出现两个不同实例的问题。面试中如果被追问「JDK 动态代理和 CGLIB 如何选择」可以这样回答JDK 动态代理要求目标类至少实现一个接口生成的代理类实现相同接口基于 InvocationHandler 完成增强。CGLIB通过继承目标类生成子类代理即使目标类没有实现接口也能代理但不能代理 final 类和 final 方法。Spring Boot 2.x 默认行为默认使用 CGLIB可以通过 spring.aop.proxy-target-class 配置调整。十三、销毁阶段DisposableBean 与 PreDestroy当容器关闭时Spring 会对单例 Bean 执行销毁回调主要用来释放数据库连接、线程池、文件句柄等资源。销毁回调同样支持三种方式PreDestroy 注解由 CommonAnnotationBeanPostProcessor 处理。DisposableBean 接口实现 destroy 方法。destroy-method / Bean(destroyMethod xxx)声明自定义销毁方法。执行顺序一般是PreDestroy → DisposableBean#destroy → 自定义 destroy-method。具体入口是 DisposableBeanAdapter#destroy 方法容器会为每个单例 Bean 包装一个销毁适配器并注册到一次性 Bean 列表中。public class DestroyBean implements DisposableBean { PreDestroy public void preDestroy() { System.out.println(1. PreDestroy); } Override public void destroy() throws Exception { System.out.println(2. DisposableBean#destroy); } public void customDestroy() { System.out.println(3. 自定义 destroy-method); } }需要注意的是销毁回调只对 singleton 作用域的 Bean 生效。对于 prototype BeanSpring 容器创建完成后就不再管理其完整生命周期因此容器关闭时不会自动调用其销毁方法需要调用方自行负责清理。十四、完整生命周期时序与源码入口对照为了便于面试前快速回顾下面把 Bean 生命周期中的核心步骤和执行入口对齐。这也是我们在回答问题时最容易组织成的「总分总」结构。阶段关键动作核心源码或入口定义解析并注册 BeanDefinitionConfigurationClassPostProcessor后置修改 BeanDefinitionBeanFactoryPostProcessor#postProcessBeanFactory实例化构造器或工厂方法创建对象AbstractAutowireCapableBeanFactory#createBeanInstance提前曝光加入三级缓存DefaultSingletonBeanRegistry#addSingletonFactory填充注入依赖和配置值AbstractAutowireCapableBeanFactory#populateBean感知回调 Aware 接口AbstractAutowireCapableBeanFactory#invokeAwareMethods前置初始化前扩展BeanPostProcessor#postProcessBeforeInitialization初始化PostConstruct、afterPropertiesSet、init-methodAbstractAutowireCapableBeanFactory#invokeInitMethods后置初始化后扩展AOP 代理BeanPostProcessor#postProcessAfterInitialization销毁PreDestroy、destroy、destroy-methodDisposableBeanAdapter#destroy这张表建议在面试前自己默写一遍。能把这个表从头到尾讲清楚说明你已经从「知道流程」进入到「知道每一步为什么发生、发生在哪里」的层次。十五、面试高频追问与回答模板除了把主流程讲清楚面试官最常见的追问集中在以下几个方向。15.1 Bean 的实例化和初始化的区别实例化是创建一个空的 Java 对象主要解决「对象从哪来」的问题初始化是在对象创建后完成属性填充并执行各种回调方法主要解决「对象如何变得可用」的问题。你可以记住一句话实例化只是把对象 new 出来初始化是让这个对象真正具备可用能力。15.2 BeanFactoryPostProcessor 和 BeanPostProcessor 的区别前者操作 BeanDefinition发生在所有 Bean 实例化之前后者操作 Bean 实例发生在单例 Bean 实例化之后、初始化前后。前者像修改「配方」后者像在「产品」上增加额外能力。15.3 InitializingBean 和 init-method 如何选择InitializingBean 会与 Spring 接口强耦合init-method 则是更轻量的配置方式。实际项目中更推荐使用 PostConstruct 或 Bean 的 initMethod因为它们更符合声明式风格业务代码不需要依赖具体接口。15.4 Spring 如何解决循环依赖核心回答是单例 Bean 的 setter 注入或字段注入形式的循环依赖通过三级缓存解决。实例化后先把 ObjectFactory 放进三级缓存提前曝光后续 Bean 发现依赖时可以先拿到早期引用完成填充后再返回真正对象构造器循环依赖无法通过三级缓存解决。15.5 为什么更推荐构造器注入虽然字段注入写起来方便但构造器注入能保证依赖不可变、对象创建后立刻可用也便于单元测试和发现依赖过多的问题。Spring 官方也建议必须的依赖使用构造器注入可选的依赖使用 setter 注入。十六、总结Spring Bean 的生命周期可以浓缩为一条主线解析 BeanDefinition → BeanFactoryPostProcessor 调整定义 → 实例化 → 提前曝光到三级缓存 → 属性填充 → Aware 回调 → BeanPostProcessor 前置处理 → 初始化方法 → BeanPostProcessor 后置处理与 AOP 代理 → 使用 → 销毁。这条线上的每一个节点都对应一个真实问题BeanDefinition 解决「对象如何描述」BeanFactoryPostProcessor 解决「定义如何被打补丁」三级缓存和提前曝光解决「循环依赖」属性填充解决「依赖从哪里来」Aware 和 BeanPostProcessor 解决「框架怎么统一增强」销毁回调解决「资源如何释放」。面试答题时建议使用「先总后分、结合源码、主动说明扩展点」的方式第一步给出完整阶段链路第二步挑两三个关键点向下展开第三步结合自己项目中的真实使用场景说明。这样比单纯背流程更有说服力也更容易接住面试官的连环追问。
返回列表