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环境,这里创建的ApplicationContext
是org.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
,
只是最后生成监听器时所用的工厂不一样而已。
转载请注明出处:https://www.huangchaoyu.com/2020/08/08/SpringBoot-消息机制,EventListener注解原理/