Springイベントメカニズム-onApplicationEventは2回実行


詳細
一、case再現
イベント定義
 
public class MyEvent extends ApplicationEvent {

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

 
 
リスニング定義
 
@Component
public class MyListener implements ApplicationListener {

    @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 getBean(String beanName, Class 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のアプリケーションContextという2つのコンテナが存在する.xmlの親コンテナとspringMVCのアプリケーションContext-mvc.xmlのサブコンテナ.
アプリケーションContextで通知を送信すると、イベントは2つのコンテナでパブリッシュされ、このような状況になります.
四、解決方案
原因を知って、解決策は比較的に簡単で、ネット上の大多数の方案を見てすべて
   @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の問題を解決して、多くのみんなはもっと多くの方案があって、伝言を残して一緒に勉強することを望みます