image.png

说明

事件监听机制可以理解为是一种观察者模式,有数据发布者(事件源)和数据接受者(监听器);
在Java中,事件对象都是继承java.util.EventObject对象,事件监听器都是java.util.EventListener实例;
EventObject对象不提供默认构造器,需要外部传递source参数,即用于记录并跟踪事件的来源;

Spring事件

Spring事件对象为ApplicationEvent,继承EventObject,源码如下:

  1. public abstract class ApplicationEvent extends EventObject {
  2. /**
  3. * Create a new ApplicationEvent.
  4. * @param source the object on which the event initially occurred (never {@code null})
  5. */
  6. public ApplicationEvent(Object source) {
  7. super(source);
  8. this.timestamp = System.currentTimeMillis();
  9. }
  10. }

Spring事件监听器为ApplicationListener,继承EventListener, 源码如下:

  1. public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
  2. void onApplicationEvent(E var1);
  3. }

实现Spring事件监听有两种方式

  1. 面向接口编程,实现ApplicationListener接口;
  2. 基于注解驱动,@EventListener(Spring自定义的注解);

实例:

  1. 面向接口编程,实现ApplicationListener接口:

自定义事件对象:

  1. public class MyApplicationEvent extends ApplicationEvent {
  2. public MyApplicationEvent(Object source) {
  3. super(source);
  4. }
  5. }

自定义事件监听器:

  1. public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {
  2. @Override
  3. public void onApplicationEvent(MyApplicationEvent event) {
  4. System.out.println("收到事件:" + event);
  5. }
  6. }

启动服务并发布事件:

  1. public class ApplicationEventBootstrap {
  2. public static void main(String[] args) {
  3. AnnotationConfigApplicationContext context =
  4. new AnnotationConfigApplicationContext();
  5. // 注册自定义事件监听器
  6. context.addApplicationListener(new MyApplicationListener());
  7. // 启动上下文
  8. context.refresh();
  9. // 发布事件,事件源为Context
  10. context.publishEvent(new MyApplicationEvent(context));
  11. // 结束
  12. context.close();
  13. }
  14. }

运行结果: 收到事件:com.xx.MyApplicationEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@cb0ed20, started on Sat May 16 16:32:04 CST 2020] 这里也可以直接在MyApplicationListener类上加上@Component注解,纳入spring容器管理

  1. package com.zhihao.miao;
  2. import org.springframework.context.ApplicationListener;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {
  6. public void onApplicationEvent(MyApplicationEvent event) {
  7. System.out.println("接收到事件:"+event.getClass());
  8. }
  9. }
  1. 使用注解 @EventListener实现Spring事件监听:

    1. @Component
    2. public class MyApplicationListener2 {
    3. @EventListener(MyApplicationEvent.class)
    4. public void onEvent(MyApplicationEvent event) {
    5. System.out.println("收到事件:" + event);
    6. }
    7. }

    启动并发布事件:

    1. public class ApplicationEventBootstrap {
    2. public static void main(String[] args) {
    3. AnnotationConfigApplicationContext context =
    4. new AnnotationConfigApplicationContext();
    5. // 注册自定义事件监听器
    6. context.register(MyApplicationListener2.class);
    7. // 启动上下文
    8. context.refresh();
    9. // 发布事件,事件源为Context
    10. context.publishEvent(new MyApplicationEvent(context));
    11. // 结束
    12. context.close();
    13. }
    14. }

    运行结果: 收到事件:com.xx.MyApplicationEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@cb0ed20, started on Sat May 16 16:32:04 CST 2020] 通过实例可以看出,上面两种方式都可正常发布和接收事件。

    实现原理

    通过上面实例可以看出,context 可以发布事件,那底层是怎么发布的,让我们继续看源码:

    1. public abstract class AbstractApplicationContext extends DefaultResourceLoader
    2. implements ConfigurableApplicationContext {
    3. protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    4. ...
    5. getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    6. ...
    7. }
    8. }

    通过源码我们可以看出,事件应该是通过ApplicationEventMulticaster发布的,我们继续看:

    1. public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster

    Spring 中事件发布都是通过SimpleApplicationEventMulticaster来实现的

    1. public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    2. ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    3. for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
    4. Executor executor = getTaskExecutor();
    5. if (executor != null) {
    6. // 异步
    7. executor.execute(() -> invokeListener(listener, event));
    8. }
    9. else {
    10. invokeListener(listener, event);
    11. }
    12. }
    13. }

    可以看出,如果设置了Executor则异步发送,否则同步;而且可以看出通过 resolveDefaultEventType(event) 对发布的事件类型进行了校验,这就是为什么我们可以直接使用泛型来指定我们想接收的事件对象, 比如上面的ApplicationListener

    1. private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    2. try {
    3. listener.onApplicationEvent(event);

    最后就使用对应的ApplicationListener进行接收和处理就行了,那么ApplicationListener是什么时候注册的呢?
    如何添加ApplicationListener?

  2. 直接添加,使用content.addApplicationListener(上面实例中有使用);

  3. 将自定义的ApplicationListener注册为一个Bean,Spring再初始化Bean之后会添加,具体代码在ApplicationListenerDetector#postProcessAfterInitialization,判断一个Bean如果是ApplicationListener,则也是使用context.addApplicationListener添加;
  4. 使用注解@EventListener,在初始化Bean之后,会在EventListenerMethodProcessor中进行处理和添加;

第三种实现的源码如下(EventListenerMethodProcessor中):

  1. private void processBean(final String beanName, final Class<?> targetType) {
  2. ....
  3. // 获取public 且有@EventListener的方法
  4. AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
  5. ...
  6. ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse);
  7. // 添加监听器
  8. context.addApplicationListener(applicationListener);
  9. }

Spring内建事件

  • ContextRefreshedEvent: Spring应用上下文就绪事件;
  • ContextStartedEvent: Spring应用上下文启动事件;
  • ContextStopedEvent: Spring应用上下文停止事件;
  • ContextClosedEvent: Spring应用上下文关闭事件;

    Spring Boot事件

    Spring Boot事件是在Spring事件基础上进行的封装
    1. public abstract class SpringApplicationEvent extends ApplicationEvent
    事件对象改为SpringApplicationEvent,事件源为SpringApplication(Spring事件源为Context);
    底层发布事件还是使用
    SimpleApplicationEventMulticaster 对象,不过有点需要说明的是,Spring Boot 1.4开始,SpringApplication和ApplicationContext使用的都是
    SimpleApplicationEventMulticaster实例,但是两者属于不同的对象(1.0 ~ 1.3版本是同一个对象);

    事件回顾:

    1. public class EventBootstrap {
    2. public static void main(String[] args) {
    3. new SpringApplicationBuilder(Object.class)
    4. .listeners(event -> {
    5. System.out.println("事件对象:"
    6. + event.getClass().getSimpleName()
    7. + " ,事件源:" + event.getSource().getClass().getSimpleName());
    8. })
    9. .web(WebApplicationType.NONE)
    10. .run(args)
    11. .close();
    12. }
    13. }
    运行结果:
    1. 事件对象:ApplicationContextInitializedEvent ,事件源:SpringApplication
    2. 事件对象:ApplicationPreparedEvent ,事件源:SpringApplication
    3. 事件对象:ContextRefreshedEvent ,事件源:AnnotationConfigApplicationContext
    4. 事件对象:ApplicationStartedEvent ,事件源:SpringApplication
    5. 事件对象:ApplicationReadyEvent ,事件源:SpringApplication
    6. 事件对象:ContextClosedEvent ,事件源:AnnotationConfigApplicationContext
    从结果可以看出,事件对象类型和事件源,以及事件发布顺序。