@EventListener注解的神秘面纱 -Java

 2024-10-15    0 条评论    94 浏览 Java

前言

最近工作中需要做一个需求,我们系统作为一个中转系统,其他异构系统向我们系统发来付款请求,在我们系统中需要进行付款,但是我们系统是有工作流这个概念的我就在考虑如何监听到我的paymentDto触发判断是否成功付款后工作流执行下一步,并且回调相关的异构系统,那就要介绍我们本期的主角了 事件监听

简介

观察者模式:简单的来讲就是你在做事情的时候身边有人在盯着你,当你做的某一件事情是旁边观察的人感兴趣的事情的时候,他会根据这个事情做一些其他的事,但是盯着你看的人必须要到你这里来登记,否则你无法通知到他(或者说他没有资格来盯着你做事情)。

对于 Spring 容器的一些事件,可以监听并且触发相应的方法。通常的方法有 2 种,ApplicationListener 接口和@EventListener 注解。 要想顺利的创建监听器,并起作用,这个过程中需要这样几个角色:

  1. 事件(event)可以封装和传递监听器中要处理的参数,如对象或字符串,并作为监听器中监听的目标。

  2. 监听器(listener)具体根据事件发生的业务处理模块,这里可以接收处理事件中封装的对象或字符串。

  3. 事件发布者(publisher)事件发生的触发者。

@EventListener

@EventListener 注解,实现对任意的方法都能监听事件。

在任意方法上标注@EventListener 注解,指定 classes,即需要处理的事件类型,一般就是 ApplicationEven 及其子类,可以设置多项。

@Async
@EventListener(paymentCallBackEvent.class)
public void onApplicationEvent(paymentCallBackEvent payArrangeEvent) {
    log.info("支付结果已返回");
    //TODO:实际业务处理代码
}

原理

其实上面添加 @EventListener 注解的方法被包装成了 ApplicationListener 对象,上面的类似于下面这种写法,这个应该比较好理解。

@Component
public class MyAnnotationListener implements ApplicationListener<MyTestEvent> {
    
    @Override
    public void onApplicationEvent(MyTestEvent event) {
         System.out.println("注解监听器1:" + event.getMsg());
    }
}

那么Spring是什么时候做这件事的呢?

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                break;
            case REACTIVE:
                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                break;
            default:
                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
                    ex);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

他的构造方法如下:

public AnnotationConfigServletWebServerApplicationContext() {
    this.reader = new AnnotatedBeanDefinitionReader(this);
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

进到 AnnotatedBeanDefinitionReader 里面:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    Assert.notNull(environment, "Environment must not be null");
    this.registry = registry;
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

再进到 AnnotationConfigUtils 的方法里面,省略了一部分代码,可以看到他注册了一个 EventListenerMethodProcessor 类到工厂了。这是一个 BeanFactory 的后置处理器。

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
        BeanDefinitionRegistry registry, @Nullable Object source) {

    DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); 
    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));
    }
    return beanDefs;
}

查看这个 BeanFactory 的后置处理器 EventListenerMethodProcessor,下面方法,他会遍历所有bean,找到其中带有 @EventListener 的方法,将它包装成 ApplicationListenerMethodAdapter,注册到工厂里,这样就成功注册到Spring的监听系统里了。

总结

上面介绍了 @EventListener 的原理,其实上面方法里还有一个 @TransactionalEventListener 注解,其实原理是一模一样的,只是这个监听者可以选择在事务完成后才会被执行,事务执行失败就不会被执行。

这两个注解的逻辑是一模一样的,并且 @TransactionalEventListener 本身就被标记有 @EventListener,只是最后生成监听器时所用的工厂不一样而已。

部分素材来自网络,若不小心影响到您的利益,请联系我们删除。