文章目录

  • 一、Java中事件/监听器编程模型
  • 1、Java中Observable/Observer事件监听
  • (1)代码实例
  • (2)Java9新的事件监听
  • 2、面向接口的事件/监听器设计模式
  • 3、面向注解的事件/监听器设计模式
  • 二、Spring事件
  • 1、Spring标准事件-ApplicationEvent
  • 2、基于接口的 Spring 事件监听器
  • 代码实例
  • 3、基于注解的 Spring 事件监听器
  • 代码实例
  • 4、注册Spring ApplicationListener
  • 方法一:ApplicationListener 作为 Spring Bean 注册
  • 方法二:通过 ConfigurableApplicationContext API 注册
  • 方法三:直接使用注解@EventListener
  • 三种方式的执行顺序
  • 5、Spring 事件发布器
  • (1)源码分析
  • (2)依赖注入获取ApplicationEventPublisher
  • (3)依赖查找 ApplicationEventMulticaster
  • (4)ApplicationEventPublisher 发布事件
  • (5)ApplicationEventPublisher与ApplicationEventMulticaster的关联
  • 6、Spring 层次性上下文事件传播
  • 代码实例
  • 源码分析
  • 事件重复处理
  • 7、Spring 内建事件
  • 源码分析
  • 8、Spring 4.2 Payload 事件
  • 代码实例及分析
  • 9、自定义 Spring 事件
  • 10、同步和异步 Spring 事件广播
  • (1)基于实现类 - org.springframework.context.event.SimpleApplicationEventMulticaster
  • (2)基于注解 - @org.springframework.context.event.EventListener
  • (3)小tips
  • 11、Spring 4.1 事件异常处理
  • 源码分析
  • 代码实例
  • 12、Spring 事件/监听器实现原理
  • 源码分析
  • 基于注解的监听器如何注册
  • 三、扩展-Spring Boot 、Spring Cloud事件
  • 1、Spring Boot 事件
  • 2、Spring Cloud 事件
  • 参考资料

一、Java中事件/监听器编程模型

1、Java中Observable/Observer事件监听

设计模式 - 观察者模式扩展
• 可观者对象(消息发送者) - java.util.Observable
• 观察者 - java.util.Observer

标准化接口
• 事件对象 - java.util.EventObject
• 事件监听器 - java.util.EventListener

Observable/Observer 是观察者模型在 JDK 中的实现,而 EventObject 和 EventListener 是事件驱动的接口,这里有涉及实现,实现可以利用 Observable/Observer 或者扩展来完成。

Observable/Observer 是jdk中观察者模式的实现标准,有具体实现。但是事件驱动只是建议基于EventObject/EventListener来拓展,并没有具体实现。Spring事件并没有用到Observable/Observer ,并且Observable/Observer 在 jdk 9 被推荐转为java.util.concurrent.Flow实现。

JDK 中的 Observable/Observer 只是一个参考,它的执行顺序和插入顺序是相反,也即是它是 Stack 的方式,无法实现自定义顺序,并且它使用了 Vector 线程安全的集合容器,无法提升高性能,所以 Spring 没有必要用到它。

(1)代码实例

import java.util.EventListener;
import java.util.EventObject;
import java.util.Observable;
import java.util.Observer;

/**
 * Java 事件监听示例
 * {@link Observer} 示例
 */
public class ObserverDemo {

    public static void main(String[] args) {
        EventObservable observable = new EventObservable();
        // 添加观察者(监听者)
        observable.addObserver(new EventObserver());
        // 发布消息(事件)
        observable.notifyObservers("Hello,World");
    }

    static class EventObservable extends Observable {

        public void setChanged() {
            super.setChanged();
        }

        public void notifyObservers(Object arg) {
            setChanged(); // 需要手动打开开关
            super.notifyObservers(new EventObject(arg));
            clearChanged();
        }
    }

    static class EventObserver implements Observer, EventListener {

        @Override
        public void update(Observable o, Object event) {
            EventObject eventObject = (EventObject) event;
            System.out.println("收到事件 :" + eventObject);
        }
    }
}

(2)Java9新的事件监听

Observer和Observable在JDK9以后被废弃,因为观察者和可观察对象支持的事件模型是非常有限的,可观察对象传递的通知的顺序无法指定,状态变化与通知不能一一对应。

为了在线程之间可靠而有序地传递消息,可以考虑在java.util.concurrent包中使用一种并发数据结构。对于响应式流风格的编程,javaDOC提示考虑使用java.util.concurrent.Flow

2、面向接口的事件/监听器设计模式

事件/监听器场景举例:

Java 技术规范

事件接口

监听器接口

JavaBeans

java.beans.PropertyChangeEvent

java.beans.PropertyChangeListener

Java AWT

java.awt.event.MouseEvent

java.awt.event.MouseListener

Java Swing

javax.swing.event.MenuEvent

javax.swing.event.MenuListener

Java Preference

java.util.prefs.PreferenceChangeEvent

java.util.prefs.PreferenceChangeListener

通过上面列举的类的源码我们可以看到,Event都是继承了EventObject;Listener都是继承了EventListener,这基本是一个约定俗成的事情,Spring也不例外。

3、面向注解的事件/监听器设计模式

事件/监听器注解场景举例:

Java 技术规范

事件注解

监听器注解

Servlet 3.0+

@javax.servlet.annotation.WebListener

JPA 1.0+

@javax.persistence.PostPersist

Java Common

@PostConstruct

EJB 3.0+

@javax.ejb.PrePassivate

JSF 2.0+

@javax.faces.event.ListenerFor

二、Spring事件

1、Spring标准事件-ApplicationEvent

上面我们提到过,Java 标准事件 java.util.EventObject 扩展的扩展特性就是事件发生事件戳。

而Spring 应用上下文 ApplicationEvent 扩展就是 ApplicationContextEvent,Spring 应用上下文(ApplicationContext)作为事件源。
具体实现:
• org.springframework.context.event.ContextClosedEvent
• org.springframework.context.event.ContextRefreshedEvent
• org.springframework.context.event.ContextStartedEvent
• org.springframework.context.event.ContextStoppedEvent

分析源码我们可以看出,ApplicationEvent抽象类继承了jdk的EventObject,ApplicationContextEvent抽象类又继承了ApplicationEvent。

ApplicationContextEvent就是Spring应用上下文的一个事件源,提供了获取ApplicationContext方法,事件的source就是ApplicationContext。

ApplicationContextEvent有以上四个实现:ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent。

2、基于接口的 Spring 事件监听器

Java 标准事件监听器 java.util.EventListener 扩展
• 扩展接口 - org.springframework.context.ApplicationListener
• 设计特点:单一类型事件处理
• 处理方法:onApplicationEvent(ApplicationEvent)
• 事件类型:org.springframework.context.ApplicationEvent

代码实例

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.support.GenericApplicationContext;

GenericApplicationContext context = new GenericApplicationContext();

// 向 Spring 应用上下文注册事件
context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println(("ApplicationListener - 接收到 Spring 事件:" + event));
    }
});

// 启动 Spring 应用上下文
context.refresh(); // ContextRefreshedEvent
// 启动 Spring 上下文(通常,ConfigurableApplicationContext#start() 方法是为了发布 ContextStartedEvent 事件,而真正的启动方法是 refresh() 方法。)
context.start();  // ContextStartedEvent
// 停止 Spring 上下文
context.stop();  // ContextStoppedEvent
// 关闭 Spring 应用上下文
context.close(); // ContextClosedEvent

我们通过该实例可以看出,Spring的四个事件通过Spring应用上下文的几个方法来触发的。

3、基于注解的 Spring 事件监听器

Spring 注解 - @org.springframework.context.event.EventListener

特性

说明

设计特点

支持多 ApplicationEvent 类型,无需接口约束

注解目标

方法

是否支持异步执行

支持

是否支持泛型类型事件

支持

是指支持顺序控制

支持,配合 @Order 注解控制

代码实例

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.event.*;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync // 激活异步
public class MyApplicationListenerDemo {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        // 将引导类 MyApplicationListenerDemo 作为 Configuration Class
        context.register(MyApplicationListenerDemo.class);

        // 启动 Spring 应用上下文
        context.refresh(); // ContextRefreshedEvent
        // 启动 Spring 上下文
        context.start();  // ContextStartedEvent
        // 停止 Spring 上下文
        context.stop();  // ContextStoppedEvent
        // 关闭 Spring 应用上下文
        context.close(); // ContextClosedEvent
    }

    @EventListener
    @Async // 需要@EnableAsync
    public void onApplicationEventAsync(ContextRefreshedEvent event) {
        println("@EventListener(异步) - 接收到 Spring ContextRefreshedEvent");
    }

    @EventListener
    @Order(2)
    public void onApplicationEvent(ContextStartedEvent event) {
        println("@EventListener - 接收到 Spring ContextStartedEvent1");
    }
    @EventListener
    @Order(1) // 可以使用@Order控制顺序,数字越小越先执行
    public void onApplicationEvent2(ContextStartedEvent event) {
        println("@EventListener - 接收到 Spring ContextStartedEvent2");
    }

    @EventListener
    public void onApplicationEvent(ContextClosedEvent event) {
        println("@EventListener - 接收到 Spring ContextClosedEvent");
    }

    @EventListener
    public void onApplicationEvent(ContextStoppedEvent event) {
        println("@EventListener - 接收到 Spring ContextStoppedEvent");
    }

    private static void println(Object printable) {
        System.out.printf("[线程:%s] : %s\n", Thread.currentThread().getName(), printable);
    }
}

执行结果:

[线程:SimpleAsyncTaskExecutor-1] : @EventListener(异步) - 接收到 Spring ContextRefreshedEvent
[线程:main] : @EventListener - 接收到 Spring ContextStartedEvent2
[线程:main] : @EventListener - 接收到 Spring ContextStartedEvent1
[线程:main] : @EventListener - 接收到 Spring ContextStoppedEvent
[线程:main] : @EventListener - 接收到 Spring ContextClosedEvent

使用注解监听Spring事件相对而言比较简单,而且可以手动实现异步、进行排序。

4、注册Spring ApplicationListener

方法一:ApplicationListener 作为 Spring Bean 注册

// 基于 ApplicationListener 注册为 Spring Bean
// 通过 Configuration Class 来注册
context.register(MyApplicationListener.class);

public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        println("MyApplicationListener - 接收到 Spring 事件:" + event);
    }
}

方法二:通过 ConfigurableApplicationContext API 注册

以上通过接口、注解的方式其实就是使用方法二的方式进行注册的

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

// 将引导类 ApplicationListenerDemo 作为 Configuration Class
context.register(ApplicationListenerDemo.class);

// 基于 Spring 接口:向 Spring 应用上下文注册事件
// 基于 ConfigurableApplicationContext API 实现
context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        println("ApplicationListener - 接收到 Spring 事件:" + event);
    }
});

方法三:直接使用注解@EventListener

上面介绍过了

三种方式的执行顺序

通过运行代码我们发现,基于注解的Listener先执行,而后是基于接口的,最后才是注册为Bean的。

后面我们会通过分析源码来分析这个执行顺序的原因。

5、Spring 事件发布器

方法一:通过 ApplicationEventPublisher 发布 Spring 事件(事件发布)
• 获取 ApplicationEventPublisher:依赖注入

方法二:通过 ApplicationEventMulticaster 发布 Spring 事件(事件广播)
• 获取 ApplicationEventMulticaster:依赖注入、依赖查找

(1)源码分析

ApplicationEventPublisher接口有两个方法,其中有个方法是Spring4.2引入的。

@FunctionalInterface
public interface ApplicationEventPublisher {

	default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}

	void publishEvent(Object event);
}

ApplicationEventMulticaster接口会在ApplicationEventPublisher接口的基础上增加很多东西,包括对ApplicationListener的增删改查,multicastEvent方法就是事件广播方法。

public interface ApplicationEventMulticaster {

	void addApplicationListener(ApplicationListener<?> listener);

	void addApplicationListenerBean(String listenerBeanName);

	void removeApplicationListener(ApplicationListener<?> listener);
	
	void removeApplicationListenerBean(String listenerBeanName);

	void removeAllListeners();

	void multicastEvent(ApplicationEvent event);

	void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}

(2)依赖注入获取ApplicationEventPublisher

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import javax.annotation.PostConstruct;

/**
 * 注入 {@link ApplicationEventPublisher} 示例
 */
public class InjectingApplicationEventPublisherDemo implements ApplicationEventPublisherAware,
        ApplicationContextAware, BeanPostProcessor {

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    private ApplicationContext applicationContext;

    @PostConstruct
    public void init() {
        //# 3
        applicationEventPublisher.publishEvent(new MySpringEvent("The event from @Autowired ApplicationEventPublisher"));
        // #4
        applicationContext.publishEvent(new MySpringEvent("The event from @Autowired ApplicationContext"));
    }

    public static void main(String[] args) {

        // 创建注解驱动 Spring 应用上下文
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        // 注册 Configuration Class
        context.register(InjectingApplicationEventPublisherDemo.class);

        // 增加 Spring 事件监听器
        context.addApplicationListener(new MySpringEventListener());

        // 启动 Spring 应用上下文
        context.refresh();

        // 关闭 Spring 应用上下文
        context.close();
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { // #1
        applicationEventPublisher.publishEvent(new MySpringEvent("The event from ApplicationEventPublisherAware"));
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // #2
        // ApplicationContext
        applicationEventPublisher.publishEvent(new MySpringEvent("The event from ApplicationContextAware"));
    }
}

通过以上实例我们可以看出,获取ApplicationEventPublisher 有两种方式,一种是通过 ApplicationEventPublisherAware 回调接口;一种是通过 @Autowired ApplicationEventPublisher。

而ApplicationContext也是继承了ApplicationEventPublisher,所以我们认为它也是一种ApplicationEventPublisher,也具有事件发布的能力。

(3)依赖查找 ApplicationEventMulticaster

查找条件
• Bean 名称:“applicationEventMulticaster”
• Bean 类型:org.springframework.context.event.ApplicationEventMulticaster

我们进行一下源码分析:

事实上,我们调用context.addApplicationListener()方法时,会委派给applicationEventMulticaster调用它的addApplicationListener方法。

// org.springframework.context.support.AbstractApplicationContext#addApplicationListener
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
	Assert.notNull(listener, "ApplicationListener must not be null");
	if (this.applicationEventMulticaster != null) {
		this.applicationEventMulticaster.addApplicationListener(listener);
	}
	this.applicationListeners.add(listener);
}

而此处的applicationEventMulticaster初始化方法:

// org.springframework.context.support.AbstractApplicationContext#initApplicationEventMulticaster
protected void initApplicationEventMulticaster() {
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { // applicationEventMulticaster
		this.applicationEventMulticaster =
				beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
		if (logger.isTraceEnabled()) {
			logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
		}
	}
	else {
		this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); // 默认使用这个
		beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
		if (logger.isTraceEnabled()) {
			logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
					"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
		}
	}
}

initApplicationEventMulticaster这个方法是在springIOC容器refresh时被调用的。

而实际上ApplicationEventPublisher 发布事件的底层实现,也是通过getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); 实现的。ApplicationEventPublisher 没有直接实现。

(4)ApplicationEventPublisher 发布事件

通过以下实例我们可以看出,实现了ApplicationEventPublisherAware 接口可以获取到ApplicationEventPublisher ,从而直接发送事件(这种事件貌似只能通过addApplicationListener添加的监听器接收到,通过注解的方式是接收不到的,具体原因尚不明确)。

import org.springframework.context.*;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyApplicationListenerDemo implements ApplicationEventPublisherAware {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        // 将引导类 MyApplicationListenerDemo 作为 Configuration Class
        context.register(MyApplicationListenerDemo.class);
        context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
            @Override
            public void onApplicationEvent(ApplicationEvent event) {
                System.out.println(("ApplicationListener - 接收到 Spring 事件:" + event));
            }
        });

        // 启动 Spring 应用上下文
        context.refresh(); // ContextRefreshedEvent
        // 启动 Spring 上下文
        context.start();  // ContextStartedEvent
        // 停止 Spring 上下文
        context.stop();  // ContextStoppedEvent
        // 关闭 Spring 应用上下文
        context.close(); // ContextClosedEvent
    }


    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        applicationEventPublisher.publishEvent(new ApplicationEvent("Hello,World"){});

        // 发送 PayloadApplicationEvent
        applicationEventPublisher.publishEvent("Hello,World2");
    }

}

也可以通过通过 @Autowired ApplicationEventPublisher 来依赖注入ApplicationEventPublisher 。

(5)ApplicationEventPublisher与ApplicationEventMulticaster的关联

底层实现:
• 接口:org.springframework.context.event.ApplicationEventMulticaster
• 抽象类:org.springframework.context.event.AbstractApplicationEventMulticaster
• 实现类:org.springframework.context.event.SimpleApplicationEventMulticaster

实际上,ApplicationEventPublisher与ApplicationEventMulticaster之间是没有接口依赖关系的。

那么他们两个是怎么关联的呢?

我们通过context调用publishEvent方法,里面的逻辑我们再看一下:

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
	Assert.notNull(event, "Event must not be null");

	// Decorate event as an ApplicationEvent if necessary
	ApplicationEvent applicationEvent;
	if (event instanceof ApplicationEvent) {
		applicationEvent = (ApplicationEvent) event;
	}
	else {
		applicationEvent = new PayloadApplicationEvent<>(this, event);
		if (eventType == null) {
			eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
		}
	}

	// Multicast right now if possible - or lazily once the multicaster is initialized
	if (this.earlyApplicationEvents != null) {
		this.earlyApplicationEvents.add(applicationEvent);
	}
	else {
		// 关键逻辑!获取ApplicationEventMulticaster
		getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
	}

	// Publish event via parent context as well...
	if (this.parent != null) {
		if (this.parent instanceof AbstractApplicationContext) {
			((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
		}
		else {
			this.parent.publishEvent(event);
		}
	}
}

我们可以看到,ApplicationEventPublisher的publishEvent方法底层其实是调用了ApplicationEventMulticaster的multicastEvent方法,而SimpleApplicationEventMulticaster作为ApplicationEventMulticaster的一个默认实现,是支持异步事件发送的:

// org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
	Executor executor = getTaskExecutor();
	for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
		if (executor != null) {
			executor.execute(() -> invokeListener(listener, event));
		}
		else {
			invokeListener(listener, event);
		}
	}
}

话又说回来,earlyApplicationEvents 是什么呢?
其实这是Spring4开始支持的,就是早期的Event。Spring3其实有个bug,如果一个类同时实现ApplicationEventPublisherAware、BeanPostProcessor或者BeanFactoryPostProcessor,就会出问题。

SpringIOC在初始化时执行的refresh方法:

// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.
initMessageSource();

// Initialize event multicaster for this context.
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
onRefresh();

// Check for listener beans and register them.
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();

我们发现,如果一个bean实现了BeanPostProcessor,会在initApplicationEventMulticaster初始化ApplicationEventMulticaster之前初始化,所以当此时发布事件的时候,ApplicationEventMulticaster还未初始化,导致报错,所以使用一个earlyApplicationEvents 临时存储,当registerListeners()方法执行完之后,此时ApplicationEventMulticaster已经完全初始化了,这时候取出所有存储的earlyApplicationEvents ,进行挨个事件发布。

所以,这相当于一个Spring的BUG,只不过在后期修复了。

6、Spring 层次性上下文事件传播

发生说明:
当 Spring 应用出现多层次 Spring 应用上下文(ApplicationContext)时,如 Spring WebMVC、Spring Boot或 Spring Cloud 场景下,由子 ApplicationContext 发起 Spring 事件可能会传递到其 Parent ApplicationContext(直到 Root)的过程。

如何避免:
定位 Spring 事件源(ApplicationContext)进行过滤处理。

代码实例

import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.event.ApplicationContextEvent;

import java.util.LinkedHashSet;
import java.util.Set;

/**
 * 层次性 Spring 事件传播实例
 */
public class HierarchicalSpringEventPropagateDemo {

    public static void main(String[] args) {
        // 1. 创建 parent Spring 应用上下文
        AnnotationConfigApplicationContext parentContext = new AnnotationConfigApplicationContext();
        parentContext.setId("parent-context");
        // 注册 MyListener 到 parent Spring 应用上下文
        parentContext.register(MyListener.class);

        // 2. 创建 current Spring 应用上下文
        AnnotationConfigApplicationContext currentContext = new AnnotationConfigApplicationContext();
        currentContext.setId("current-context");
        // 3. current -> parent
        currentContext.setParent(parentContext);
        // 注册 MyListener 到 current Spring 应用上下文
        currentContext.register(MyListener.class);

        // 4.启动 parent Spring 应用上下文
        parentContext.refresh();

        // 5.启动 current Spring 应用上下文
        currentContext.refresh();

        // 关闭所有 Spring 应用上下文
        currentContext.close();
        parentContext.close();
    }

    static class MyListener implements ApplicationListener<ApplicationContextEvent> {

        private
        //static
        Set<ApplicationContextEvent> processedEvents = new LinkedHashSet<>();

        @Override
        public void onApplicationEvent(ApplicationContextEvent event) {
            if (processedEvents.add(event)) {
                System.out.printf("监听到 Spring 应用上下文[ ID : %s ] 事件 :%s\n", event.getApplicationContext().getId(),
                        event.getClass().getSimpleName());
            }
        }
    }
}

执行结果:

监听到 Spring 应用上下文[ ID : parent-context ] 事件 :ContextRefreshedEvent
监听到 Spring 应用上下文[ ID : current-context ] 事件 :ContextRefreshedEvent
监听到 Spring 应用上下文[ ID : current-context ] 事件 :ContextRefreshedEvent
监听到 Spring 应用上下文[ ID : current-context ] 事件 :ContextClosedEvent
监听到 Spring 应用上下文[ ID : current-context ] 事件 :ContextClosedEvent
监听到 Spring 应用上下文[ ID : parent-context ] 事件 :ContextClosedEvent

我们可以看出,每个事件会执行三次,因为current执行完毕之后,还会在其parent中执行一次。

源码分析

我们可以看一下context的refresh方法触发的finishRefresh方法中publishEvent触发的事件逻辑:

// org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
	Assert.notNull(event, "Event must not be null");

	// Decorate event as an ApplicationEvent if necessary
	ApplicationEvent applicationEvent;
	if (event instanceof ApplicationEvent) {
		applicationEvent = (ApplicationEvent) event;
	}
	else {
		applicationEvent = new PayloadApplicationEvent<>(this, event);
		if (eventType == null) {
			eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
		}
	}

	// Multicast right now if possible - or lazily once the multicaster is initialized
	if (this.earlyApplicationEvents != null) {
		this.earlyApplicationEvents.add(applicationEvent);
	}
	else {
		getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
	}

	// Publish event via parent context as well...
	if (this.parent != null) { // 递归,如果有parent的话,就传递给parent进行事件发布
		if (this.parent instanceof AbstractApplicationContext) {
			((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
		}
		else {
			this.parent.publishEvent(event);
		}
	}
}

事件重复处理

比较简单的方式就是定义一个全局变量,如上我们的代码实例,定义了一个static的set做去重处理,同一个事件保证只能监听一次。

也可以注入ApplicationContext,判断事件的上下文对象是否是当前的上下文对象。从而进行过滤。

7、Spring 内建事件

ApplicationContextEvent 派生事件
• ContextRefreshedEvent :Spring 应用上下文就绪事件
• ContextStartedEvent :Spring 应用上下文启动事件
• ContextStoppedEvent :Spring 应用上下文停止事件
• ContextClosedEvent :Spring 应用上下文关闭事件

start 和 stop 方法是一种辅助的特性,通常使用不多。

源码分析

我们来分析一下context的refresh方法:

refresh方法最终会调用finishRefresh方法,会调用publishEvent方法发送ContextRefreshedEvent事件:

// org.springframework.context.support.AbstractApplicationContext#finishRefresh
protected void finishRefresh() {
	// Clear context-level resource caches (such as ASM metadata from scanning).
	clearResourceCaches();

	// Initialize lifecycle processor for this context.
	initLifecycleProcessor();

	// Propagate refresh to lifecycle processor first.
	getLifecycleProcessor().onRefresh();

	// Publish the final event.
	publishEvent(new ContextRefreshedEvent(this));

	// Participate in LiveBeansView MBean, if active.
	LiveBeansView.registerApplicationContext(this);
}

实际上,AbstractApplicationContext实现了ApplicationContext,ApplicationContext继承了ApplicationEventPublisher,所以在底层是可以发布事件的。

其他几个事件发送同理。

8、Spring 4.2 Payload 事件

Spring Payload 事件 - org.springframework.context.PayloadApplicationEvent

使用场景:简化 Spring 事件发送,关注事件源主体

发送方法:ApplicationEventPublisher#publishEvent(java.lang.Object)

代码实例及分析

import org.springframework.context.*;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyApplicationListenerDemo implements ApplicationEventPublisherAware {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        // 将引导类 MyApplicationListenerDemo 作为 Configuration Class
        context.register(MyApplicationListenerDemo.class);
        context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
            @Override
            public void onApplicationEvent(ApplicationEvent event) {
                System.out.println(("ApplicationListener - 接收到 Spring 事件:" + event));
            }
        });

        // 启动 Spring 应用上下文
        context.refresh(); // ContextRefreshedEvent
        // 启动 Spring 上下文
        context.start();  // ContextStartedEvent
        // 停止 Spring 上下文
        context.stop();  // ContextStoppedEvent
        // 关闭 Spring 应用上下文
        context.close(); // ContextClosedEvent
    }


    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        applicationEventPublisher.publishEvent(new ApplicationEvent("Hello,World"){});

        // 发送 PayloadApplicationEvent
        applicationEventPublisher.publishEvent("Hello,World2");
        applicationEventPublisher.publishEvent(new HelloPayloadApplicationEvent(this, "Hello,World3"));
    }

    static class HelloPayloadApplicationEvent extends PayloadApplicationEvent<String> {

        public HelloPayloadApplicationEvent(Object source, String payload) {
            super(source, payload);
        }
    }
}

通过运行我们可以看出,使用下面这种方式发送PayloadApplicationEvent会报错:

applicationEventPublisher.publishEvent(new HelloPayloadApplicationEvent(this, "Hello,World3"));

错误提示:

Mismatched number of generics specified

当我们把HelloPayloadApplicationEvent类改一下:

static class HelloPayloadApplicationEvent<String> extends PayloadApplicationEvent<String> {

    public HelloPayloadApplicationEvent(Object source, String payload) {
        super(source, payload);
    }
}

此时就不会报错了,正常发送我们的事件。

实际上,Spring一直没有处理过这个问题,在Payload事件中Bean的泛型具体化并没有处理好。

关于Spring泛型处理:Spring泛型处理源码详解,Java泛型处理

所以,想要发送PayloadApplicationEvent事件,只需要使用上面的那种方式即可,不需要再继承、new一个Event了:

// 发送 PayloadApplicationEvent
applicationEventPublisher.publishEvent("Hello,World2");// (√)
applicationEventPublisher.publishEvent(new HelloPayloadApplicationEvent(this, "Hello,World3")); // (×)

9、自定义 Spring 事件

(1)扩展 org.springframework.context.ApplicationEvent

import org.springframework.context.ApplicationEvent;

/**
 * 自定义 Spring 事件
 */
public class MySpringEvent extends ApplicationEvent {

    /**
     * Create a new {@code ApplicationEvent}.
     *
     * @param message 事件消息
     */
    public MySpringEvent(String message) {
        super(message);
    }

    @Override
    public String getSource() {
        return (String) super.getSource();
    }

    public String getMessage() {
        return getSource();
    }
}

(2)实现 org.springframework.context.ApplicationListener

import org.springframework.context.ApplicationListener;

/**
 * 自定义 Spring 事件监听器
 */
public class MySpringEventListener implements ApplicationListener<MySpringEvent> {

    @Override
    public void onApplicationEvent(MySpringEvent event) {
        System.out.printf("[线程 : %s] 监听到事件 : %s\n", Thread.currentThread().getName(), event);
    }
}

(3)注册 org.springframework.context.ApplicationListener

GenericApplicationContext context = new GenericApplicationContext();

// 1.添加自定义 Spring 事件监听器
context.addApplicationListener(new MySpringEventListener());

// 2.启动 Spring 应用上下文
context.refresh();

// 3. 发布自定义 Spring 事件
context.publishEvent(new MySpringEvent("Hello,World"));

// 4. 关闭 Spring 应用上下文
context.close();

10、同步和异步 Spring 事件广播

(1)基于实现类 - org.springframework.context.event.SimpleApplicationEventMulticaster

模式切换:setTaskExecutor(java.util.concurrent.Executor) 方法
• 默认模式:同步
• 异步模式:如 java.util.concurrent.ThreadPoolExecutor

设计缺陷:非基于接口契约编程

源码分析:
ApplicationEventMulticaster的一个默认实现就是SimpleApplicationEventMulticaster,它的multicastEvent如下:

// org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
	Executor executor = getTaskExecutor();
	for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
		if (executor != null) {
			executor.execute(() -> invokeListener(listener, event));
		}
		else {
			invokeListener(listener, event);
		}
	}
}

如果手动设置(setTaskExecutor)了taskExecutor,就是异步执行。

代码实例:

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 异步事件处理器示例
 */
public class AsyncEventHandlerDemo {

    public static void main(String[] args) {

        GenericApplicationContext context = new GenericApplicationContext();

        // 1.添加自定义 Spring 事件监听器
        context.addApplicationListener(new MySpringEventListener());

        // 2.启动 Spring 应用上下文
        context.refresh(); // 初始化 ApplicationEventMulticaster

        // 依赖查找 ApplicationEventMulticaster
        ApplicationEventMulticaster applicationEventMulticaster =
                context.getBean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);

        // 判断当前 ApplicationEventMulticaster 是否为 SimpleApplicationEventMulticaster
        if (applicationEventMulticaster instanceof SimpleApplicationEventMulticaster) {
            SimpleApplicationEventMulticaster simpleApplicationEventMulticaster =
                    (SimpleApplicationEventMulticaster) applicationEventMulticaster;
            // 切换 taskExecutor
            ExecutorService taskExecutor = Executors.newSingleThreadExecutor(new CustomizableThreadFactory("my-spring-event-thread-pool"));
            // 同步 -> 异步
            simpleApplicationEventMulticaster.setTaskExecutor(taskExecutor);

            // 添加 ContextClosedEvent 事件处理,优雅关闭线程池
            applicationEventMulticaster.addApplicationListener(new ApplicationListener<ContextClosedEvent>() {
                @Override
                public void onApplicationEvent(ContextClosedEvent event) {
                    if (!taskExecutor.isShutdown()) {
                        taskExecutor.shutdown();
                    }
                }
            });

        }

        // 3. 发布自定义 Spring 事件
        context.publishEvent(new MySpringEvent("Hello,World"));

        // 4. 关闭 Spring 应用上下文(ContextClosedEvent)
        context.close();

    }
}

import org.springframework.context.ApplicationListener;

/**
 * 自定义 Spring 事件监听器
 */
public class MySpringEventListener implements ApplicationListener<MySpringEvent> {

    @Override
    public void onApplicationEvent(MySpringEvent event) {
        System.out.printf("[线程 : %s] 监听到事件 : %s\n", Thread.currentThread().getName(), event);
    }
}

(2)基于注解 - @org.springframework.context.event.EventListener

模式切换
• 默认模式:同步
• 异步模式:标注 @org.springframework.scheduling.annotation.Async

实现限制:无法直接实现同步/异步动态切换

事实上注解方式实现异步,上面我们已经介绍过了。

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;

import static java.util.concurrent.Executors.newSingleThreadExecutor;

/**
 * 注解驱动异步事件处理器示例
 */
@EnableAsync // 激活 Spring 异步特性
public class AnnotatedAsyncEventHandlerDemo {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        // 1. 注册当前类作为 Configuration Class
        context.register(AnnotatedAsyncEventHandlerDemo.class);

        // 2.启动 Spring 应用上下文
        context.refresh(); // 初始化 ApplicationEventMulticaster

        // 3. 发布自定义 Spring 事件
        context.publishEvent(new MySpringEvent("Hello,World"));

        // 4. 关闭 Spring 应用上下文(ContextClosedEvent)
        context.close();

    }

    @Async // 同步 -> 异步
    @EventListener
    public void onEvent(MySpringEvent event) {
        System.out.printf("[线程 : %s] onEvent方法监听到事件 : %s\n", Thread.currentThread().getName(), event);
    }

    @Bean
    public Executor taskExecutor() {
        ExecutorService taskExecutor = newSingleThreadExecutor(new CustomizableThreadFactory("my-spring-event-thread-pool-a"));
        return taskExecutor;
    }
}

(3)小tips

上面的例子我们可以看出,基于实现类SimpleApplicationEventMulticaster手动设置线程池的方式来实现异步,其实是全局设置。

而使用注解的方式,可以指定方法异步。

11、Spring 4.1 事件异常处理

Spring 3.0 错误处理接口 - org.springframework.util.ErrorHandler

使用场景:
• Spring 事件(Events):SimpleApplicationEventMulticaster Spring 4.1 开始支持
• Spring 本地调度(Scheduling):org.springframework.scheduling.concurrent.ConcurrentTaskScheduler和org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler

源码分析

上面我们分析到,SimpleApplicationEventMulticaster发布事件执行multicastEvent方法,最终会调用invokeListener来调用监听器:

// org.springframework.context.event.SimpleApplicationEventMulticaster#invokeListener
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
	ErrorHandler errorHandler = getErrorHandler();
	if (errorHandler != null) { // 如果有ErrorHandler 
		try {
			doInvokeListener(listener, event);
		}
		catch (Throwable err) {
			errorHandler.handleError(err);
		}
	}
	else { // 没有ErrorHandler 就直接执行
		doInvokeListener(listener, event);
	}
}

代码实例

public class AsyncEventHandlerDemo {

    public static void main(String[] args) {

        GenericApplicationContext context = new GenericApplicationContext();

        // 启动 Spring 应用上下文
        context.refresh(); // 初始化 ApplicationEventMulticaster

        // 依赖查找 ApplicationEventMulticaster
        ApplicationEventMulticaster applicationEventMulticaster =
                context.getBean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);

        // 判断当前 ApplicationEventMulticaster 是否为 SimpleApplicationEventMulticaster
        if (applicationEventMulticaster instanceof SimpleApplicationEventMulticaster) {
            SimpleApplicationEventMulticaster simpleApplicationEventMulticaster =
                    (SimpleApplicationEventMulticaster) applicationEventMulticaster;

            simpleApplicationEventMulticaster.setErrorHandler(e -> {
                System.err.println("当 Spring 事件异常时,原因:" + e.getMessage());
            });
        }

        context.addApplicationListener(new ApplicationListener<MySpringEvent>() {
            @Override
            public void onApplicationEvent(MySpringEvent event) {
                System.out.printf("[线程 : %s] 监听到事件 : %s\n", Thread.currentThread().getName(), event);
                throw new RuntimeException("故意抛出异常");
            }
        });

        // 3. 发布自定义 Spring 事件
        context.publishEvent(new MySpringEvent("Hello,World"));

        // 4. 关闭 Spring 应用上下文(ContextClosedEvent)
        context.close();

    }
}

12、Spring 事件/监听器实现原理

通过上面我们的分析与实践,大致基本也了解了Spring 事件/监听器实现原理,在此我们进一步做一下总结。

核心类 - org.springframework.context.event.SimpleApplicationEventMulticaster

设计模式:观察者模式扩展
• 观察者 - org.springframework.context.ApplicationListener:API 添加、依赖查找
• 通知对象 - org.springframework.context.ApplicationEvent

执行模式:同步/异步

异常处理:org.springframework.util.ErrorHandler

泛型处理:org.springframework.core.ResolvableType

源码分析

SimpleApplicationEventMulticaster先继承了AbstractApplicationEventMulticaster,
AbstractApplicationEventMulticaster又实现了ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware。

在addApplicationListener时,实际上发生了以下事情:

// org.springframework.context.event.AbstractApplicationEventMulticaster#addApplicationListener
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
	synchronized (this.retrievalMutex) {
		// Explicitly remove target for a proxy, if registered already,
		// in order to avoid double invocations of the same listener.
		Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
		if (singletonTarget instanceof ApplicationListener) {
			this.defaultRetriever.applicationListeners.remove(singletonTarget);
		}
		this.defaultRetriever.applicationListeners.add(listener);
		this.retrieverCache.clear();
	}
}

defaultRetriever就是ListenerRetriever一个内部类,保存着ApplicationListener的集合:

public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();

在AbstractApplicationEventMulticaster定义了一个

final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);

ListenerCacheKey就是将ApplicationEvent的泛型,里面存着ResolvableType。

实际上,我们上面分析到,调用multicastEvent方法时,会获取所有的ApplicationListener:

// org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
	Executor executor = getTaskExecutor();
	for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
		if (executor != null) {
			executor.execute(() -> invokeListener(listener, event));
		}
		else {
			invokeListener(listener, event);
		}
	}
}
// org.springframework.context.event.AbstractApplicationEventMulticaster#getApplicationListeners(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
protected Collection<ApplicationListener<?>> getApplicationListeners(
		ApplicationEvent event, ResolvableType eventType) {

	Object source = event.getSource();
	Class<?> sourceType = (source != null ? source.getClass() : null);
	ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

	// Quick check for existing entry on ConcurrentHashMap...
	ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
	if (retriever != null) {
		return retriever.getApplicationListeners(); // 一个类型关联多个监听器
	}

	if (this.beanClassLoader == null ||
			(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
					(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
		// Fully synchronized building and caching of a ListenerRetriever
		synchronized (this.retrievalMutex) {
			retriever = this.retrieverCache.get(cacheKey);
			if (retriever != null) {
				return retriever.getApplicationListeners();
			}
			retriever = new ListenerRetriever(true);
			Collection<ApplicationListener<?>> listeners =
					retrieveApplicationListeners(eventType, sourceType, retriever);
			this.retrieverCache.put(cacheKey, retriever);
			return listeners;
		}
	}
	else {
		// No ListenerRetriever caching -> no synchronization necessary
		return retrieveApplicationListeners(eventType, sourceType, null);
	}
}

在获取所有的Listener时,会首先通过泛型构造一个ListenerCacheKey,来确定查找监听指定类型的监听器,迭代这些监听器挨个进行通知。

这也是可以解释了,为什么可以单独监听指定事件的类型了:

context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        println("ApplicationListener - 接收到 Spring 事件:" + event);
    }
});

context.addApplicationListener(new ApplicationListener<ContextRefreshedEvent>() {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        println("ApplicationListener - 接收到 Spring 事件:" + event);
    }
});

基于注解的监听器如何注册

1.org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry) 注册所有相关注解的post processor到beanRegistry里面,其中就包括监 听器相关的EventListenerMethodProcessor

2.该类的这个方法org.springframework.context.event.EventListenerMethodProcessor#afterSingletonsInstantiated会在bean实例化的中被调用。

3.org.springframework.context.event.EventListenerMethodProcessor#processBean 最终在这个方法找到相关注解并不listener注册到ApplicationContext中

其实spring的很多功能都是通过扩展post processor来完成的。

三、扩展-Spring Boot 、Spring Cloud事件

1、Spring Boot 事件

事件类型

发生时机

ApplicationStartingEvent

当 Spring Boot 应用已启动时

ApplicationStartedEvent

当 Spring Boot 应用已启动时

ApplicationEnvironmentPreparedEvent

当 Spring Boot Environment 实例已准备时

ApplicationPreparedEvent

当 Spring Boot 应用预备时

ApplicationReadyEvent

当 Spring Boot 应用完全可用时

ApplicationFailedEvent

当 Spring Boot 应用启动失败时

实际上,Spring Boot事件基本上还是使用Spring事件那一套-ApplicationEvent。

也是用的ApplicationEventPublisher来发布的事件。

关于SpringBoot事件详细用法请移步:
Springboot启动之后立即执行某些方法可以怎么做?Springboot监听器,Springboot生命周期钩子函数总结大全

2、Spring Cloud 事件

事件类型

发生时机

EnvironmentChangeEvent

当 Environment 示例配置属性发生变化时

HeartbeatEvent

当 DiscoveryClient 客户端发送心跳时

InstancePreRegisteredEvent

当服务实例注册前

InstanceRegisteredEvent

当服务实例注册后

RefreshEvent

当 RefreshEndpoint 被调用时

RefreshScopeRefreshedEvent

当 Refresh Scope Bean 刷新后

Spring Cloud 事件和Spring事件也都差不多,Spring Cloud 事件会分到不同的功能模块里面。

参考资料

极客时间-《小马哥讲 Spring 核心编程思想》