说明
事件监听机制可以理解为是一种观察者模式,有数据发布者(事件源)和数据接受者(监听器);
在Java中,事件对象都是继承java.util.EventObject对象,事件监听器都是java.util.EventListener实例;
EventObject对象不提供默认构造器,需要外部传递source参数,即用于记录并跟踪事件的来源;
Spring事件
Spring事件对象为ApplicationEvent,继承EventObject,源码如下:
publicabstractclassApplicationEventextendsEventObject{ /** *CreateanewApplicationEvent. *@paramsourcetheobjectonwhichtheeventinitiallyoccurred(never{@codenull}) */ publicApplicationEvent(Objectsource){ super(source); this.timestamp=System.currentTimeMillis(); } }
Spring事件监听器为ApplicationListener,继承EventListener, 源码如下:
publicinterfaceApplicationListener<EextendsApplicationEvent>extendsEventListener{ voidonApplicationEvent(Evar1); }
实现Spring事件监听有两种方式:
- 面向接口编程,实现ApplicationListener接口;
- 基于注解驱动,@EventListener(Spring自定义的注解);
实例:
面向接口编程,实现ApplicationListener接口:
自定义事件对象:
publicclassMyApplicationEventextendsApplicationEvent{ publicMyApplicationEvent(Objectsource){ super(source); } }
自定义事件监听器:
publicclassMyApplicationListenerimplementsApplicationListener<MyApplicationEvent>{ @Override publicvoidonApplicationEvent(MyApplicationEventevent){ System.out.println("收到事件:"+event); } }
启动服务并发布事件:
publicclassApplicationEventBootstrap{ publicstaticvoidmain(String[]args){ AnnotationConfigApplicationContextcontext= newAnnotationConfigApplicationContext(); //注册自定义事件监听器 context.addApplicationListener(newMyApplicationListener()); //启动上下文 context.refresh(); //发布事件,事件源为Context context.publishEvent(newMyApplicationEvent(context)); //结束 context.close(); } }
运行结果:
收到事件:com.xx.MyApplicationEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@cb0ed20,startedonSatMay1616:32:04CST2020]
使用注解 @EventListener实现Spring事件监听:
@Component publicclassMyApplicationListener2{ @EventListener(MyApplicationEvent.class) publicvoidonEvent(MyApplicationEventevent){ System.out.println("收到事件:"+event); } }
启动并发布事件:
publicclassApplicationEventBootstrap{ publicstaticvoidmain(String[]args){ AnnotationConfigApplicationContextcontext= newAnnotationConfigApplicationContext(); //注册自定义事件监听器 context.register(MyApplicationListener2.class); //启动上下文 context.refresh(); //发布事件,事件源为Context context.publishEvent(newMyApplicationEvent(context)); //结束 context.close(); } }
运行结果:
收到事件:com.xx.MyApplicationEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@cb0ed20,startedonSatMay1616:32:04CST2020]
通过实例可以看出,上面两种方式都可正常发布和接收事件。
实现原理
通过上面实例可以看出,context 可以发布事件,那底层是怎么发布的,让我们继续看源码:
publicabstractclassAbstractApplicationContextextendsDefaultResourceLoader implementsConfigurableApplicationContext{ protectedvoidpublishEvent(Objectevent,@NullableResolvableTypeeventType){ ... getApplicationEventMulticaster().multicastEvent(applicationEvent,eventType); ... } }
通过源码我们可以看出,事件应该是通过
ApplicationEventMulticaster发布的,我们继续看:
publicclassSimpleApplicationEventMulticasterextendsAbstractApplicationEventMulticaster
Spring 中事件发布都是通过
SimpleApplicationEventMulticaster来实现的
publicvoidmulticastEvent(finalApplicationEventevent,@NullableResolvableTypeeventType){ ResolvableTypetype=(eventType!=null?eventType:resolveDefaultEventType(event)); for(finalApplicationListener<?>listener:getApplicationListeners(event,type)){ Executorexecutor=getTaskExecutor(); if(executor!=null){ //异步 executor.execute(()->invokeListener(listener,event)); } else{ invokeListener(listener,event); } } }
可以看出,如果设置了Executor则异步发送,否则同步;而且可以看出通过 resolveDefaultEventType(event) 对发布的事件类型进行了校验,这就是为什么我们可以直接使用泛型来指定我们想接收的事件对象, 比如上面的 ApplicationListener。
privatevoiddoInvokeListener(ApplicationListenerlistener,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(finalStringbeanName,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
从结果可以看出,事件对象类型和事件源,以及事件发布顺序。
转载请注明:IT运维空间 » 安全防护 » 深入浅出Spring/SpringBoot 事件监听机制
发表评论