Java注解@EventListener的神秘面纱
本文最后更新于 2024-10-22,文章内容可能已经过时。
前言
最近工作中需要做一个需求,我们系统作为一个中转系统,其他异构系统向我们系统发来付款请求,在我们系统中需要进行付款,但是我们系统是有工作流这个概念的我就在考虑如何监听到我的paymentDto触发判断是否成功付款后工作流执行下一步,并且回调相关的异构系统,那就要介绍我们本期的主角了"事件监听
"
简介
观察者模式:简单的来讲就是你在做事情的时候身边有人在盯着你,当你做的某一件事情是旁边观察的人感兴趣的事情的时候,他会根据这个事情做一些其他的事,但是盯着你看的人必须要到你这里来登记,否则你无法通知到他(或者说他没有资格来盯着你做事情)。
对于 Spring 容器的一些事件,可以监听并且触发相应的方法。通常的方法有 2 种,ApplicationListener 接口和@EventListener 注解。
要想顺利的创建监听器,并起作用,这个过程中需要这样几个角色:
-
事件(event)可以封装和传递监听器中要处理的参数,如对象或字符串,并作为监听器中监听的目标。
-
监听器(listener)具体根据事件发生的业务处理模块,这里可以接收处理事件中封装的对象或字符串。
-
事件发布者(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
,
只是最后生成监听器时所用的工厂不一样而已。