SpringBoot @EventListener注解的原理和使用

SpringBoot @EventListener注解的原理和使用

1.观察者模式/监听者模式

​ 这个模式大家应该很熟悉,也经常被使用。但是如果我们使用的是Spring框架,其实它内置了一个好用的观察者模式的实现,用法也很简单。

2.用法示例

​ 下面的代码,首先在类里自动注入了ApplicationEventPublisher,这个也就是我们的ApplicationCOntext,它实现了这个接口。

再在post方法里,发送一条消息,这个消息其实就是一个类的实力。这样这条消息就会发送给监听器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 1.自动注入ApplicationEventPublisher
@Autowired
private ApplicationEventPublisher applicationEventPublisher;

// 2.在类里面发送消息
@PostConstruct
public void post(){
applicationEventPublisher.publishEvent(new UserCreateMessage("张三"));
}

public static class UserCreateMessage{
public UserCreateMessage(String name) {
this.name = name;
}

private String name;
private Date createTime;
}

​ 在另一个类配置类里面使用注解@EventListener标记一个方法,这里注意参数是泛型的PayloadApplicationEvent,泛型就是我们的消息类型。因为上面发送的消息会被包装成PayloadApplicationEvent对象发送给监听者,只有泛型类型一致的才会匹配成功,且方法只能有一个参数

1
2
3
4
5
6
7
//3. 这个方法就可以收到消息
@EventListener
public void listener1(PayloadApplicationEvent<UserCreateMessage> event){
UserCreateMessage payload = event.getPayload();
System.out.println(payload);
}

​ 这就是最简单的用法,要保证上面的注解类会被注册到Spring里才行哦。

还有就是调用监听者是同步的,在监听方法里抛异常或堵塞都会影响发送消息的方法。

3.原理

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

1
2
3
4
5
6
7
8
9
10
@Component
public class MyListener implements ApplicationListener<PayloadApplicationEvent<WebApplication.UserCreateMessage>> {

@Override
public void onApplicationEvent(PayloadApplicationEvent<WebApplication.UserCreateMessage> event) {
WebApplication.UserCreateMessage payload = event.getPayload();
System.out.println(payload);
}

}

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

查看SpringBoot的源码,找到下面的代码,因为我是Tomcat环境,这里创建的ApplicationContextorg.springframework.bootweb.servlet.context.AnnotationConfigServletWebServerApplicationContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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);
}

这是他的构造方法

1
2
3
4
public AnnotationConfigServletWebServerApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}

进到AnnotatedBeanDefinitionReader里面

1
2
3
4
5
6
7
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);
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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的监听系统里了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
	@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);
}
}
}
}
}




private void processBean(final String beanName, final Class<?> targetType) {
if (!this.nonAnnotatedClasses.contains(targetType) &&
!targetType.getName().startsWith("java") &&
!isSpringContainerClass(targetType)) {

Map<Method, EventListener> annotatedMethods = null;
try {
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);
}
}

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");
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;
}
}
}
if (logger.isDebugEnabled()) {
logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
beanName + "': " + annotatedMethods);
}
}
}
}

由方法生成Listener的逻辑由EventListenerFactory完成的,这又分为两种,一种是普通的@EventLintener 另一种是@TransactionalEventListener ,是由两个工厂处理的。

4.总结

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

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

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


SpringBoot @EventListener注解的原理和使用
https://www.huangchaoyu.com/1233211011.html
作者
hcy
发布于
2020年8月8日
更新于
2024年8月17日
许可协议