Spring Boot(六)自定义事件及监听

事件及监听并不是SpringBoot的新功能,Spring框架早已提供了完善的事件监听机制,在Spring框架中实现事件监听的流程如下:

  1. 自定义事件,继承org.springframework.context.ApplicationEvent抽象类

  2. 定义事件监听器,实现org.springframework.context.ApplicationListener接口

  3. 在Spring容器中发布事件

实现自定义事件及监听

  • 定义事件
//自定义事件
public class ApplicationEventTest extends ApplicationEvent {
    public ApplicationEventTest(Object source) {
        super(source);
    }
    /**
     * 事件处理事项
     * @param msg
     */
    public void printMsg(String msg)
    {
        System.out.println("监听到事件:"+ApplicationEventTest.class);
    }
}
  • 定义监听器
//自定义事件监听器
//@Component
public class ApplicationListenerTest implements ApplicationListener<ApplicationEventTest> {
    @Override
    public void onApplicationEvent(ApplicationEventTest event) {
        event.printMsg(null);
    }
}
  • 在Spring容器中发布事件
public static void main(String[] args) {
   SpringApplication application = new SpringApplication(SpringbootdemoApplication.class);
   //需要把监听器加入到spring容器中
   application.addListeners(new ApplicationListenerTest());
   Set<ApplicationListener<?>> listeners = application.getListeners();
   ConfigurableApplicationContext context =  application.run(args);
   //发布事件
   context.publishEvent(new ApplicationEventTest(new Object()));
   context.close();
}

上面的示例是在SpringBoot应用中简单的测试一下。

实际开发中实现监听还有其他的方式,在Spring框架中提供了两种事件监听的方式:

  1. 编程式:通过实现ApplicationListener接口来监听指定类型的事件

  2. 注解式:通过在方法上加@EventListener注解的方式监听指定参数类型的事件,写该类需要托管到Spring容器中

 在SpringBoot应用中还可以通过配置的方式实现监听:

   3. 通过application.properties中配置context.listener.classes属性指定监听器

下面分别分析一下这三种监听方式

编程式实现监听

实现ApplicationListenser接口:

@Component
public class ApplicationListenerTest implements ApplicationListener<ApplicationEventTest> {
    @Override
    public void onApplicationEvent(ApplicationEventTest event) {
        event.printMsg(null);
    }
}

控制台输出测试:

public static void main(String[] args) {
   SpringApplication application = new SpringApplication(SpringbootdemoApplication.class);
   //需要把监听器加入到spring容器中
   //application.addListeners(new ApplicationListenerTest());
   //Set<ApplicationListener<?>> listeners = application.getListeners();
   ConfigurableApplicationContext context =  application.run(args);
   //发布事件
   context.publishEvent(new ApplicationEventTest(new Object()));
}

那么我们跟踪一下源码,看一下事件是如何发布出去的,又是如何被监听到的呢?

AbstractApplicationContext.java中截取部分代码

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
   Assert.notNull(event, "Event must not be null");
   if (logger.isTraceEnabled()) {
      logger.trace("Publishing event in " + getDisplayName() + ": " + event);
   }

   // Decorate event as an ApplicationEvent if necessary
  //将object转成ApplicationEvent
   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 {
    // SimpleApplicationEventMulticaster 获取事件发布器,发布事件
      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);
      }
   }
}

查看一下ApplicationContext类结构图可以发现:应用上下文AbstractApplicationContext实际还是通过继承ApplicationEventPublisher接口,实现了其中的事件发布的方法,使得Spring应用上下文有了发布事件的功能,在AbstractApplicationContext内部通过SimpleApplicationEventMulticaster事件发布类,将具体事件ApplicationEvent发布出去。

那么事件发布出去后又是如何被监听到的呢?下面看一下具Spring中负责处理事件发布类SimpleApplicationEventMulticaster 中multicastEvent方法具体实现过程

SimpleApplicationEventMulticaster.java部分代码,实际尝试将当前事件逐个广播到指定类型的监听器中(listeners已经根据当前事件类型过滤了)

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    // getApplicationListeners(event, type) 筛选监听器,在context.publish(ApplicationEvent event)中已经将事件传入,getApplicationListeners中将可以根据这个event类型从Spring容器中检索出符合条件的监听器

   for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      Executor executor = getTaskExecutor();
      if (executor != null) {
         executor.execute(() -> invokeListener(listener, event));
      }
      else {
    //尝试逐个向监听器广播
         invokeListener(listener, event);
      }
   }
}

@EventListener注解方式实现

 定义注解方法

@Component
public class MyEventHandleTest {
    /**
     * 参数为Object类型时,所有事件都会监听到
     * 参数为指定类型事件时,该参数类型事件或者其子事件(子类)都可以接收到
     */
    @EventListener
    public void event(ApplicationEventTest event){
        event.printMsg(null);
    }

}

实现过程分析:

@EventListener注解主要通过EventListenerMethodProcessor扫描出所有带有@EventListener注解的方法,然后动态构造事件监听器,并将监听器托管到Spring应用上文中。

protected void processBean(
      final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) {

   if (!this.nonAnnotatedClasses.contains(targetType)) {
      Map<Method, EventListener> annotatedMethods = null;
      try {
        //查找含有@EventListener注解的所有方法
         annotatedMethods = MethodIntrospector.selectMethods(targetType,
               (MethodIntrospector.MetadataLookup<EventListener>) method ->
                     AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
      }
      catch (Throwable ex) {
         // An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
         if (logger.isDebugEnabled()) {
            logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
         }
      }
      if (CollectionUtils.isEmpty(annotatedMethods)) {
         this.nonAnnotatedClasses.add(targetType);
         if (logger.isTraceEnabled()) {
            logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
         }
      }
      else {
         // Non-empty set of methods
         ConfigurableApplicationContext context = getApplicationContext();
    //遍历含有@EventListener注解的方法
         for (Method method : annotatedMethods.keySet()) {
            for (EventListenerFactory factory : factories) {
               if (factory.supportsMethod(method)) {
                  Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
          //动态构造相对应的事件监听器
                  ApplicationListener<?> applicationListener =
                        factory.createApplicationListener(beanName, targetType, methodToUse);
                  if (applicationListener instanceof ApplicationListenerMethodAdapter) {
                     ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
                  }
          //将监听器添加的Spring应用上下文中托管
                  context.addApplicationListener(applicationListener);
                  break;
               }
            }
         }
         if (logger.isDebugEnabled()) {
            logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
                  beanName + "': " + annotatedMethods);
         }
      }
   }
}

在application.properties中配置context.listener.classes

添加如下配置:

context.listener.classes=com.sl.springbootdemo.Listeners.ApplicationListenerTest

查看一下DelegatingApplicationListener类中实现逻辑:

public class DelegatingApplicationListener
      implements ApplicationListener<ApplicationEvent>, Ordered {
   private static final String PROPERTY_NAME = "context.listener.classes";
   private int order = 0;
   //Spring framework提供的负责处理发布事件的类,前面说的Spring应用上下文中也是通过这个类发布事件的
   private SimpleApplicationEventMulticaster multicaster;
   @Override
   public void onApplicationEvent(ApplicationEvent event) {
      if (event instanceof ApplicationEnvironmentPreparedEvent) {
        // getListeners内部实现读取context.listener.classes配置的监听器
         List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
               ((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
         if (delegates.isEmpty()) {
            return;
         }
         this.multicaster = new SimpleApplicationEventMulticaster();
         for (ApplicationListener<ApplicationEvent> listener : delegates) {
            this.multicaster.addApplicationListener(listener);
         }
      }
    //发布事件
      if (this.multicaster != null) {
         this.multicaster.multicastEvent(event);
      }
   }

 Spring-boot-{version}.jar包中提供一个类DelegatingApplicationListener,该类的作用是从application.properties中读取配置context.listener.classes,并将事件广播给这些配置的监听器。通过前面一章对SpringBoot启动流程分析,我们已经了解到SpringBoot启动时会从META-INF/spring.factories中读取key为org.springframework.context.ApplicationListener的所有监听器。DelegatingApplicationListener的功能可以让我们不需要创建META-INF/spring.factories,直接在application.properties中配置即可。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仰望星空@脚踏实地

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值