spring的ApplicationEvent事件监听机制原理与应用

spring的ApplicationEvent事件通知机制原理与应用

spring框架抽象出了一套事件机制,通过发布,订阅方式来实现,类似观察者模式。先来搭建一个基本的spring环境,然后做个简单的案例演示事件的应用,最后在做源码分析。

1.环境搭建

引入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

创建一个配置类和启动类

@Configuration
public class AppConfig {

}

public class TestSpring {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    }
}

简单的环境搭建完毕。

2.案例演示

熟悉发布-订阅模型都清楚,整个模型肯定要包括3种角色,发布者,订阅者(也叫监听者),事件。这三个角色有什么作用呢?

  • 发布者:触发事件的角色,触发以后,所有监听,也叫订阅该事件的监听者都会收到通知,发布者不需要知道有多少个监听者存在。

  • 订阅者:监听某个事件的发生,一旦发生事件,执行相应处理逻辑。

  • 事件:代表事件的实体,这个事件实体通常会包含某些信息,发布者将信息包装到事件内,订阅者可以获取该信息,达到传递信息的作用

spring本身定义了一个发布事件的方法,可以看成是发布者。

订阅者在spring容器内叫做ApplicationListener,是一个泛型接口,用户可以自己定义某一事件的订阅者,注册到容器中。

spring内部定义了一个ApplicationEvent抽象类,所有spring管理的事件都实现了ApplicationEvent类,他的一个子类ApplicationContextEvent也是一个抽象类,spring又定义了几个ApplicationContextEvent的子类,用于代表spring容器生命周期的典型事件,包括ContextStartedEvent,ContextRefreshedEvent,ContextStoppedEvent,ContextClosedEvent,发布这些容器生命周期事件的方法嵌入在容器的整个生命周期中。spring的实现原来在后面的原理分析中详细讲解。

发布者,订阅者spring已经有自己的实现机制,这里我们自定义一个实现ApplicationEvent的事件类,用于测试。

public class MyEvent extends ApplicationEvent {
    public MyEvent(Object source) {
        super(source);
    }
}

spring的事件机制有两种使用方式,分别是ApplicationListener接口方式基于EventListener注解方式

2.1 实现ApplicationListener接口方式

ApplicationListener是一个泛型接口,我们实现该接口,定义一个监听器。

@Component
public class MyApplicationListener implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println("MyEvent事件发生");
        System.out.println("收到信息:" + event.getSource());
    }
}

在TestSpring启动类的main方法中加入下面两行代码

MyEvent myEvent = new MyEvent(context);
context.publishEvent(myEvent);

启动TestSpring类的main方法,显示结果

MyEvent事件发生
收到信息:org.springframework.context.annotation.AnnotationConfigApplicationContext@2e0fa5d3, started on Thu Feb 04 09:47:23 CST 2021
2.2 EventListener注解方式

创建一个测试类AnnotationEventTest,类中定义一个方法,注意方法必须有形参,不然会报错,方法添加EventListener注解,这就相当于监听了MyEvent事件的处理方法。

@Component
public class AnnotationEventTest {

    @EventListener
    public void test(MyEvent event)
    {
        System.out.println("事件测试:"+event);
    }
}

启动TestSpring类的main方法,显示结果:

事件测试:com.li.study.event.MyEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e0fa5d3, started on Fri Feb 05 08:22:11 CST 2021]

这种基于EventListener注解的方式,省掉了定义监听器类,用起来更方便。

很多人会有疑问,spring为什么会做这种事件通知机制?我理解主要为了解耦,事件的发布者不需要知道有多少监听者在监听该事件的发生。有人说这种功能也可以通过依赖注入的方式实现,把监听逻辑的实现类注入到发布事件的对象中,直接通过方法调用即可。我只能说,呃。。。你说的对,太郭敬明了。

3.原理分析

其实这个实现原理大家仔细一想,也很简单,无非就是spring自动识别所有的事件监听器,不管是实现ApplicationListener还是添加EventListener注解的方法,将所有的监听器注入到容器,每个监听器绑定了一个事件类型,ApplicationListener通过泛型的参数类型绑定,EventListener注解通过方法形参绑定。当容器触发事件时,遍历所有监听器,找到类型匹配的事件监听器,执行监听方法。

下面就从源码角度来分析spring的实现方式,分为两个主要步骤:事件监听器的自动注册和发布事件

3.1 事件监听器的注册原理

前面也提到,spring的事件监听器有两种形式,实现ApplicationListener接口和标记EventListener注解的方法。

3.1.1 实现ApplicationListener接口的事件监听器发现

自动注册ApplicationListener接口的实现类,实现原理是BeanPostProcessor后置处理器,后置处理器的实现原理参考BeanPostProcessor后置处理器原理与应用

限于篇幅,这里不详细说明。在上面的案例调用AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);时,会启动该并刷新容器,会执行AbstractApplicationContext类的refresh() 方法,在refresh() 方法中会执行很多复杂的逻辑,这个方法时spring的合新方法。其中有一行代码prepareBeanFactory(beanFactory);我们查看一下,在代码中有一行 beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));就是向spring容器中注册了一个发现事件监听器的BeanPostProcessor。

//省略无关代码
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    
   // Register early post-processor for detecting inner beans as ApplicationListeners.
   beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

}

我们看一下ApplicationListenerDetector的postProcessMergedBeanDefinition和postProcessAfterInitialization方法。为什么只关心这两个方法,这就和BeanPostProcessor的原理有关。

  • postProcessMergedBeanDefinition方法在MergedBeanDefinitionPostProcessor接口中定义,MergedBeanDefinitionPostProcessor是BeanPostProcessor的一个子接口,postProcessMergedBeanDefinition方法会在bean创建之后,初始化之前调用的。

  • postProcessAfterInitialization方法在BeanPostProcessor接口中定义,通过也可以推断,该方法是在bean初始化之后被调用的。

所以,在ApplicationListenerDetector类中,postProcessMergedBeanDefinition方法会在postProcessAfterInitialization方法之前被调用。

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    if (ApplicationListener.class.isAssignableFrom(beanType)) {
        this.singletonNames.put(beanName, beanDefinition.isSingleton());
    }
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
   if (bean instanceof ApplicationListener) {
      // potentially not detected as a listener by getBeanNamesForType retrieval
      Boolean flag = this.singletonNames.get(beanName);
      if (Boolean.TRUE.equals(flag)) {
         // singleton bean (top-level or inner): register on the fly
         this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
      }
      else if (Boolean.FALSE.equals(flag)) {
         if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
            // inner bean with other scope - can't reliably process events
            logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
                  "but is not reachable for event multicasting by its containing ApplicationContext " +
                  "because it does not have singleton scope. Only top-level listener beans are allowed " +
                  "to be of non-singleton scope.");
         }
         this.singletonNames.remove(beanName);
      }
   }
   return bean;
}

在postProcessMergedBeanDefinition方法中判断bean类型是不是ApplicationListener的实现类,如果是加入到map中。在postProcessAfterInitialization方法中,通过beanName在map中将ApplicationListener的实现类取出,注册到容器的applicationListeners集合中,准备将来被调用。

3.1.2 EventListener注解

EventListener注解只能标记再方法和注解上,不允许标记在类上。原理就是通过注解指定的方法,动态代理出一个ApplicationListener的实现类,来看一下具体实现。

首先我们来看一下,spring框架是怎么识别EventListener和怎么动态代理的。还是从AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);开始分析,走下面的调用链路。

AnnotationConfigApplicationContext#AnnotationConfigApplicationContext() //无参构造函数

​ AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry)

​ AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment)

​ AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry registry)

​ AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source)

来看一下registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source)方法

//省略无关代码
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
      BeanDefinitionRegistry registry, @Nullable Object source) {

   if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
   }

   if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
   }

   return beanDefs;
}

这里注册了两个bean,EventListenerMethodProcessor和DefaultEventListenerFactory,就是这两个bean实现了基于EventListener注解的事件机制。

看一下EventListenerMethodProcessor,他实现了BeanFactoryPostProcessor接口,是一个bean工厂的后置处理器,原理见BeanFactoryPostProcessor与BeanDefinitionRegistryPostProcessor用法与原理

我们关注postProcessBeanFactory和afterSingletonsInstantiated两个方法

  • postProcessBeanFactory在BeanFactoryPostProcessor接口定义,在容器将所有bean解析成beanDefinition后,实例化之前执行

  • afterSingletonsInstantiated在SmartInitializingSingleton接口中定义,SmartInitializingSingleton接口其实也是一种扩展机制,类似InitializingBean接口,不同之处在于SmartInitializingSingleton中的afterSingletonsInstantiated方法在bean实例化以后调用,InitializingBean的afterPropertiesSet在bean初始化时调用。

这里postProcessBeanFactory在afterSingletonsInstantiated之前调用。

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   this.beanFactory = beanFactory;

   Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
   List<EventListenerFactory> factories = new ArrayList<>(beans.values());
   AnnotationAwareOrderComparator.sort(factories);
   this.eventListenerFactories = factories;
}


@Override
public void afterSingletonsInstantiated() {
   ConfigurableListableBeanFactory beanFactory = this.beanFactory;
   Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
   String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
   for (String beanName : beanNames) {
      if (!ScopedProxyUtils.isScopedTarget(beanName)) {
         Class<?> type = null;
         try {
            type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
         }
         catch (Throwable ex) {
            // An unresolvable bean type, probably from a lazy bean - let's ignore it.
            if (logger.isDebugEnabled()) {
               logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
            }
         }
         if (type != null) {
            if (ScopedObject.class.isAssignableFrom(type)) {
               try {
                  Class<?> targetClass = AutoProxyUtils.determineTargetClass(
                        beanFactory, ScopedProxyUtils.getTargetBeanName(beanName));
                  if (targetClass != null) {
                     type = targetClass;
                  }
               }
               catch (Throwable ex) {
                  // An invalid scoped proxy arrangement - let's ignore it.
                  if (logger.isDebugEnabled()) {
                     logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex);
                  }
               }
            }
            try {
               processBean(beanName, type);
            }
            catch (Throwable ex) {
               throw new BeanInitializationException("Failed to process @EventListener " +
                     "annotation on bean with name '" + beanName + "'", ex);
            }
         }
      }
   }
}

postProcessBeanFactory方法先获取所有实现EventListenerFactory接口的单例对象,排序后保存在容器中。

afterSingletonsInstantiated方法执行一系列的判断,然后真正的逻辑在processBean(beanName, type);中实现

private void processBean(final String beanName, final Class<?> targetType) {
   //判断类型包含注解,并且是EventListener注解
   if (!this.nonAnnotatedClasses.contains(targetType) &&
         AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
         !isSpringContainerClass(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);
         }
      }

      //没有添加找到添加EventListener注解的方法对象添加到nonAnnotatedClasses集合,这里为了上面的判断使用
      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 = this.applicationContext;
         Assert.state(context != null, "No ApplicationContext set");
         //这里的eventListenerFactories实际上在容器启动时添加了一个默认的监听器工厂DefaultEventListenerFactory
         List<EventListenerFactory> factories = this.eventListenerFactories;  
         Assert.state(factories != null, "EventListenerFactory List not initialized");
         for (Method method : annotatedMethods.keySet()) {
            for (EventListenerFactory factory : factories) {  //遍历工厂,其实只有一个
         
               if (factory.supportsMethod(method)) {  //判断是否支持方法的注解方式,这里是DefaultEventListenerFactory的逻辑,返回的是true
                  Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));//获取方法对象
                  //后面的这段逻辑就是通过这个方法,构建出一个ApplicationListener的实例对象,然后加入到容器中
                  ApplicationListener<?> applicationListener =
                        factory.createApplicationListener(beanName, targetType, methodToUse);
                  if (applicationListener instanceof ApplicationListenerMethodAdapter) {
                     ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
                  }
                  context.addApplicationListener(applicationListener);
                  break;
               }
            }
         }
         if (logger.isDebugEnabled()) {
            logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
                  beanName + "': " + annotatedMethods);
         }
      }
   }
}

至此事件监听器的注册原理就输说完了,还希望大家理解,其实这些理解清楚了,整个spring的原理也比较清楚了。

3.2 事件发布原理

相比于事件监听器原理,事件发布原理简单得多,就是直接调用事件监听器的处理方法。

通过上面案例演示的context.publishEvent(myEvent);代码开始分析。会进入到AbstractApplicationContext类的下面方法。

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
   Assert.notNull(event, "Event must not be null");

   // Decorate event as an ApplicationEvent if necessary
   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 {
      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);
      }
   }
}

这里主要是一些判断逻辑,我们关注getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);这里又事件广播器的概念,事件发布就是在事件广播器实现的。获取的事件广播器是在AbstractApplicationContext类的refresh()方法中调用initApplicationEventMulticaster();方法注册的,注册的类是SimpleApplicationEventMulticaster,这个方法的逻辑很简单,就不分析了。就是让大家知道时间广播器怎么来的。我们去看一下事件广播器的multicastEvent方法。

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   //解析事件类型
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   Executor executor = getTaskExecutor();
   //在容器中获取该类型得事件监听器,调用监听器得处理方法,支持异步调用
   for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      if (executor != null) {
         executor.execute(() -> invokeListener(listener, event));
      }
      else {
         invokeListener(listener, event);
      }
   }
}

具体得调用逻辑在invokeListener方法

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
   //有没有异常处理,代码逻辑不一样
   ErrorHandler errorHandler = getErrorHandler();
   if (errorHandler != null) {
      try {
         doInvokeListener(listener, event);
      }
      catch (Throwable err) {
         errorHandler.handleError(err);
      }
   }
   else {
      doInvokeListener(listener, event);
   }
}

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
   try {
      listener.onApplicationEvent(event);  //调用监听器得处理方法,完成事件发布
   }
   catch (ClassCastException ex) {
      String msg = ex.getMessage();
      if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
         // Possibly a lambda-defined listener which we could not resolve the generic event type for
         // -> let's suppress the exception and just log a debug message.
         Log logger = LogFactory.getLog(getClass());
         if (logger.isTraceEnabled()) {
            logger.trace("Non-matching event type for listener: " + listener, ex);
         }
      }
      else {
         throw ex;
      }
   }
}
总结

以上就是spring事件通知机制的全部内容,实现的逻辑比较清晰,但是涉及的spring源码相关的知识比较多,这些都理解清楚了,spring框架的基本功能就了解一半以上了。本人能力有限,有错误之处希望大佬指出。

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值