51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Java注解@EventListener的神秘面纱

前言 {#%E5%89%8D%E8%A8%80}

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

简介 {#%E7%AE%80%E4%BB%8B}

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

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

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

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

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

@EventListener {#%40eventlistener}

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

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

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

原理 {#%E5%8E%9F%E7%90%86}

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

@Component
public class MyAnnotationListener implements ApplicationListener<MyTestEvent> {
@Override
public void onApplicationEvent(MyTestEvent event) {
	 System.out.println(&quot;注解监听器1:&quot; + 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的监听系统里了。

总结 {#%E6%80%BB%E7%BB%93}

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

这两个注解的逻辑是一模一样的,并且@TransactionalEventListener本身就被标记有@EventListener

只是最后生成监听器时所用的工厂不一样而已。

赞(1)
未经允许不得转载:工具盒子 » Java注解@EventListener的神秘面纱