一、Spring Bean 生命周期
1.1 完整生命周期流程
以 ApplicationContext 为例(最常用容器)
-
BeanDefinition 注册阶段(Bean 还没创建)
- 解析 XML / @Configuration
- 扫描 @Component
- 生成 BeanDefinition
- 注册到 BeanFactory
此时:
- 只是“定义”
- 还没有实例
可扩展点:
- BeanFactoryPostProcessor
- BeanDefinitionRegistryPostProcessor
这个阶段可以:
- 修改 Bean 的作用域
- 改 Bean 的 class
- 动态注册 Bean
-
实例化 Bean
UserService userService = new UserService();源码入口:
AbstractAutowireCapableBeanFactory#createBean此时:
- 只是 new 出对象
- 还没有依赖注入
- 还没有 AOP
-
属性填充(依赖注入)
@Autowired private OrderService orderService;执行:
populateBean()完成:
- @Autowired
- @Value
- @Resource
- setter 注入
注意:此时还没有执行初始化方法
-
aware 回调
如果实现了这些接口:
BeanNameAware BeanFactoryAware ApplicationContextAware会被回调:
setBeanName() setBeanFactory() setApplicationContext()-
BeanNameAware- 让 Bean 知道自己在容器里的名字。
-
BeanFactoryAware-
让你拿到:BeanFactory
你可以获取 Bean
beanFactory.getBean(OrderService.class);作用:
- 手动获取其他 Bean
- 延迟获取 Bean(避免循环依赖)
- 动态创建 Bean
本质:主动使用 IoC 容器
-
-
ApplicationContextAware-
它比 BeanFactory 更强。
ApplicationContext = BeanFactory + 国际化 + 事件机制 + 资源加载 + 环境变量你可以:
- 发布事件
- 读取配置
- 加载文件
- 获取环境变量
-
它们本质都是:让 Bean 反向获取 Spring 容器信息
-
-
BeanPostProcessor(前置)
applyBeanPostProcessorsBeforeInitialization()此时:
- Bean 已注入
- 但还没初始化
你可以:
- 修改 Bean
- 替换 Bean
- 包装 Bean
这里是 AOP 的关键入口之一典型实现:
-
初始化
执行顺序:
- @PostConstruct
- InitializingBean.afterPropertiesSet()
- init-method
例子:
@PostConstruct public void init1(){} @Override public void afterPropertiesSet(){} @Bean(initMethod="init2")顺序:
@PostConstruct afterPropertiesSet init-method -
BeanPostProcessor(后置)
执行:
applyBeanPostProcessorsAfterInitialization()AOP 代理就是在这里创建的!!!
关键类:
AbstractAutoProxyCreator如果:
- 有 @Transactional
- 有 @Aspect
- 有切面
此时会:
原始对象 → 代理对象代理对象最终放入容器
-
Bean 可用阶段
现在 Bean 已经:
- 初始化完成
- 代理完成
- 放入单例池
-
销毁阶段
当容器关闭:
context.close();执行:
- @PreDestroy
- DisposableBean.destroy()
- destroy-method
容器关闭的时间:
-
手动调用
context.close();或者
SpringApplication.run(...).close(); -
JVM 关闭时(注册了 shutdown hook)
坑点:
-
只销毁单例 Bean,prototype 不会销毁,因为 prototype:Spring 不管理生命周期,用完就丢
-
很多人以为:
@Scope("prototype")就一定是“每次用都是新的”。其实错,如果:
@Autowired private UserService userService;它只会注入一次。
想每次都 new,必须:
@Autowired private ObjectProvider<UserService> provider;然后:
provider.getObject();
整生命周期顺序
- BeanDefinition 注册阶段
- 实例化
- 属性填充
- BeanNameAware
- BeanFactoryAware
- ApplicationContextAware
- BeanPostProcessor before
- @PostConstruct
- afterPropertiesSet
- init-method
- BeanPostProcessor after(AOP代理在这里)
- Bean 使用
- @PreDestroy
- destroy()
- destroy-method
记住:AOP 在初始化之后生成代理对象
1.2 三级缓存
1.2.1 为什么需要三级缓存
核心问题只有一个:解决 singleton 的循环依赖(A 依赖 B,B 又依赖 A)
例如:
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
如果没有特殊机制:
创建 A
→ 需要 B
→ 创建 B
→ 需要 A
→ 创建 A
→ 无限递归
会 StackOverflow。
解即思路:在 A 还没初始化完成时,就提前暴露一个“早期对象”给 B 使用。
但是问题来了:如果 A 需要 AOP 代理怎么办?这就是为什么需要 三级缓存。
注意:三级缓存只解决:
单例 + setter 注入
不支持:
- prototype:因为这种Spring只负责创建不负责管理他的生命周期
- 构造器循环依赖:构造器循环依赖无法解决,因为在实例化阶段就必须完成依赖解析,容器还没机会把当前 Bean 放入三级缓存。
1.2.2 三个缓存到底是什么?
在 DefaultSingletonBeanRegistry 里有三个 Map:
// 一级缓存:完整的 Bean(最终形态)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
// 二级缓存:提前暴露的半成品 Bean(已经实例化,但未初始化)
private final Map<String, Object> earlySingletonObjects = new HashMap<>();
// 三级缓存:ObjectFactory(用于生成早期引用)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
一级缓存:singletonObjects
存放:完整初始化完成的 Bean(最终版本)
当 Bean 完全创建完成后:
singletonObjects.put(beanName, bean);
这是你平时 getBean 拿到的对象。
二级缓存:earlySingletonObjects
存放:存放“已经通过三级缓存工厂生成的 early reference”
特点:
- 已实例化
- 如果发生循环依赖时,通常已经是 early 代理对象
三级缓存:singletonFactories(最关键)
存放:一个 ObjectFactory(对象工厂)
为什么是工厂?
因为:Spring 不确定你是否需要代理,所以它存的不是对象,而是:
() -> getEarlyBeanReference()
可以理解为:“未来可能变成代理对象的生成器”
1.2.3 三级缓存演示
场景
@Component
class A {
@Autowired
private B b;
}
@Component
class B {
@Autowired
private A a;
}
依赖关系:
A → B
B → A
执行流程
-
创建 A(实例化)
调用:
doCreateBean("A")实例化 A(构造器执行),将 A 的 ObjectFactory 放入三级缓存
singletonFactories.put("A", () -> getEarlyBeanReference()); -
A 需要注入 B(依赖注入 )
进入:
populateBean("A")发现需要 B
-
创建 B
同样流程:
- 实例化 B
- B 的 ObjectFactory 放入三级缓存
-
需要注入 A
B 在注入时调用:
getSingleton("A")查找顺序:
- 一级缓存 —— 没有
- 二级缓存 —— 没有
- 三级缓存 —— 有!
于是:
Object earlyBean = singletonFactory.getObject();执行:
getEarlyBeanReference()如果 A 有 AOP,这里就会创建代理!
然后:
- 放入二级缓存
- 删除三级缓存
-
B 注入 A(早期对象)
B 完成创建,放入一级缓存
-
回到 A
A 注入 B,完成初始化,放入一级缓存,清理二级缓存。
1.2.4 为什么需要三级缓存
如果只有两级缓存:
一级:成品
二级:半成品
当 B 需要 A 时,如果 A 还没代理完成,你只能拿到原始对象。
但后面 A 可能会被 AOP 代理。
那就会出现:
B 里面是原始 A
容器里是代理 A
两个不同对象
会导致:
- 事务失效
- AOP 失效
三级缓存的作用:
延迟决定是否创建代理,只有真正需要被依赖时,才调用:
getEarlyBeanReference()
这个方法会:
getEarlyBeanReference()
→ wrapIfNecessary()
判断是否要代理。
三级缓存核心逻辑图
实例化
↓
放入三级缓存(工厂)
↓
发生循环依赖?
↓ 是
从三级缓存获取 early bean
↓
可能创建代理
↓
放入二级缓存
↓
完成初始化
↓
放入一级缓存
三级缓存的核心目的:
在 Bean 完全初始化前,提前暴露可能是代理对象的早期引用,从而解决 singleton 的循环依赖问题。
1.2.5 earlyProxyReference
出现位置
-
getEarlyBeanReference(beanName, mbd, bean) -
内部调用:
SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference -
真正实现者是:
AbstractAutoProxyCreator
earlyProxyReference 是干嘛的?
-
它的作用只有一个:
记录:这个 bean 在早期阶段已经被代理过了 -
源码里有一个集合:
private final Set<Object> earlyProxyReferences = Collections.newSetFromMap(new ConcurrentHashMap<>(16));在 getEarlyBeanReference 里:
this.earlyProxyReferences.add(cacheKey); return wrapIfNecessary(bean, beanName, cacheKey);
为什么要记录这个?
因为否则会出现:
早期生成一个代理
初始化阶段再生成一个代理
变成:
代理套代理
这叫“双代理问题”。
初始化阶段如何避免重复代理?
在:
postProcessAfterInitialization()
内部判断:
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
意思是:
如果这个 bean 在 early 阶段已经代理过
那初始化阶段就别再代理了
所以:
earlyProxyReference = 防止双代理的标记机制
1.2.6 总结
当出现:
A 有事务
A → B
B → A
流程:
- A 实例化
- A factory 放入三级缓存
- B 需要 A
- getBean("A")
- 进入 getSingleton
- 调用 getEarlyBeanReference
- 生成代理 A
- 记录 earlyProxyReferences
- 放入二级缓存
- B 注入的是代理 A
然后:
A 初始化阶段:
- 检查 earlyProxyReferences
- 发现已代理
- 不再创建新代理
最终只有一个代理。
二,Bean的生命周期扩展点
2.1 生命周期整体时间线
BeanDefinition 注册
↓
实例化前
↓
实例化
↓
属性注入
↓
Aware 回调
↓
初始化前
↓
初始化
↓
初始化后(AOP)
↓
使用阶段
↓
销毁阶段
2.2 BeanFactoryPostProcessor
Bean 还没实例化
能干什么?
- 修改 BeanDefinition 的属性
- 改 scope
- 改属性值
代码示例:把 Bean 改成 prototype
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
BeanDefinition bd = beanFactory.getBeanDefinition("userService");
bd.setScope("prototype");
System.out.println("修改 userService 为 prototype");
}
}
2.3 BeanDefinitionRegistryPostProcessor
最早阶段 —— Bean 还没创建
能干什么?
- 动态注册 Bean
- 修改 BeanDefinition
- 改 class / scope
BeanDefinitionRegistryPostProcessor 是 BeanFactoryPostProcessor 的子接口
代码示例:动态注册一个 Bean
@Component
public class MyRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserService.class);
registry.registerBeanDefinition("userService2", builder.getBeanDefinition());
System.out.println("动态注册 userService2");
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 用于获取已经注册的bean
}
}
效果:启动时会多一个 userService2
典型用途:
- MyBatis Mapper 注册
- @ComponentScan
2.4 InstantiationAwareBeanPostProcessor
实例化前 & 实例化后
能干什么?
- 实例化前直接返回代理
- 控制是否注入属性
代码示例:实例化前直接替换对象
@Component
public class MyInstantiationProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
if (beanClass == UserService.class) {
System.out.println("提前返回代理对象");
return new UserService(null); // 可替换为代理
}
return null;
}
}
注意:返回非 null 会跳过默认创建流程。
2.5 postProcessProperties
属性注入阶段
能干什么?
- 修改注入属性
- 替换 @Autowired
代码示例:修改注入字段
@Component
public class MyPropertyProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public PropertyValues postProcessProperties(
PropertyValues pvs,
Object bean,
String beanName) {
if (bean instanceof UserService userService) {
userService.setName("被修改的名字");
}
return pvs;
}
}
2.6 Aware 接口
2.6.1 BeanNameAware Demo
@Component
public class BeanAware implements BeanNameAware {
@Override
public void setBeanName(String name) {
System.out.println("Bean 名字:" + name);
}
}
2.6.2 ApplicationContextAware Demo
@Component
public class BeanApplicationAware implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext ctx) {
System.out.println("容器中 Bean 数量:" + ctx.getBeanDefinitionCount());
}
}
用途:
- 发布事件
- 手动 getBean
2.7 BeanPostProcessor(初始化前)
能干什么?
- 执行 @PostConstruct
- 修改 Bean
- 包装 Bean
代码示例
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(
Object bean, String beanName) {
if (bean instanceof UserService) {
System.out.println("初始化前增强");
}
return bean;
}
}
2.8 初始化阶段
2.8.1 @PostConstruct
@Component
public class UserBean {
@PostConstruct
public void init() {
System.out.println("@PostConstruct 执行");
}
}
2.8.2 InitializingBean
@Component
public class UserBean implements InitializingBean {
@Override
public void afterPropertiesSet() {
System.out.println("afterPropertiesSet 执行");
}
}
2.8.3 init-method
@Configuration
public class Config {
@Bean(initMethod = "initMethod")
public UserBean userBean(){
return new UserBean();
}
}
2.9 BeanPostProcessor(初始化后)
注意: AOP 就在这里。
代码示例:简单日志代理
@Component
public class MyProxyProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(
Object bean, String beanName) {
if (bean instanceof UserService) {
return Proxy.newProxyInstance(
bean.getClass().getClassLoader(),
bean.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("方法执行前");
Object result =
method.invoke(bean, args);
System.out.println("方法执行后");
return result;
}
);
}
return bean;
}
}
2.10 SmartInitializingSingleton
所有单例创建完成后
代码示例
@Component
public class MySmartInit
implements SmartInitializingSingleton {
@Override
public void afterSingletonsInstantiated() {
System.out.println("所有单例已创建完成");
}
}
用途:
- 全局校验
- 启动任务
2.11 DestructionAwareBeanPostProcessor
代码示例
@Component
public class MyDestroyProcessor
implements DestructionAwareBeanPostProcessor {
@Override
public void postProcessBeforeDestruction(
Object bean, String beanName) {
if (bean instanceof UserService) {
System.out.println("销毁前增强");
}
}
}
2.12 @PreDestroy
代码示例
@Component
public class MyDestroyProcessor{
@PreDestroy
public void destroy() {
System.out.println("@PreDestroy 执行");
}
}
2.13 DisposableBean
代码示例
@Component
public class MyDestroyProcessor implements DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("销毁Bean");
}
}
2.14 总结
| 阶段 | 扩展点 | 能力 |
|---|---|---|
| 注册阶段 | BeanDefinitionRegistryPostProcessor | 改容器结构 |
| 定义阶段 | BeanFactoryPostProcessor | 改 Bean 定义 |
| 实例化阶段 | InstantiationAwareBeanPostProcessor | 控制创建 |
| 初始化前后 | BeanPostProcessor | 替换 / 代理 |
| 全局完成 | SmartInitializingSingleton | 最终统一处理 |
| 销毁阶段 | DestructionAwareBeanPostProcessor | 销毁增强 |
三,增强初始化方法
项目中,经常需要在启动过程中初始化一些数据,如从数据库读取一些配置初始化,或从数据库读取一些热点数据到 redis 进行初始化缓存。
他们之间的初始化顺序为:
类加载
↓
static
↓
实例化(构造器)
↓
依赖注入
↓
Aware
↓
@PostConstruct
↓
afterPropertiesSet
↓
init-method
↓
SmartInitializingSingleton
↓
ApplicationRunner / CommandLineRunner
3.1 使用@PostConstruct注解的方式
对于注入到 Spring 容器中的类,在其成员函数前添加 @PostConstruct 注解,则在执行 Spring beans 初始化时,就会执行该函数。
@PostConstruct 方法执行时:当前 Bean 的依赖已经注入完成,但整个容器未必完全启动
@Component
public class InitHandler{
@PostConstruct
public void init(){
System.out.println("bean初始化!");
}
}
注意:
- @PostConstruct注解使用在方法上,它可以被用来标注一个非静态的 void 方法,这个方法会在该类被 Spring 容器初始化后立即执行。
- 因为它的执行时机是在依赖注入之后,对象构造完成之后,也就是说是在@Autowired注入之后执行。所以这里可以进行一些初始化操作,如某些需要在对象创建后才能进行的数据初始化操作。
- 注意要点:
- @PostConstruct 只能用在方法上面,而不能用在属性或构造函数上。
- 一个类中可以有多个使用 @PostConstruct 注解的方法,但执行顺序并不是固定的。
- 虽然规范允许,但 Spring 并不保证顺序,而且:JSR-250 标准里建议一个类只有一个 @PostConstruct。
- @PostConstruct 注解的方法在本类中必须是无参数的,如果有参数,那么这个方法不会被执行。
- @PostConstruct 注解的方法在实现上可以使用任意修饰符。
3.2 实现ApplicationRunner接口重写run方法逻辑
ApplicationRunner 接口与 CommandLineRunner 接口类似,都需要实现 run() 方法。二者的区别在于run() 方法的参数不同:
@Component
public class InitHandler implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("项目初始化!");
}
}
注意点:
-
ApplicationRunner 接口的 run() 参数为
ApplicationArguments对象,因此可以获取更多项目相关的内容。 -
ApplicationRunner 接口与 CommandLineRunner 接口的调用时机也是相同的,都是 Spring beans 初始化之后。因此 ApplicationRunner 接口也使用
@Order(n)来设置执行顺序。
3.3 实现CommandLineRunner 接口重写run方法逻辑
@Component
public class InitHandler implements CommandLineRunner{
@Override
public void run(String... args) {
System.out.println("InitHandler init \t"+args.length);
}
}
注意:
-
CommandLineRunner 的执行时机为 Spring bean 初始化之后,因此CommandLineRunner 的执行一定是晚于 @PostConstruct 的。
-
若有多组初始化操作,则每一组操作都要定义一个 CommandLineRunner 派生类并实现 run()方法。这些操作的执行顺序使用 @Order(n) 来设置,n 为 int 型数据。
@Component @Order(2) public class InitHandlerA implements CommandLineRunner{ @Override public void run(String... args) { System.out.println("InitHandler init \t"+args.length); } } @Component @Order(1) public class InitHandlerB implements CommandLineRunner{ @Override public void run(String... args) { System.out.println("InitHandler init \t"+args.length); } }-
如上,会先执行 CommandLineRunnerB 的 run(),再执行 CommandLineRunnerA 的 run()。
-
@Order(n) 中的 n 较小的会先执行,较大的后执行。n 只要是 int 值即可,无需顺序递增。
-
四,增强销毁方法
在 Spring Boot 应用程序中,可以使用多种方式在应用程序结束时执行销毁操作。这些方法通常用于释放资源、关闭连接、保存状态等。
4.1 使用 @PreDestroy 注解
@PreDestroy 是 Java 的标准注解,用于在 Spring Bean 被销毁之前执行清理操作。只需在 bean 的销毁方法上标注该注解即可。
@Component
public class MyService {
@PreDestroy
public void cleanup() {
System.out.println("Executing cleanup before bean destruction...");
// 释放资源或执行其他销毁操作
}
}
当应用程序关闭时,Spring 会自动调用 cleanup() 方法。
4.2 实现 DisposableBean 接口
Spring 提供了 DisposableBean 接口,允许你在销毁 bean 时执行特定的操作。这个接口定义了一个 destroy() 方法,可以在此方法中编写销毁逻辑。
@Component
public class MyService implements DisposableBean {
@Override
public void destroy() {
System.out.println("Executing cleanup in destroy method...");
// 释放资源或执行其他销毁操作
}
}
当 Spring 容器销毁该 bean 时,会自动调用 destroy() 方法。
4.3 使用 @Bean 注解的 destroyMethod 属性
在配置类中使用 @Bean 注解定义 bean 时,可以通过 destroyMethod 属性指定一个方法作为销毁方法。这个方法会在 bean 销毁时被调用。
@Configuration
public class MyConfig {
@Bean(destroyMethod = "cleanup")
public MyService myService() {
return new MyService();
}
}
class MyService {
public void cleanup() {
System.out.println("Executing cleanup in custom destroy method...");
// 释放资源或执行其他销毁操作
}
}
当 Spring 容器关闭时,会调用 cleanup() 方法。
4.4 实现 SmartLifecycle 接口
Spring 容器启动完成后,有些组件需要:
- 自动启动
- 有启动顺序
- 有停止顺序
- 容器关闭时自动优雅停机
例如:
- 消息监听器
- MQ 消费者
- 定时任务
- Netty 服务器
- 长连接客户端
这些组件不能在 @PostConstruct 启动。
因为:@PostConstruct 是 Bean 初始化完成就执行但此时 Spring 可能还没完全 refresh 完
而 SmartLifecycle 是:在容器 refresh 完成之后统一启动,在容器关闭时统一停止
接口结构
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
int getPhase();
}
继承关系:
SmartLifecycle
├── Lifecycle
│ ├── start()
│ ├── stop()
│ ├── isRunning()
│
└── Phased
└── getPhase()
代码示例
@Component
public class MQConsumer implements SmartLifecycle {
private boolean running = false;
@Override
public void start() {
System.out.println("MQConsumer 启动...");
running = true;
}
@Override
public void stop() {
System.out.println("MQConsumer 停止...");
running = false;
}
@Override
public void stop(Runnable callback) {
System.out.println("MQConsumer 优雅停止...");
stop();
callback.run(); // 必须调用
}
@Override
public boolean isRunning() {
return running;
}
@Override
public boolean isAutoStartup() {
return true; // 容器刷新后自动启动
}
@Override
public int getPhase() {
return 1; // 先启动
}
}
4.4 使用 @EventListener 监听 ContextClosedEvent
@EventListener 注解可以用于监听 Spring 上下文的事件。通过监听 ContextClosedEvent 事件,可以在应用程序关闭时执行销毁操作。
@Component
public class MyService {
@EventListener(ContextClosedEvent.class)
public void onContextClosed() {
System.out.println("Executing cleanup on context closed...");
// 释放资源或执行其他销毁操作
}
}