【大白话说Java面试题 第151题】【06_Spring篇】第11题:说一下 Spring Bean 的生命周期?
PDF大白话说Java面试题 — 06_Spring篇第11题说一下 Spring Bean 的生命周期回答核心考点 Spring Bean 的生命周期是 Spring 框架最核心的机制之一大厂面试不会只问有哪几个阶段而是深入考察BeanDefinition 的合并与后置处理MergedBeanDefinitionPostProcessor、三级缓存解决循环依赖的源码级原理singletonObjects/earlySingletonObjects/singletonFactories、推断构造方法的完整算法autowireConstructor、初始化阶段的三层回调顺序PostConstruct→InitializingBean→ 自定义 init、AOP 代理的创建时机与BeanPostProcessor的作用、以及SmartInitializingSingleton和DestructionAwareBeanPostProcessor等扩展点。面试官真正想判断的是你是否能从源码层面理解 Spring 容器的完整创建链路以及能否在循环依赖、AOP 代理、初始化顺序等生产级场景中定位和解决问题。1. 生命周期的完整阶段——从源码视角梳理Spring Bean 的生命周期是一个精密的流水线涉及 IoC 容器、依赖注入、AOP 代理、事件机制等多个子系统的协同。以下是完整的生命周期阶段【阶段1】加载 BeanDefinition ↓ 【阶段2】BeanDefinition 合并与后置处理 ↓ 【阶段3】推断构造方法并实例化 ↓ 【阶段4】依赖注入属性填充 ↓ 【阶段5】初始化前处理Aware 接口、PostConstruct ↓ 【阶段6】初始化InitializingBean、自定义 init ↓ 【阶段7】初始化后处理AOP 代理创建 ↓ 【阶段8】放入单例池可供使用 ↓ 【阶段9】容器关闭时销毁PreDestroy、DisposableBean、自定义 destroy1.1 阶段1加载 BeanDefinitionSpring 启动时通过BeanDefinitionReader读取配置XML、注解、Java Config将类信息解析为BeanDefinition对象注册到BeanDefinitionRegistry即DefaultListableBeanFactory。配置方式对应的 Reader示例XMLXmlBeanDefinitionReaderbean iduserService class.../注解扫描ClassPathBeanDefinitionScannerComponentScan(com.example)Java ConfigAnnotatedBeanDefinitionReaderBean public UserService userService() {...}1.2 阶段2BeanDefinition 合并与后置处理在实例化之前Spring 会执行MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition()方法用于处理Autowired、Value、PostConstruct等注解的元数据缓存后置处理器作用AutowiredAnnotationBeanPostProcessor缓存Autowired、Value注解的注入点CommonAnnotationBeanPostProcessor缓存Resource、PostConstruct、PreDestroy注解RequiredAnnotationBeanPostProcessor处理Required注解已废弃为什么要缓存避免每次创建 Bean 时都通过反射扫描注解提升性能。1.3 阶段3推断构造方法并实例化Spring 通过AbstractAutowireCapableBeanFactory.createBeanInstance()方法推断构造方法并创建实例。构造方法推断规则场景推断结果只有一个无参构造使用无参构造默认只有一个有参构造使用唯一的有参构造多个构造方法其中一个标注Autowired(requiredtrue)使用标注的构造方法多个构造方法多个标注Autowired(requiredfalse)使用参数最多的构造方法多个构造方法无Autowired使用无参构造若无无参构造则报错ServicepublicclassUserService{privatefinalUserRepositoryuserRepository;privatefinalOrderRepositoryorderRepository;// Spring 会选择这个构造方法参数最多且 AutowiredAutowiredpublicUserService(UserRepositoryuserRepository,OrderRepositoryorderRepository){this.userRepositoryuserRepository;this.orderRepositoryorderRepository;}publicUserService(UserRepositoryuserRepository){this.userRepositoryuserRepository;}}构造方法参数解析按byType先查找若同类型有多个 Bean再按byName匹配。1.4 阶段4依赖注入属性填充实例化后Spring 执行populateBean()方法填充属性InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()允许在属性注入前修改 Bean 实例InstantiationAwareBeanPostProcessor.postProcessProperties()解析并注入Autowired、Value、Resource等注解标记的属性applyPropertyValues()处理 XML 配置中的property标签。注入方式对比注入方式注解注入时机特点构造器注入无或Autowired实例化时依赖必填不可变推荐Setter 注入Autowired属性填充时依赖可选可重新设置字段注入Autowired属性填充时代码简洁但测试困难不推荐1.5 阶段5初始化前处理Aware 接口、PostConstruct属性填充完成后进入initializeBean()方法首先执行Aware接口Aware 接口注入内容用途BeanNameAwareBean 的名称获取自身 BeanNameBeanFactoryAwareBeanFactory 容器获取容器引用ApplicationContextAwareApplicationContext 容器获取应用上下文EnvironmentAwareEnvironment获取配置环境ResourceLoaderAwareResourceLoader加载资源文件Aware 接口的执行顺序按上述表格从上到下依次执行。然后执行BeanPostProcessor.postProcessBeforeInitialization()后置处理器作用ApplicationContextAwareProcessor处理ApplicationContextAware等接口InitDestroyAnnotationBeanPostProcessor调用PostConstruct标注的方法PostConstruct的执行由InitDestroyAnnotationBeanPostProcessor处理通过反射调用标注了PostConstruct的方法。1.6 阶段6初始化InitializingBean、自定义 initBeanPostProcessor.postProcessBeforeInitialization()执行完毕后进入真正的初始化阶段【初始化顺序】 ↓ 1. InitializingBean.afterPropertiesSet() ↓ 2. 自定义 init-methodBean(initMethod...) 或 XML init-method三层初始化回调的执行顺序顺序回调方式来源说明1PostConstructJSR-250 标准最先执行推荐2InitializingBean.afterPropertiesSet()Spring 接口次之侵入性较强3自定义init-method配置指定最后执行最灵活ServicepublicclassUserServiceimplementsInitializingBean{PostConstructpublicvoidpostConstruct(){System.out.println(1. PostConstruct 执行);}OverridepublicvoidafterPropertiesSet()throwsException{System.out.println(2. InitializingBean 执行);}publicvoidcustomInit(){System.out.println(3. 自定义 init-method 执行);}}// Java Config 中指定自定义 initBean(initMethodcustomInit)publicUserServiceuserService(){returnnewUserService();}1.7 阶段7初始化后处理AOP 代理创建初始化完成后执行BeanPostProcessor.postProcessAfterInitialization()。这是AOP 代理创建的关键时机后置处理器作用AbstractAutoProxyCreator检查 Bean 是否匹配切点若匹配则创建代理对象为什么 AOP 代理在初始化后创建因为代理对象需要包装完整的 Bean 实例包括已注入的依赖和已执行的初始化逻辑。如果在实例化时创建代理后续的依赖注入和初始化会作用于代理对象可能导致问题。代理创建后的 Bean 类型JDK 代理com.sun.proxy.$ProxyXX实现目标接口CGLIB 代理UserService$$EnhancerBySpringCGLIB继承目标类1.8 阶段8放入单例池初始化完成后Bean或代理对象被放入DefaultSingletonBeanRegistry的三级缓存缓存级别字段名说明一级缓存singletonObjects存放完全初始化好的 Bean成品二级缓存earlySingletonObjects存放提前暴露的 Bean半成品用于解决循环依赖三级缓存singletonFactories存放 Bean 的 ObjectFactory用于创建代理对象1.9 阶段9销毁容器关闭时执行销毁回调顺序回调方式来源1PreDestroyJSR-250 标准2DisposableBean.destroy()Spring 接口3自定义destroy-method配置指定ServicepublicclassUserServiceimplementsDisposableBean{PreDestroypublicvoidpreDestroy(){System.out.println(1. PreDestroy 执行);}Overridepublicvoiddestroy()throwsException{System.out.println(2. DisposableBean 执行);}publicvoidcustomDestroy(){System.out.println(3. 自定义 destroy-method 执行);}}2. 三级缓存解决循环依赖的源码级原理循环依赖是 Spring Bean 生命周期中最经典的问题Spring 通过三级缓存解决单例 Bean 的循环依赖。2.1 什么是循环依赖ServicepublicclassUserService{AutowiredprivateOrderServiceorderService;// 依赖 OrderService}ServicepublicclassOrderService{AutowiredprivateUserServiceuserService;// 依赖 UserService}创建UserService需要OrderService创建OrderService又需要UserService形成循环。2.2 三级缓存的结构与作用// DefaultSingletonBeanRegistry 源码publicclassDefaultSingletonBeanRegistryextendsSimpleAliasRegistryimplementsSingletonBeanRegistry{// 一级缓存成品单例池privatefinalMapString,ObjectsingletonObjectsnewConcurrentHashMap(256);// 二级缓存提前暴露的对象半成品privatefinalMapString,ObjectearlySingletonObjectsnewConcurrentHashMap(16);// 三级缓存单例工厂用于创建代理对象privatefinalMapString,ObjectFactory?singletonFactoriesnewHashMap(16);}缓存存放内容作用singletonObjects完全初始化好的 Bean最终使用的单例池earlySingletonObjects已实例化但未初始化的 Bean解决循环依赖暴露半成品singletonFactoriesObjectFactory可生成 Bean 或代理延迟创建代理对象解决循环依赖中的 AOP 代理问题2.3 循环依赖的解决流程以UserService→OrderService→UserService为例1. getBean(userService) ↓ 2. 从 singletonObjects 查找 → 未找到 ↓ 3. 实例化 UserService调用构造方法 ↓ 4. 将 UserService 的 ObjectFactory 放入 singletonFactories三级缓存 ↓ 5. 开始属性填充populateBean发现需要 OrderService ↓ 6. getBean(orderService) ↓ 7. 实例化 OrderService ↓ 8. 将 OrderService 的 ObjectFactory 放入 singletonFactories ↓ 9. 开始属性填充发现需要 UserService ↓ 10. getBean(userService) —— 再次获取 UserService ↓ 11. 从 singletonObjects 查找 → 未找到 ↓ 12. 从 earlySingletonObjects 查找 → 未找到 ↓ 13. 从 singletonFactories 查找 → 找到调用 ObjectFactory.getObject() ↓ 14. 返回 UserService 的半成品已实例化未初始化 ↓ 15. OrderService 完成属性填充、初始化 ↓ 16. OrderService 放入 singletonObjects ↓ 17. 回到 UserService 的属性填充OrderService 已可用 ↓ 18. UserService 完成初始化 ↓ 19. UserService 放入 singletonObjects2.4 为什么需要三级缓存二级缓存不够吗核心原因AOP 代理对象的创建时机问题。如果只有二级缓存循环依赖中的 Bean 在提前暴露时必须是最终形态包括代理对象。但 AOP 代理在初始化后postProcessAfterInitialization才创建而循环依赖的属性填充发生在初始化之前。三级缓存的ObjectFactory作用延迟创建代理对象。当从三级缓存获取 Bean 时ObjectFactory会判断是否需要提前创建代理通过SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference()确保暴露的是代理对象而非原始对象。// 三级缓存的 ObjectFactory 源码简化addSingletonFactory(beanName,()-{// 如果有 AOP 代理提前创建代理对象否则返回原始对象returngetEarlyBeanReference(beanName,mbd,bean);});2.5 循环依赖的局限场景是否支持原因单例 属性注入✅ 支持三级缓存解决单例 构造器注入❌ 不支持构造器注入时 Bean 尚未实例化无法提前暴露原型Prototype❌ 不支持原型 Bean 不缓存每次创建都是新实例构造器循环依赖的解决方案改用 Setter/字段注入使用Lazy延迟注入重构代码消除循环依赖。3. BeanPostProcessor 的作用与执行时机BeanPostProcessor是 Spring 提供的扩展接口允许在 Bean 初始化的前后插入自定义逻辑。接口方法执行时机典型用途BeanPostProcessorpostProcessBeforeInitialization初始化之前Aware 之后自定义初始化前处理BeanPostProcessorpostProcessAfterInitialization初始化之后AOP 代理创建、自定义初始化后处理InstantiationAwareBeanPostProcessorpostProcessBeforeInstantiation实例化之前自定义实例化逻辑如代理替换InstantiationAwareBeanPostProcessorpostProcessAfterInstantiation实例化之后控制是否继续属性填充InstantiationAwareBeanPostProcessorpostProcessProperties属性填充时自定义属性注入如Autowired处理MergedBeanDefinitionPostProcessorpostProcessMergedBeanDefinitionBeanDefinition 合并后缓存注解元数据重要后置处理器汇总后置处理器实现的接口核心作用AutowiredAnnotationBeanPostProcessorInstantiationAwareBeanPostProcessor,MergedBeanDefinitionPostProcessor处理Autowired、ValueCommonAnnotationBeanPostProcessorInstantiationAwareBeanPostProcessor,MergedBeanDefinitionPostProcessor处理Resource、PostConstruct、PreDestroyApplicationContextAwareProcessorBeanPostProcessor处理Aware接口AbstractAutoProxyCreatorInstantiationAwareBeanPostProcessor,BeanPostProcessor创建 AOP 代理RequiredAnnotationBeanPostProcessorMergedBeanDefinitionPostProcessor处理Required4. 生产环境避坑指南4.1PostConstruct中不能使用Value注入的配置可以Value的注入在属性填充阶段已完成PostConstruct在属性填充之后执行因此可以正常使用。ServicepublicclassUserService{Value(${server.port})privateStringport;PostConstructpublicvoidinit(){System.out.println(端口: port);// ✅ 可以正常获取}}4.2 初始化方法中抛出异常会怎样如果PostConstruct、afterPropertiesSet()或自定义 init 方法抛出异常Spring 会包装为BeanCreationException该 Bean 的创建失败不会放入单例池。如果其他 Bean 依赖它也会级联失败。4.3 不要在BeanPostProcessor中触发其他 Bean 的初始化如果在BeanPostProcessor中调用getBean()获取其他 Bean可能导致该后置处理器尚未完全注册引发不可预期的问题。4.4 AOP 代理对象的类型判断AutowiredprivateUserServiceuserService;publicvoidtest(){// ❌ 错误JDK 代理时 instanceof 判断失败if(userServiceinstanceofUserServiceImpl){...}// ✅ 正确使用 Spring 工具类if(AopUtils.isAopProxy(userService)){Class?targetClassAopProxyUtils.ultimateTargetClass(userService);}// ✅ 正确判断接口if(userServiceinstanceofUserService){...}}4.5 延迟初始化Lazy对于启动耗时较长的 Bean可以使用Lazy延迟初始化ServiceLazypublicclassHeavyService{// 只有在首次被注入或获取时才初始化}4.6 销毁方法中不要依赖其他 Bean容器关闭时Bean 的销毁顺序不确定。如果在destroy()中调用其他 Bean 的方法可能该 Bean 已销毁导致空指针。5. 面试官追问与高分回答模板追问 1“说一下 Spring Bean 的生命周期”低分回答“包括实例化、属性注入、初始化、使用、销毁几个阶段。”太笼统没有触及源码高分回答Spring Bean 的生命周期是一个完整的流水线我从源码层面梳理加载 BeanDefinition通过BeanDefinitionReader解析配置注册到BeanDefinitionRegistryBeanDefinition 合并与后置处理MergedBeanDefinitionPostProcessor缓存Autowired、PostConstruct等注解元数据推断构造方法并实例化根据Autowired标注、参数数量等规则选择构造方法通过反射创建实例依赖注入populateBeanInstantiationAwareBeanPostProcessor处理Autowired、Value、Resource注入初始化前处理执行Aware接口BeanNameAware、ApplicationContextAware等然后BeanPostProcessor.postProcessBeforeInitialization()调用PostConstruct初始化InitializingBean.afterPropertiesSet()→ 自定义init-method初始化后处理BeanPostProcessor.postProcessAfterInitialization()创建 AOP 代理放入单例池三级缓存singletonObjects/earlySingletonObjects/singletonFactories销毁PreDestroy→DisposableBean.destroy()→ 自定义destroy-method。其中最关键的是三级缓存解决循环依赖和AOP 代理在初始化后创建这两个设计。追问 2“Spring 如何解决循环依赖为什么需要三级缓存”低分回答“通过三级缓存解决提前暴露半成品 Bean。”没有解释为什么需要三级高分回答Spring 通过三级缓存解决单例 Bean 的属性注入循环依赖缓存内容作用singletonObjects成品 Bean最终使用的单例池earlySingletonObjects半成品 Bean提前暴露供循环依赖注入singletonFactoriesObjectFactory延迟创建代理对象为什么需要三级缓存如果只有二级缓存循环依赖中提前暴露的 Bean 必须是最终形态。但 AOP 代理在初始化后postProcessAfterInitialization才创建而循环依赖的属性填充发生在初始化之前。三级缓存的ObjectFactory实现了延迟创建当从三级缓存获取 Bean 时通过getEarlyBeanReference()判断是否需要提前创建代理对象确保暴露的是代理对象而非原始对象。局限只支持单例 属性注入的循环依赖。构造器注入和原型 Bean 的循环依赖不支持。追问 3“PostConstruct、InitializingBean、自定义 init-method 的执行顺序是什么”高分回答执行顺序是PostConstruct → InitializingBean.afterPropertiesSet() → 自定义 init-method回调来源特点PostConstructJSR-250 标准最先执行推荐无侵入InitializingBeanSpring 接口次之侵入性较强需实现接口init-method配置指定最后执行最灵活可在 XML 或Bean中配置推荐优先使用PostConstruct它是 Java 标准注解不依赖 Spring 接口代码更解耦。追问 4“构造器注入和字段注入有什么区别Spring 推荐哪种”高分回答| 维度 | 构造器注入 | 字段注入 ||------|-----------|----------||依赖保证| 必填Bean 创建时就必须提供 | 可选可能为 null ||不可变性| 可配合final实现不可变 | 不能加final||测试友好性| 高可直接 new 并传参 | 低需要反射或 Spring 容器 ||循环依赖| 不支持可检测设计问题 | 支持可能隐藏设计问题 ||NPE 风险| 低依赖必有 | 高可能忘记注入 |Spring 官方推荐构造器注入Spring 4 开始原因依赖明确不可变启动时就能发现循环依赖无需Autowired注解Spring 4.3 单构造器自动注入。字段注入虽然代码简洁但测试困难、可能隐藏循环依赖问题不推荐。追问 5“BeanPostProcessor 和 BeanFactoryPostProcessor 有什么区别”高分回答| 维度 | BeanPostProcessor | BeanFactoryPostProcessor ||------|-------------------|-------------------------||作用对象| Bean 实例 | BeanDefinition ||执行时机| Bean 实例化之后、初始化前后 | BeanDefinition 加载完成后、Bean 实例化之前 ||典型用途| AOP 代理创建、属性注入处理 | 修改 BeanDefinition如ConfigurationProperties ||代表实现|AbstractAutoProxyCreator、AutowiredAnnotationBeanPostProcessor|ConfigurationClassPostProcessor、PropertySourcesPlaceholderConfigurer|BeanFactoryPostProcessor在 Bean 实例化之前执行可以修改 Bean 的定义如修改作用域、属性值。BeanPostProcessor在 Bean 实例化之后执行可以修改 Bean 实例如创建代理、注入依赖。典型应用PropertySourcesPlaceholderConfigurer处理${...}占位符是BeanFactoryPostProcessorAutowired注入是BeanPostProcessor。追问 6“AOP 代理为什么在初始化后创建而不是实例化时”高分回答AOP 代理在初始化后postProcessAfterInitialization创建原因有三依赖注入的完整性代理对象需要包装完整的 Bean 实例包括已注入的依赖。如果在实例化时创建代理后续的属性填充会作用于代理对象可能导致代理逻辑被覆盖或注入失败。初始化回调的一致性PostConstruct、InitializingBean等初始化逻辑需要在代理对象上执行确保代理能拦截这些回调虽然通常不拦截。循环依赖的兼容性如果 AOP 代理在实例化时创建循环依赖中提前暴露的代理对象可能尚未完成依赖注入导致其他 Bean 注入的是一个不完整的代理。但这也带来一个问题循环依赖中的 Bean 需要提前暴露代理对象。Spring 通过三级缓存的ObjectFactory.getEarlyBeanReference()解决——在必要时提前创建代理确保循环依赖注入的是代理对象而非原始对象。6. 方案选型速查表业务场景推荐方案核心理由初始化资源连接池、线程池PostConstruct标准注解最先执行无侵入校验依赖完整性InitializingBean可抛出异常阻止 Bean 创建复杂初始化逻辑自定义init-method最灵活可配置化释放资源关闭连接池PreDestroy标准注解最先执行获取容器上下文ApplicationContextAwareSpring 提供的标准扩展点动态修改 Bean 定义BeanFactoryPostProcessor实例化前修改 BeanDefinition创建 AOP 代理BeanPostProcessor初始化后包装代理对象解决构造器循环依赖Lazy或重构延迟注入或消除循环面试官想要的满分总结Spring Bean 的生命周期是一个从定义到销毁的精密流水线核心阶段包括加载 BeanDefinition → 合并与后置处理 → 推断构造方法实例化 → 依赖注入 → 初始化前处理Aware PostConstruct→ 初始化InitializingBean 自定义 init→ 初始化后处理AOP 代理→ 放入单例池 → 销毁。理解生命周期必须抓住两个核心设计三级缓存解决循环依赖和BeanPostProcessor 扩展机制。三级缓存中singletonFactories的ObjectFactory延迟创建代理对象是解决循环依赖中 AOP 代理问题的关键BeanPostProcessor则在 Bean 创建的关键节点提供扩展能力AOP 代理、依赖注入、注解处理都依赖于此。工程实践中优先使用构造器注入Spring 官方推荐优先使用PostConstruct做初始化标准注解、无侵入警惕循环依赖构造器注入可提前暴露设计问题。理解BeanPostProcessor和BeanFactoryPostProcessor的区别——前者操作 Bean 实例后者操作 Bean 定义——是区分初级和高级开发者的分水岭。觉得对您有帮助麻烦点点关注啦您的关注是我创作的最大动力~