文章目录
一、概述
>1 ApplicationContext简述
官方如是描述:
1)
ApplicationContext
是Spring中的核心接口和容器,允许容器通过应用程序上下文环境创建、获取、管理bean。
2)在构建容器的时候,创建对象采用的策略是立即加载
的方式,即只要一读取完配置文件就立即创建配置文件中配置的对象。
>2 ApplicationContext事件机制简述
ApplicationContext.publishEvent(事件机制)是Spring提供的解耦的一种方式,采用观察者设计模式
;我们可以使用 MQ或 线程池 来代替它。
- ApplicationContext中的事件处理是通过
ApplicationEvent
事件类和ApplicationListener
事件监听器接口提供的;- 事件机制的3要素:事件发布者(
ApplicationContext
)、事件(ApplicationEvent
)、事件监听器(EventListener
/ ApplicationListener);
>3 事件触发 && 监听处理过程
(1)通过ApplicationContext的publishEvent()
方法将事件发布
到ApplicationListener(EventListener的子类)
/ EventListener
; 通知事件监听器处理事件;
(2)事件监听器通过实现的ApplicationListener
#onApplicationEvent()
方法处理事件
;
总结: ApplicationEvent事件 --> ApplicationContext.publishEvent()发布事件 --> 触发EventListener/ ApplicationListener监听器 --> 监听器执行内部onApplicationEvent(ApplicationEvent e)方法。
所以,只需要在容器中注册实现了ApplicationListener的Bean,当ApplicationContext发布事件时,事件会被监听器自动捕获。
>4 注意点
(1) ApplicationContext.publishEvent 默认是同步操作
, 并非发布后不管的异步操作,发布事件后需要等 EventListener / ApplicationListener 执行完;
(2) 如果需要开启异步操作
需要在EventListener / ApplicationListener子类上增加 @Async 注解
。
>5 实现机制
-
Spring底层publishEvent()方法发布一个事件之后,会调用
SimpleApplicationEventMulticaster#multicastEvent()
方法;-
遍历所有相应EventType的ApplicationListener,然后执行每个Listener的onApplicationEvent()方法。
-
如果设置了线程池,监听器的执行将放在线程池中执行。
想让Spring事件异步执行时可以考虑使用线程:
// SimpleApplicationEventMulticaster @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } }
-
>6 Spring事件机异步触发
在配置类中手动注入SimpleApplicationEventMulticaster(beanName为:applicationEventMulticaster
),并设置线程池。
@Configuration
public class EventConfig {
@Bean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
public SimpleApplicationEventMulticaster myEventMulticaster(){
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
simpleApplicationEventMulticaster.setTaskExecutor(taskExecutor());
return simpleApplicationEventMulticaster;
}
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setKeepAliveSeconds(300);
executor.setThreadNamePrefix("thread-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
二、在Nacos中的应用(事件机制的使用)
我们从要发布的事件、发布事件、处理事件三个方面来看。
>1 要发布的事件
自定义的事件只需继承ApplicationEvent
即可。
public class ServiceChangeEvent extends ApplicationEvent {
private Service service;
public ServiceChangeEvent(Object source, Service service) {
super(source);
this.service = service;
}
public Service getService() {
return service;
}
}
>2 发布事件
如果想让Spring Bean在业务过程发布指定容器事件,需要先让Bean获得ApplicationContext容器的引用,然后将指定容器事件Event交由ApplicationContext发布。
在Nacos中,UdpPushService
类中持有ApplicationContext
容器的引用:
UdpPushService
类中将ServiceChangeEvent
事件交由ApplicationContext发布:
this.applicationContext.publishEvent(new ServiceChangeEvent(this, service));
>3 处理事件
实现ApplicationListener
接口的onApplicationEvent()
方法,在该方法中做事件处理;
@Component
@SuppressWarnings("PMD.ThreadPoolCreationRule")
public class UdpPushService implements ApplicationContextAware, ApplicationListener<ServiceChangeEvent> {
@Override
public void onApplicationEvent(ServiceChangeEvent event) {
// todo 处理业务逻辑
// If upgrade to 2.0.X, do not push for v1.
if (ApplicationUtils.getBean(UpgradeJudgement.class).isUseGrpcFeatures()) {
return;
}
.......
// 发送UDP请求
udpPush(ackEntry);
.......
}
三、补充
更多内容可以请见Spring官网: https://docs.spring.io/spring-framework/docs/5.2.19.RELEASE/spring-framework-reference/core.html#context-functionality-events
>1 ContextRefreshedEvent(内置事件)
ContextRefreshedEvent是Spring内置的事件;
-
ApplicationContext
容器初始化、refresh完后
触发该事件。-
即:所有的bean被成功加载ApplicationContext容器已就绪可用。
-
具体体现在AbstractApplicationContext#refresh()中,在方法内部调用finishRefresh()方法,finishRefresh()方法中
publishEvent
;protected void finishRefresh() { .... // Publish the final event. publishEvent(new ContextRefreshedEvent(this)); }
-