• [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和时间戳方式做幂等操作!

    参考资料 & 致谢

  • 相关阅读:
    cannot find defineEmits(or defineProps) in ts的原因
    PMP项目管理学习笔记
    【Leetcode】链表排序(逐步提高时空复杂度)
    路由交换技术之代理ARP
    第九章【ADFS集成Exchang实现OWA\ECP单点登录SSO】验证owa是否成功集成adfs
    java计算机毕业设计权限办公用具采购管理源码+系统+数据库+lw文档+mybatis+运行部署
    【考研】数据结构(更新到顺序表)
    逍遥自在学C语言 | 枚举的那些事儿
    hue编译、启动、使用
    html5 web 按钮跳转方法(及其相关)
  • 原文地址:https://blog.csdn.net/YangCheney/article/details/127110134