黑客24小时在线接单网站

黑客24小时在线接单网站,黑客接单,接单网站,黑客入口

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

说明

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

在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
  • 从结果可以看出,事件对象类型和事件源,以及事件发布顺序。

    • 评论列表:
    •  夙世征棹
       发布于 2022-05-28 09:30:15  回复该评论
    • ...getApplicationEventMulticaster().multicastEvent(applicationEvent,eventType);...}}通过源码我们可以看出,事件应该是通过Applicati
    •  断渊南忆
       发布于 2022-05-28 08:34:44  回复该评论
    • ntext事件对象:ApplicationStartedEvent,事件源:SpringApplication事件对象:ApplicationReadyEvent,事件源:SpringApplication事件对象:ContextClosedEvent,事件源:Annot

    发表评论:

    «    2024年8月    »
    1234
    567891011
    12131415161718
    19202122232425
    262728293031
    文章归档
    标签列表

    Powered By

    Copyright Your WebSite.Some Rights Reserved.