Spring事件-ApplicationListener ContextClosedEvent被调用三次解析

时间:2024-04-02 07:25:30

发布事件的源代码所在处

public void publishEvent(ApplicationEvent event) {
    Assert.notNull(event, "Event must not be null");
    if (this.logger.isTraceEnabled()) {
        this.logger.trace("Publishing event in " + this.getDisplayName() + ": " + event);
    }

    this.getApplicationEventMulticaster().multicastEvent(event);
    if (this.parent != null) {
        this.parent.publishEvent(event);
    }

}

Spring父子容器发布事件调用都是这段代码

父容器finishRefresh时发布事件this.parent == null,会调用一次
子容器finishRefresh时发布事件this.parent != null,会调用两次
所以一个事件监听器会被调用三次,必要时我们需要对自己对监听器做幂等,防止重复调用引发bug!!! 
例如,加以下判断只允许父容器调用一次:
@Override  
public void onApplicationEvent(ContextRefreshedEvent event) {
    if(event.getApplicationContext().getDisplayName().equals("Root WebApplicationContext")){
        // 监听器逻辑
    }
}

Spring事件-ApplicationListener ContextClosedEvent被调用三次解析

 

/*
上下文更新事件(ContextRefreshedEvent):该事件会在ApplicationContext被初始化或者更新时发布。也可以在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。
 */