• [Java Framework] 解决监听ContextRefreshedEvent事件执行多次问题


    简介

    搜索引擎很多答案都是未加测试,对想当然的“解决方案”以讹传讹,本文将从多个方面找到最低一个解决方案!

    • 始作俑者是对下面一段代码
      “容器已经初始化”会出现多次的情况进行解决
    @Component
    public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
        @Autowired
        private ApplicationContext applicationContext;
    
        @Override
        public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
            System.out.println("容器已经初始化");
            
        }
    }
    

    解释:
    在web开发中会存在这样问题, 项目会存在两个容器,一个是spring的ioc容器(父),一个是springmvc的ioc容器(子),这两个容器是父子关系。这样就会造成onApplicationEvent方法被执行两次。为了解决此问题,我们可以判断当前容器是否父容器,是父容器才执行下边的代码。

    方法 / 步骤

    ContextRefreshedEvent ApplicationContext 被初始化或刷新时,该事件被触发。这也可以在 ConfigurableApplicationContext接口中使用 refresh() 方法来发生。此处的初始化是指:所有的Bean被成功装载后处理Bean被检测,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用

    ❌方案一: 判断是否是父容器进行触发(测试无效)

    @Component
    public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
        @Autowired
        private ApplicationContext applicationContext;
    
        @Override
        public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
            ApplicationContext parent = contextRefreshedEvent.getApplicationContext().getParent();
            if (parent == null){
                System.out.println("容器已经初始化");
            }
    
        }
    }
    

    ❌方案二: 判断DisplayName是否是Root容器进行触发(测试无效)

    @Component
    public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
        @Autowired
        private ApplicationContext applicationContext;
       	if ("Root WebApplicationContext”".equals(contextRefreshedEvent.getApplicationContext().getDisplayName())) {
                //需要执行的方法
                System.out.println("容器已经初始化");
            }
    
    }
    

    如果想 ApplicationListener 只执行一次,那就只应该把它配置在 其中一个ApplicationContext 中,另外一个ApplicationContext 不配置。也就是说,使ApplicationListener的实现类,只被其中一个ApplicationContext 加载到。

    ✅方案三: 把ContextRefreshedEvent改成ApplicationReadyEvent事件 (测试可用)

    @Component
    public class BizListenerInitializer implements ApplicationListener<ApplicationReadyEvent> {
        @Override
        public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
            System.out.println("容器已经初始化");
        }
    }
    

    ✅方案四: 在Bus总线监听异步事件业务场景

    业务中使用消息总线做异步操作,对自定义事件进行实例化时候,通过唯一ID和时间戳方式做幂等操作!

    参考资料 & 致谢

  • 相关阅读:
    朴素贝叶斯简介
    spring boot-Resolved element must not contain multiple elements 警告
    计算机编程
    java扩展jmeter依赖
    【算法训练-动态规划 五】【二维DP问题】最大正方形
    将虚拟机VMware从C盘移动到E盘
    每日一题9.17打家劫舍2
    常用的git分支管理方法都在这了
    Ultralytics YOLOv8的关键特点
    (c语言)经典bug
  • 原文地址:https://blog.csdn.net/YangCheney/article/details/127110134