知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!
Spring Bean 初始化过程深入分析
Spring Bean 的初始化是生命周期中的关键阶段,涉及多个扩展点和执行顺序的精确控制。以下是初始化过程的详细解析:
1. 初始化阶段的触发时机
初始化在 属性注入(依赖注入)完成之后 开始,此时 Bean 的实例已存在,但尚未完全就绪(如代理未生成、自定义逻辑未执行)。
2. 初始化方法执行流程
Spring 按固定顺序调用三类初始化方法:
-
@PostConstruct
注解方法- 触发方式 :由
CommonAnnotationBeanPostProcessor
处理。 - 特点:JSR-250 标准注解,执行顺序最先。
java@Component public class MyBean { @PostConstruct public void init() { System.out.println("@PostConstruct 方法执行"); } }
- 触发方式 :由
-
InitializingBean.afterPropertiesSet()
- 接口定义 :Spring 原生接口,优先级次于
@PostConstruct
。
java@Component public class MyBean implements InitializingBean { @Override public void afterPropertiesSet() { System.out.println("InitializingBean 方法执行"); } }
- 接口定义 :Spring 原生接口,优先级次于
-
自定义
init-method
- 配置方式 :XML 中
<bean init-method="init">
或@Bean(initMethod = "init")
。 - 执行顺序:最后触发。
javapublic class MyBean { public void customInit() { System.out.println("自定义 init-method 执行"); } } @Configuration public class AppConfig { @Bean(initMethod = "customInit") public MyBean myBean() { return new MyBean(); } }
- 配置方式 :XML 中
3. 初始化过程源码级分析
Spring 通过 initializeBean()
方法协调初始化流程:
java
// AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
// 1. 执行 Aware 接口回调(BeanNameAware, BeanFactoryAware 等)
invokeAwareMethods(beanName, bean);
// 2. BeanPostProcessor 前置处理
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
}
try {
// 3. 执行初始化方法
invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable ex) {
throw new BeanCreationException(...);
}
// 4. BeanPostProcessor 后置处理(如生成 AOP 代理)
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
关键方法 invokeInitMethods
的实现:
java
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
// 执行 InitializingBean.afterPropertiesSet()
if (bean instanceof InitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
// 执行自定义 init-method
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(bean instanceof InitializingBean && "afterPropertiesSet".equals(initMethodName))) {
Method initMethod = ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName);
ReflectionUtils.makeAccessible(initMethod);
initMethod.invoke(bean);
}
}
4. 初始化扩展点详解
4.1 BeanPostProcessor 的作用
-
postProcessBeforeInitialization
在初始化方法前执行,可修改 Bean 属性或返回包装对象。
javapublic class CustomBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { if (bean instanceof MyBean) { System.out.println("BeforeInitialization: " + beanName); } return bean; } }
-
postProcessAfterInitialization
在初始化方法后执行,常用于生成代理对象。
java@Override public Object postProcessAfterInitialization(Object bean, String beanName) { if (bean instanceof MyService) { return Enhancer.create(bean.getClass(), (MethodInterceptor) (obj, method, args, proxy) -> { System.out.println("代理逻辑执行"); return proxy.invokeSuper(obj, args); }); } return bean; }
4.2 @PostConstruct
的实现机制
- 由
CommonAnnotationBeanPostProcessor
处理。 - 底层通过反射调用标记了
@PostConstruct
的方法。 - 执行顺序控制 :该处理器优先级高于处理
InitializingBean
的逻辑。
5. 初始化过程中的典型问题与解决方案
5.1 初始化方法未执行
- 常见原因 :
- Bean 未被 Spring 管理(如未加
@Component
)。 - 原型作用域 Bean 未被正确获取。
- 自定义
init-method
名称拼写错误。
- Bean 未被 Spring 管理(如未加
- 排查工具 :
启用 Spring 调试日志(logging.level.org.springframework.beans=DEBUG
)。
5.2 初始化顺序依赖
-
需求场景:Bean A 需在 Bean B 初始化完成后才能初始化。
-
解决方案 :
- 使用
@DependsOn("b")
注解。
java@Component @DependsOn("b") public class A { // ... }
- 使用
5.3 在初始化方法中访问其他 Bean
- 风险:若依赖的 Bean 尚未初始化完成,可能导致 NPE。
- 最佳实践 :
通过ApplicationContext.getBean()
延迟获取(需谨慎使用)。
6. 初始化阶段与其他生命周期的交互
阶段 | 与初始化的关系 |
---|---|
实例化 | 初始化必须在实例化之后(对象已存在) |
属性注入 | 所有依赖注入完成才会触发初始化(确保 @Autowired 字段可用) |
AOP 代理 | 代理通常在 postProcessAfterInitialization 生成,因此原始 Bean 的初始化方法先执行 |
销毁阶段 | 初始化与销毁方法通过对称设计(如 @PreDestroy 对应 @PostConstruct ) |
7. 特殊场景下的初始化行为
7.1 原型(Prototype)Bean
- 特点:每次请求创建新实例,初始化方法每次都会执行。
- 陷阱 :Spring 不会管理原型 Bean 的销毁阶段,
@PreDestroy
不生效。
7.2 延迟初始化(Lazy Init)
- 配置方式 :
@Lazy
或<bean lazy-init="true">
。 - 行为:在首次访问时才会触发初始化和依赖注入。
7.3 FactoryBean 的初始化
- 特殊逻辑 :
FactoryBean.getObject()
返回的对象会单独走初始化流程。
总结:初始化阶段的核心要点
要素 | 关键实现 |
---|---|
执行顺序 | @PostConstruct → InitializingBean → init-method |
扩展点 | BeanPostProcessor 、@PostConstruct 、InitializingBean |
代理生成时机 | postProcessAfterInitialization (可能覆盖原始 Bean) |
设计原则 | 通过接口与注解分离关注点,避免在初始化方法中实现复杂业务逻辑 |