EventListener注解原理

两种监听方式

在Spring中,如果要进行事件监听有两种方式:一种是实现ApplicationListener接口,另一种就是在方法上加@EventListener注解。

java 复制代码
@Component
public class MyListener implements ApplicationListener<ApplicationEvent> {

    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        System.out.println("I got it!");
    }
}
java 复制代码
@Component
public class MethodListener {

    @EventListener(classes = ApplicationEvent.class)
    public void listen() {
        System.out.println("I got it!");
    }
}

EventListenerMethodProcessor

那么原理是什么呢? 查看EventListener注解源码,阅读一番后,我们注意到了一个关键类:EventListenerMethodProcessor。看一下源码:

java 复制代码
public class EventListenerMethodProcessor
      implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
    //...
}

通过注释我们可以得知,EventListenerMethodProcessor类的目的就是为了把EventListener注解方法注册成ApplicationListener实例。 除去我们常见的ApplicationContextAware,BeanFactoryPostProcessor两位"老友",剩下有点不熟悉的就是SmartInitializingSingleton了。

SmartInitializingSingleton

java 复制代码
/**
 * Callback interface triggered at the end of the singleton pre-instantiation phase during
 * {@link BeanFactory} bootstrap. 
 * ...
/
public interface SmartInitializingSingleton {
   void afterSingletonsInstantiated();
}

SmartInitializingSingleton接口也很简单,就一个方法。通过注释我们可以得知,它的作用时机就是在单例完成pre-instantiation阶段后作为一个回调接口被触发。这个pre-instantiation,如果看过容器刷新(refresh)的小伙伴们,应该对它非常熟悉。

java 复制代码
//AbstractApplicationContext
@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
    
      prepareRefresh();
      // ...
        try {
           // ...
        
           // Instantiate all remaining (non-lazy-init) singletons.
           // 在这里
           finishBeanFactoryInitialization(beanFactory);

           // Last step: publish corresponding event.
           finishRefresh();
        }catch(BeanException ex){
            //...
        }
}

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
   
   // ...
   // Instantiate all remaining (non-lazy-init) singletons.
   beanFactory.preInstantiateSingletons();
}
java 复制代码
@Override
public void preInstantiateSingletons() throws BeansException {
    // ...
    // Trigger post-initialization callback for all applicable beans...
    for (String beanName : beanNames) {
       Object singletonInstance = getSingleton(beanName);
       if (singletonInstance instanceof SmartInitializingSingleton) {
          final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
          if (System.getSecurityManager() != null) {
             AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                smartSingleton.afterSingletonsInstantiated();
                return null;
             }, getAccessControlContext());
          }
          else {
             smartSingleton.afterSingletonsInstantiated();
          }
       }
    }
}

没错就是在这里调用了afterSingletonsInstantiated方法。我们看看EventListenerMethodProcessorafterSingletonsInstantiated方法干了什么。

再探EventListenerMethodProcessor

java 复制代码
//EventListenerMethodProcessor
@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)) {
         // ...
         if (type != null) {
            // ...
            try {
               processBean(beanName, type);
            }
            catch (Throwable ex) {
              // ...
            }
         }
      }
   }
}
private void processBean(final String beanName, final Class<?> targetType) {
   if (!this.nonAnnotatedClasses.contains(targetType) &&
         AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
         !isSpringContainerClass(targetType)) {

      Map<Method, EventListener> annotatedMethods = null;
      try {
         annotatedMethods = MethodIntrospector.selectMethods(targetType,
               (MethodIntrospector.MetadataLookup<EventListener>) method ->
                     AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
      }
      catch (Throwable ex) {
        // ...
      }

      if (CollectionUtils.isEmpty(annotatedMethods)) {
        // ...
      }
      else {
         // Non-empty set of methods
         ConfigurableApplicationContext context = this.applicationContext;
         Assert.state(context != null, "No ApplicationContext set");
         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)) {
                  // 俺在这
                  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);
                  }
                  context.addApplicationListener(applicationListener);
                  break;
               }
            }
         }
        // ...
      }
   }
}

里面内容很多,咱们直接看重点。从第27行到第32行可以看出,主要目的就是对所有的bean进行检查,找到带有EventListener注解的方法。从第50行到第56行可以看出,就是利用找到的这些方法创建监听器,然后加入容器中。这里也非常巧妙地使用了适配器模式ApplicationListenerMethodAdapter就是做适配的。 我们再来看看这些方法是如何被使用的呢?

java 复制代码
//ApplicationListenerMethodAdapter
@Override
public void onApplicationEvent(ApplicationEvent event) {
   processEvent(event);
}
public void processEvent(ApplicationEvent event) {
   Object[] args = resolveArguments(event);
   if (shouldHandle(event, args)) {
      Object result = doInvoke(args);
      // ...
   }
}
@Nullable
protected Object doInvoke(Object... args) {
   Object bean = getTargetBean();
   // ...
   try {
      return this.method.invoke(bean, args);
   }
   // ...
}

可以看到当监听到事件后就会执行onApplicationEvent方法,然后去容器中找到这些bean,利用反射执行这些方法。

总结

在容器刷新的倒数第二步中会执行bean中所有实现了SmartInitializingSingleton接口的afterSingletonsInstantiated方法。EventListenerMethodProcessor类重写了该方法,获取所有bean中带有EventListener注解的方法,并使用ApplicationListenerMethodAdapter进行适配,当监听到事件时就执行该方法。

《完》

相关推荐
蓝斯4974 小时前
[原创]《C#高级GDI+实战:从零开发一个流程图》第章:增加贝塞尔曲线,上、下、左、右连接点
java·c#·流程图
前端工作日常5 小时前
我学习到的Java 的 Service 分层:itf 和 impl 到底是什么?
java·后端
都叫我大帅哥5 小时前
Spring @Transactional 注解完全指南
java·spring
Java成神之路-5 小时前
G1 垃圾回收器 :SATB + TAMS核心机制深度解析
java·jvm
前端工作日常6 小时前
我学习到的JIT即时编译与机器码缓存失效
java·后端
带刺的坐椅6 小时前
Solon 的 10 种 HTTP 服务器:改一行依赖,换一个引擎
java·solon·jetty·undertow·mcp-server·htttp
xcLeigh6 小时前
Go入门:变量声明的五种方式详解
java·开发语言·golang
AI大模型-小华7 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
Mark_ZP7 小时前
【锁1】Synchronized vs ReentrantLock区别
java
立心者08 小时前
SpringBoot中使用TOTP实现MFA(多因素认证)
java·spring boot·后端