网站Logo 苏叶的belog

SpringBean的生命周期

wdadwa
4
2026-03-03

一、Spring Bean 生命周期

1.1 完整生命周期流程

ApplicationContext 为例(最常用容器)

  1. BeanDefinition 注册阶段(Bean 还没创建)

    • 解析 XML / @Configuration
    • 扫描 @Component
    • 生成 BeanDefinition
    • 注册到 BeanFactory

    此时:

    • 只是“定义”
    • 还没有实例

    可扩展点:

    • BeanFactoryPostProcessor
    • BeanDefinitionRegistryPostProcessor

    这个阶段可以:

    • 修改 Bean 的作用域
    • 改 Bean 的 class
    • 动态注册 Bean
  2. 实例化 Bean

    UserService userService = new UserService();
    

    源码入口:

    AbstractAutowireCapableBeanFactory#createBean
    

    此时:

    • 只是 new 出对象
    • 还没有依赖注入
    • 还没有 AOP
  3. 属性填充(依赖注入)

    @Autowired
    private OrderService orderService;
    

    执行:

    populateBean()
    

    完成:

    • @Autowired
    • @Value
    • @Resource
    • setter 注入

    注意:此时还没有执行初始化方法

  4. 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 容器信息

  5. BeanPostProcessor(前置)

    applyBeanPostProcessorsBeforeInitialization()
    

    此时:

    • Bean 已注入
    • 但还没初始化

    你可以:

    • 修改 Bean
    • 替换 Bean
    • 包装 Bean

    这里是 AOP 的关键入口之一典型实现:

  6. 初始化

    执行顺序:

    1. @PostConstruct
    2. InitializingBean.afterPropertiesSet()
    3. init-method

    例子:

    @PostConstruct
    public void init1(){}
    
    @Override
    public void afterPropertiesSet(){}
    
    @Bean(initMethod="init2")
    

    顺序:

    @PostConstruct
    afterPropertiesSet
    init-method
    
  7. BeanPostProcessor(后置)

    执行:

    applyBeanPostProcessorsAfterInitialization()
    

    AOP 代理就是在这里创建的!!!

    关键类:

    AbstractAutoProxyCreator
    

    如果:

    • 有 @Transactional
    • 有 @Aspect
    • 有切面

    此时会:

    原始对象 → 代理对象
    

    代理对象最终放入容器

  8. Bean 可用阶段

    现在 Bean 已经:

    • 初始化完成
    • 代理完成
    • 放入单例池
  9. 销毁阶段

    当容器关闭:

    context.close();
    

    执行:

    1. @PreDestroy
    2. DisposableBean.destroy()
    3. destroy-method

    容器关闭的时间:

    • 手动调用

      context.close();
      

      或者

      SpringApplication.run(...).close();
      
    • JVM 关闭时(注册了 shutdown hook)

    坑点

    1. 只销毁单例 Bean,prototype 不会销毁,因为 prototype:Spring 不管理生命周期,用完就丢

    2. 很多人以为:@Scope("prototype") 就一定是“每次用都是新的”。

      其实错,如果:

      @Autowired
      private UserService userService;
      

      它只会注入一次。

      想每次都 new,必须:

      @Autowired
      private ObjectProvider<UserService> provider;
      

      然后:

      provider.getObject();
      

整生命周期顺序

  1. BeanDefinition 注册阶段
  2. 实例化
  3. 属性填充
  4. BeanNameAware
  5. BeanFactoryAware
  6. ApplicationContextAware
  7. BeanPostProcessor before
  8. @PostConstruct
  9. afterPropertiesSet
  10. init-method
  11. BeanPostProcessor after(AOP代理在这里)
  12. Bean 使用
  13. @PreDestroy
  14. destroy()
  15. 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

执行流程

  1. 创建 A(实例化)

    调用:

    doCreateBean("A")
    

    实例化 A(构造器执行),将 A 的 ObjectFactory 放入三级缓存

    singletonFactories.put("A", () -> getEarlyBeanReference());
    
  2. A 需要注入 B(依赖注入 )

    进入:

    populateBean("A")
    

    发现需要 B

  3. 创建 B

    同样流程:

    • 实例化 B
    • B 的 ObjectFactory 放入三级缓存
  4. 需要注入 A

    B 在注入时调用:

    getSingleton("A")
    

    查找顺序:

    1. 一级缓存 —— 没有
    2. 二级缓存 —— 没有
    3. 三级缓存 —— 有!

    于是:

    Object earlyBean = singletonFactory.getObject();
    

    执行:

    getEarlyBeanReference()
    

    如果 A 有 AOP,这里就会创建代理!

    然后:

    • 放入二级缓存
    • 删除三级缓存
  5. B 注入 A(早期对象)

    B 完成创建,放入一级缓存

  6. 回到 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

流程:

  1. A 实例化
  2. A factory 放入三级缓存
  3. B 需要 A
  4. getBean("A")
  5. 进入 getSingleton
  6. 调用 getEarlyBeanReference
  7. 生成代理 A
  8. 记录 earlyProxyReferences
  9. 放入二级缓存
  10. 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注入之后执行。所以这里可以进行一些初始化操作,如某些需要在对象创建后才能进行的数据初始化操作。
  • 注意要点:
    1. @PostConstruct 只能用在方法上面,而不能用在属性或构造函数上。
    2. 一个类中可以有多个使用 @PostConstruct 注解的方法,但执行顺序并不是固定的。
      • 虽然规范允许,但 Spring 并不保证顺序,而且:JSR-250 标准里建议一个类只有一个 @PostConstruct。
    3. @PostConstruct 注解的方法在本类中必须是无参数的,如果有参数,那么这个方法不会被执行。
    4. @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...");
        // 释放资源或执行其他销毁操作
    }
}
动物装饰