1. Case recurrence
event definition
public class MyEvent extends ApplicationEvent { public MyEvent(Object object) { super(object); } }
monitor definition
@Component public class MyListener implements ApplicationListener<MyEvent> { @Override public void onApplicationEvent(MyEvent event) { System.out.println("myEvent occured msg : " + event.getSource()); } }
event notification
@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); } /** * Notify events * * @param applicationEvent */ public static void pushEvent(ApplicationEvent applicationEvent) { //Get the event sent by the parent container //ContextLoader.getCurrentWebApplicationContext().publishEvent(applicationEvent); applicationContext.publishEvent(applicationEvent); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public static ApplicationContext getApplicationContext() { return applicationContext; } }
Test Results:
myEvent occured msg : publish myEvent occured msg : publish
3. Reason
If both spring and springMVC are integrated in a web project, there will be two containers in the context, namely the parent container of spring's applicationContext.xml and the child container of springMVC's applicationContext-mvc.xml.
When a notification is sent through the applicationContext, the event will be published by both containers, causing the above.
4. Solutions
Knowing the reason, the solution is relatively simple. Most of the solutions on the Internet are
@Override public void onApplicationEvent(ContextRefreshedEvent event) { if(event.getApplicationContext().getParent() == null){ //The logic code that needs to be executed, this method will be executed when the spring container is initialized. } }
However, this scheme predetermines the type of event, and custom events are not feasible, so the idea of the solution is to use the parent container to send notifications
/** * Notify events * * @param applicationEvent */ public static void pushEvent(ApplicationEvent applicationEvent) { //Get the event sent by the parent container ContextLoader.getCurrentWebApplicationContext().publishEvent(applicationEvent); }
Test Results:
myEvent occured msg : publish
So far, the problem of this case has been solved, and everyone has better and more solutions. I hope to leave a message and learn together.