spring事件机制-onApplicationEvent执行两次

一、case复现

事件定义

public class MyEvent extends ApplicationEvent {

    public MyEvent(Object object) {
        super(object);
    }
}

监听定义

@Component
public class MyListener implements ApplicationListener<MyEvent> {

    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println("myEvent occured msg : " + event.getSource());
    }
}

事件通知

@ResponseBody
@RequestMapping(value = "/publish")
public String publish(String key) {
     BeanFactory.pushEvent(new MyEvent("publish"));
     return "success";
}
@Component
public class BeanFactory implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    public static <T> T getBean(String beanName, Class<T> clazz) {
        return (T) applicationContext.getBean(beanName);
    }

    /**
     * 通知事件
     *
     * @param applicationEvent
     */
    public static void pushEvent(ApplicationEvent applicationEvent) {
        //获取父容器发送事件
        //ContextLoader.getCurrentWebApplicationContext().publishEvent(applicationEvent);
        applicationContext.publishEvent(applicationEvent);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
}

 测试结果:

myEvent occured msg : publish
myEvent occured msg : publish

三、原因

在web项目中如果同时集成了spring和springMVC的话,上下文中会存在两个容器,即spring的applicationContext.xml的父容器和springMVC的applicationContext-mvc.xml的子容器。

在通过applicationContext发送通知的时候,事件会被两个容器发布,造成上述情况。

四、解决方案

知道了原因,解决方案就比较简单了,看了网上大多数的方案都是

@Override
   public void onApplicationEvent(ContextRefreshedEvent event) {
       if(event.getApplicationContext().getParent() == null){
            //需要执行的逻辑代码,当spring容器初始化完成后就会执行该方法。
       }
   }

 但这种方案先定了事件的类型,自定义的事件是行不通的,所以解决方案的思路是用父容器去发送通知

/**
     * 通知事件
     *
     * @param applicationEvent
     */
    public static void pushEvent(ApplicationEvent applicationEvent) {
        //获取父容器发送事件
        ContextLoader.getCurrentWebApplicationContext().publishEvent(applicationEvent);
    }

 测试结果:

myEvent occured msg : publish

 至此,解决了这个case的问题,多大家有更好更多的方案,希望留言一起学习

相关推荐