Spring(十六)-Events事件
参考文献
Java事件/监听器编程模型
- 设计模式-观察模式扩展
- 可观者对象(消息发送者):
java.util.Observable
- 观察者:
java.util.Observer
- 可观者对象(消息发送者):
- 标准化接口
- 事件对象:
java.util.EventObject
- 事件监听器:
java.util.EventListener
- 事件对象:
事件模式中的几个概念
- 事件源: 事件的触发者.
- 事件: 描述发生了什么事情的对象.
- 事件监听器: 监听到事件发生的时候,做一些处理.
硬编码方式实现Spring事件
-
定义事件
- 自定义事件,需要继承
ApplicationEvent
类
- 自定义事件,需要继承
-
定义监听器
- 自定义事件监听器,需要实现
ApplicationListener
接口,这个接口有个方法onApplicationEvent
需要实现,用来处理感兴趣的事件.
- 自定义事件监听器,需要实现
-
创建事件广播器
- 创建事件广播器
ApplicationEventMulticaster
,这是个接口,可以自己实现这个接口,也可以直接使用系统提供的SimpleApplicationEventMulticaster
- 创建事件广播器
-
向广播器中注册事件监听器
-
将事件监听器注册到广播器
ApplicationEventMulticaster
1
2ApplicationEventMulticaster applicationEventMulticaster = new SimpleApplicationEventMulticaster();
applicationEventMulticaster.addApplicationListener(new CustomApplicationListener());
-
-
通过广播器发布事件
-
广播事件,调用
ApplicationEventMulticaster#multicastEvent
方法广播事件,此时广播器中对这个感兴趣的监听器会处理这个事件.1
applicationEventMulticaster.multicastEvent(new CustomEvent(applicationEventMulticaster,1L));
-
结合Spring使用Spring事件
- 面向接口的方式
- 面向
@EventListener
注解的方式
面向接口的方式使用步骤
-
自定义事件,继承
ApplicationEvent
类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import lombok.Getter;
import org.springframework.context.ApplicationEvent;
/**
* @author HoleLin
*/
public class PullTaskProcessingEvent extends ApplicationEvent {
private final String taskId;
public PullTaskProcessingEvent(String taskId) {
super(new Object());
this.taskId = taskId;
}
} -
自定义事件监听器,需要实现
ApplicationListener
接口,这个接口有个方法onApplicationEvent
需要实现,用来处理感兴趣的事件.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* 监听器
*
* @author HoleLin
*/
public class PullTaskProcessingListener implements ApplicationListener<PullTaskProcessingEvent> {
public void onApplicationEvent(PullTaskProcessingEvent processingEvent) {
}
} -
在对应的业务处理类中实现
ApplicationEventPublishAware
,注入ApplicationEventPublisher
来发布事件1
this.applicationEventPublisher.publishEvent(new CustomEvent(object));
原理
-
Spring容器在创建Bean的过程中,会判断Bean是否为
ApplicationListener
类型,进而会将其作为监听器注册到org.springframework.context.support.AbstractApplicationContext#applicationEventMulticaster
中.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// org.springframework.context.support.ApplicationListenerDetector#postProcessAfterInitialization
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof ApplicationListener) {
// potentially not detected as a listener by getBeanNamesForType retrieval
Boolean flag = this.singletonNames.get(beanName);
if (Boolean.TRUE.equals(flag)) {
// singleton bean (top-level or inner): register on the fly
this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
}
else if (Boolean.FALSE.equals(flag)) {
if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
// inner bean with other scope - can't reliably process events
logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
"but is not reachable for event multicasting by its containing ApplicationContext " +
"because it does not have singleton scope. Only top-level listener beans are allowed " +
"to be of non-singleton scope.");
}
this.singletonNames.remove(beanName);
}
}
return bean;
}
面向@EventListener
注解方式使用步骤
-
自定义事件,继承
ApplicationEvent
类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import lombok.Getter;
import org.springframework.context.ApplicationEvent;
/**
* @author HoleLin
*/
public class PullTaskProcessingEvent extends ApplicationEvent {
private final String taskId;
public PullTaskProcessingEvent(String taskId) {
super(new Object());
this.taskId = taskId;
}
} -
使用
@EventListener
标注在一个Bean的方法上,这个方法就可以用来处理感兴趣的事件1
2
3
4
5
6
7
public class CustomListener{
public void custom(CustomEvent event){
// deal event
}
}
原理
1 | // org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object) |
-
EventListenerMethodProcessor
实现了SmartInitializingSingleton
接口,SmartInitializingSingleton
接口中的afterSingletonsInstantiated
方法会在所有单例的bean创建完成之后被Spring容器调用 -
Spring中处理
@EventListener
注解源码位于1
org.springframework.context.event.EventListenerMethodProcessor#afterSingletonsInstantiated
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
51private void processBean(final String beanName, final Class<?> targetType) {
if (!this.nonAnnotatedClasses.contains(targetType) &&
AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
!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);
}
}
}
} -
@EventListener
方法必须是Spring Bean
中的public
方法,并支持返回类型为非void
的情况,当它监听一个或多个ApplicationEvent
时,其参数可为零到一个ApplicationEvent
面向接口的事件/监听器设计模式
-
事件/监听器场景举例
Java 技术规范 事件接口 监听器接口 Java Beans java.beans.PropertyChangeEvent
java.beans.PropertyChangeListener
Java AWT java.awt.event.MouseEvent
java.awt.event.MouseListener
Java Swing javax.swing.event.MenuEvent
javax.swing.event.MenuListener
Java Perference java.util.prefs.PreferenceChangeEvent
java.util.prefs.PreferenceChangeListener
Java 技术规范 事件注解 监听器注解 Servlet 3.0+ @javax.servlet.annotation.WebListener
JPA 1.0+ @javax.persistence.PostPersist
Java Common @PostConstruct
EJB 3.0+ @javax.ejb.PerPassivate
JSF 2.0+ @javax.faces.event.ListenerFor
Spring 标准事件 - ApplicationEvent
- Java标准事件
java.util.EventObject
扩展- 扩展特性: 事件发生事件戳
- Spring 应用上下文
ApplicationEvent
扩展:ApplicationContextEvent
- Spring 应用上下文(
ApplicationContext
)作为事件源 - 具体实现
org.springframework.context.event.ContextClosedEvent
org.springframework.context.event.ContextRefreshedEvent
org.springframework.context.event.ContextStartedEvent
org.springframework.context.event.ContextStoppedEvent
- Spring 应用上下文(
基于接口的Spring事件监听器
-
Java标准事件监听器
java.util.EventListener
扩展- 扩展接口:
org.springframework.context.ApplicationListener
- 设计特点: 单一类型事件处理
- 处理方法:
onApplicationEvent(ApplicationEventevent)
- 事件类型:
org.springframework.context.ApplicationEvent
- 扩展接口:
-
Spring注解:
org.springframework.context.event.EventListener
特性 说明 设计特点 支持多 ApplicationEvent
类型,无需接口约束注解目标 方法 是否支持异步执行 支持 是否支持泛型类型事件 支持 是指支持顺序控制 支持,配合 @Order
注解控制
注册Spring ApplicationListener
- 方法一:
ApplicationListener
作为Spring Bean注册 - 方法二: 通过
ConfigurableApplicationContext
API注册
Spring事件发布器
-
方法一: 通过
ApplicationEventPublisher
发布Spring事件- 获取
ApplicationEventPublisher
- 依赖注入
1
2
3
4
5
6
7
8
9
10
class SpirngEventApplicationTests {
ApplicationEventPublisher appEventPublisher;
void contextLoads() {
appEventPublisher.publishEvent(new CustomEvent("1111111"));
}
}1
2
3
4
5
6
7
8
9
10
public class PublisherService implements ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
} - 获取
-
方法二: 通过
ApplicationEventMulticaster
发布Spring事件- 获取
ApplicationEventMulticaster
- 依赖注入
- 依赖查找
- 获取
Spring 层次性上下文事件传播
-
发生说明
当Spring应用出现多层次Spring应用上下文(
ApplicationContext
)时,如Spring WebMVC
,Spring Boot
或Spring Cloud
场景下,由子ApplicationContext
发起Spring事件可能传递其Parent ApplicationContext
(直到Root)的过程 -
如何避免
- 定位Spring事件源(
ApplicationContext
)进行过滤处理
- 定位Spring事件源(
同步和异步Spring事件广播
-
基于实现类:
org.springframework.context.event.SimpleApplicationEventMulticaster
- 模式切换:
setTaskExecutor(@Nullable Executor taskExecutor)
方法- 默认模式: 同步
- 异步模式:
java.util.concurrent.ThreadPoolExecutor
- 设计缺陷: 非基于接口契约编程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MainConfig {
public ApplicationEventMulticaster applicationEventMulticaster() {
//创建一个事件广播器
SimpleApplicationEventMulticaster result = new
SimpleApplicationEventMulticaster();
//给广播器提供一个线程池,通过这个线程池来调用事件监听器
Executor executor =
this.applicationEventMulticasterThreadPool().getObject();
//设置异步执行器
result.setTaskExecutor(executor);
return result;
}
public ThreadPoolExecutorFactoryBean applicationEventMulticasterThreadPool() {
ThreadPoolExecutorFactoryBean result = new ThreadPoolExecutorFactoryBean();
result.setThreadNamePrefix("applicationEventMulticasterThreadPool-");
result.setCorePoolSize(5);
return result;
}
} - 模式切换:
-
基于注解:
org.springframework.context.event.EventListener
- 模式切换
- 默认模式: 同步
- 异步模式:
org.springframework.scheduling.annotation.Async
- 实现限制: 无法直接实现同步/异步动态切换
- 模式切换
Spring 4.1事件异常处理
- Spring 3.0 错误处理接口:
org.springframework.util.ErrorHandler
- 使用场景
- Spring事件(
Events
)SimpleApplicationEventMulticaster
Spring 4.1开始支持
- Spring本地调度(
Scheduling
)org.springframework.scheduling.concurrent.ConcurrentTaskScheduler
org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
- Spring事件(
- 使用场景
Spring事件/监听实现原理
- 核心类:
org.springframework.context.event.SimpleApplicationEventMulticaster
- 设计模式: 观察者模式扩展
- 被观察者:
org.springframework.context.ApplicationListener
- API 添加
- 依赖查找
- 通过对象:
org.springframework.context.ApplicationEvent
- 被观察者:
- 执行模式: 同步/异步
- 异常处理:
org.springframework.util.ErrorHandler
- 泛型处理:
org.springframework.core.ResolvableType
- 设计模式: 观察者模式扩展
SimpleApplicationEventMulticaster
默认采用同步广播事件的方式.Spring事件通过调用SimpleApplicationEventMulticaster#multicastEvent
方法广播,根据ApplicationEvent
具体类型查找匹配的ApplicationListener
列表,然后逐一同步或异步地调用ApplicationListener.onApplicationEvent(ApplicationEvent event)
方法,实现ApplicationListener
事件监听.
泛型事件
- 实现
org.springframework.core.ResolvableTypeProvider
接口
1 | import lombok.Getter; |
SpringBoot
事件
监听方法 | refresh() 方法执行顺序 |
事件类型 | 发生时机 | SpringBoot起始版本 |
---|---|---|---|---|
starting() |
调用之前 | ApplicationStartingEvent |
当SpringBoot 应用已启动时 |
1.5 |
environmentPrepared() |
调用之前 | ApplicationEnvironmentPreparedEvent |
当SpringBoot Environment 实例已准备时 |
1.0 |
contextPrepared() |
调用之前 | ApplicationContextInitializedEvent |
applicationcontextinitializer被调用时发布,但在加载任何bean定义之前 |
2.1 |
contextLoaded() |
调用之前 | ApplicationPreparedEvent |
当SpringBoot Environment 应用预备时 |
1.0 |
started() |
调用之后 | ApplicationStartedEvent |
当SpringBoot 应用已启动时 |
|
ready() |
调用之后 | ApplicationReadyEvent |
当SpringBoot Environment 应用完全可用时 |
1.3 |
failed() |
调用之后 | ApplicationFailedEvent |
当SpringBoot Environment 应用启动失败时 |
1.0 |
SpringBoot
1.4开始采用SpringApplication
与ConfigurableApplicationContext
隔离SimpleApplicationEventMulticaster
实例,即SpringBoot
有单独的SimpleApplicationEventMulticaster.
SpringBoot
事件/监听机制继承与Spring
事件/监听机制,其事件类型继承与Spring ApplicationEvent
,事件监听器仍通过ApplicationListener
实现,而广播器实现SimpleApplicationEventMulticaster
将TM关联起来.
SpringCloud
事件
事件类型 | 发生时机 |
---|---|
EnvironmentChangeEvent |
当Environment 示例配置属性发生变化时 |
HeartbeatEvent |
当DiscoveryClient 客户端发送心跳时 |
InstancePreRegisteredEvent |
当服务实例注册前 |
InstanceRegisteredEvent |
当服务实例注册后 |
RefreshEvent |
当RefreshEndpoint 被调用时 |
RefreshScopeRefreshedEvent |
当Refresh Scope Bean 被调用时 |