美文网首页Java
深入浅出Spring/SpringBoot 事件监听机制

深入浅出Spring/SpringBoot 事件监听机制

作者: 一只程序猿哟 | 来源:发表于2022-01-07 14:30 被阅读0次

    监听器模型

    说明

    事件监听机制可以理解为是一种观察者模式,有数据发布者(事件源)和数据接受者(监听器);

    在Java中,事件对象都是继承java.util.EventObject对象,事件监听器都是java.util.EventListener实例;

    EventObject对象不提供默认构造器,需要外部传递source参数,即用于记录并跟踪事件的来源;

    Spring事件

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

    publicabstractclassApplicationEventextendsEventObject{/** * Create a new ApplicationEvent. *@paramsource the object on which the event initially occurred (never {@codenull}) */publicApplicationEvent(Object source){super(source);this.timestamp = System.currentTimeMillis();}}

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

    publicinterfaceApplicationListenerextendsEventListener{voidonApplicationEvent(E var1);}

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

    面向接口编程,实现ApplicationListener接口;

    基于注解驱动,@EventListener(Spring自定义的注解);

    实例:

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

    自定义事件对象:

    publicclassMyApplicationEventextendsApplicationEvent{publicMyApplicationEvent(Object source){super(source);    }}

    自定义事件监听器:

    publicclassMyApplicationListenerimplementsApplicationListener{@OverridepublicvoidonApplicationEvent(MyApplicationEvent event){        System.out.println("收到事件:"+ event);    }}

    启动服务并发布事件:

    publicclassApplicationEventBootstrap{publicstaticvoidmain(String[] args){        AnnotationConfigApplicationContext context =newAnnotationConfigApplicationContext();// 注册自定义事件监听器context.addApplicationListener(newMyApplicationListener());// 启动上下文context.refresh();// 发布事件,事件源为Contextcontext.publishEvent(newMyApplicationEvent(context));// 结束context.close();    }}

    运行结果:

    收到事件:com.xx.MyApplicationEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@cb0ed20, started on Sat May 16 16:32:04 CST 2020]

    使用注解 @EventListener实现Spring事件监听:

    @ComponentpublicclassMyApplicationListener2{@EventListener(MyApplicationEvent.class)publicvoid onEvent(MyApplicationEvent event) {        System.out.println("收到事件:"+ event);    }}

    启动并发布事件:

    publicclassApplicationEventBootstrap{publicstaticvoidmain(String[] args){        AnnotationConfigApplicationContext context =newAnnotationConfigApplicationContext();// 注册自定义事件监听器context.register(MyApplicationListener2.class);// 启动上下文context.refresh();// 发布事件,事件源为Contextcontext.publishEvent(newMyApplicationEvent(context));// 结束context.close();    }}

    运行结果:

    收到事件:com.xx.MyApplicationEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@cb0ed20, started on Sat May 16 16:32:04 CST 2020]

    通过实例可以看出,上面两种方式都可正常发布和接收事件。

    实现原理

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

    publicabstractclassAbstractApplicationContextextendsDefaultResourceLoaderimplementsConfigurableApplicationContext{protectedvoidpublishEvent(Object event,@NullableResolvableType eventType) {        ...getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);        ...      }}

    通过源码我们可以看出,事件应该是通过

    ApplicationEventMulticaster发布的,我们继续看:

    publicclassSimpleApplicationEventMulticasterextendsAbstractApplicationEventMulticaster

    Spring 中事件发布都是通过

    SimpleApplicationEventMulticaster来实现的

    publicvoidmulticastEvent(final ApplicationEventevent, @Nullable ResolvableType eventType){ResolvableType type = (eventType !=null? eventType : resolveDefaultEventType(event));for(final ApplicationListener listener : getApplicationListeners(event, type)) {Executor executor = getTaskExecutor();if(executor !=null) {// 异步executor.execute(() -> invokeListener(listener,event));}else{invokeListener(listener,event);}}}

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

    privatevoiddoInvokeListener(ApplicationListener listener, ApplicationEventevent){try{listener.onApplicationEvent(event);

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

    如何添加ApplicationListener?

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

    将自定义的ApplicationListener注册为一个Bean,Spring再初始化Bean之后会添加,具体代码在ApplicationListenerDetector#postProcessAfterInitialization,判断一个Bean如果是ApplicationListener,则也是使用context.addApplicationListener添加;

    使用注解@EventListener,在初始化Bean之后,会在EventListenerMethodProcessor中进行处理和添加;

    第三种实现的源码如下(

    EventListenerMethodProcessor中):

    privatevoidprocessBean(finalString beanName,finalClass targetType){  ....// 获取public 且有@EventListener的方法 AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));  ...  ApplicationListener applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse);// 添加监听器context.addApplicationListener(applicationListener); }

    Spring内建事件

    ContextRefreshedEvent: Spring应用上下文就绪事件;

    ContextStartedEvent: Spring应用上下文启动事件;

    ContextStopedEvent: Spring应用上下文停止事件;

    ContextClosedEvent: Spring应用上下文关闭事件;

    Spring Boot事件

    Spring Boot事件是在Spring事件基础上进行的封装

    publicabstractclassSpringApplicationEventextendsApplicationEvent

    事件对象改为SpringApplicationEvent,事件源为SpringApplication(Spring事件源为Context);

    底层发布事件还是使用

    SimpleApplicationEventMulticaster 对象,不过有点需要说明的是,Spring Boot 1.4开始,SpringApplication和ApplicationContext使用的都是SimpleApplicationEventMulticaster实例,但是两者属于不同的对象(1.0 ~ 1.3版本是同一个对象);

    事件回顾:

    publicclassEventBootstrap{publicstaticvoidmain(String[] args){newSpringApplicationBuilder(Object.class)                .listeners(event->{                    System.out.println("事件对象:"+ event.getClass().getSimpleName()                    +" ,事件源:"+ event.getSource().getClass().getSimpleName());                })                .web(WebApplicationType.NONE)                .run(args)                .close();    }}

    运行结果:

    事件对象:ApplicationContextInitializedEvent ,事件源:SpringApplication

    事件对象:ApplicationPreparedEvent ,事件源:SpringApplication

    事件对象:ContextRefreshedEvent ,事件源:AnnotationConfigApplicationContext

    事件对象:ApplicationStartedEvent ,事件源:SpringApplication

    事件对象:ApplicationReadyEvent ,事件源:SpringApplication

    事件对象:ContextClosedEvent ,事件源:AnnotationConfigApplicationContext

    从结果可以看出,事件对象类型和事件源,以及事件发布顺序。

    相关文章

      网友评论

        本文标题:深入浅出Spring/SpringBoot 事件监听机制

        本文链接:https://www.haomeiwen.com/subject/qzxdcrtx.html