Spring事件机制 ApplicationEventPublisher
目录
2.4、事件管理(ApplicationEventMulticaster)
4.2.1、初始化事件广播器(initApplicationEventMulticaster)
4.2.2、注册监听器(registerListeners)
1、观察者模式
包含观察者和目标(被观察者)两种对象。
目标和观察者之间存在一对多的关系,通过建立目标与观察者之间的联系,当目标的某个状态发生变化时,所有与之关联的观察者都会得到通知并执行各自的任务。
也叫作发布-订阅模式:
- 订阅:观察者向目标对象注册自己,告诉目标对象自己在观察它。
- 发布:当目标对象的状态发生改变时,向它所有的观察者发送通知,观察者收到通知后执行各自的操作。
优点:降低了目标与观察者之间的耦合关系。
缺点:目标与观察者之间没有完全解耦,还可能出现循环引用;当观察者对象很多时,通知的发布可能会很耗时。
2、Spring中的观察者
2.1、事件(ApplicationEvent)
事件本身,ApplicationEvent是所有事件的弗雷,可以用来传递数据。
Spring内置事件如下:
- ContextRefreshedEvent: ApplicationContext初始化或刷新,或者说在容器实例化(refresh())时发布事件;
- ContextStartedEvent:Spring容器启动时,即调用ConfigurableApplicationContext接口的start()时发布事件;
- ContextStoppedEvent:Spring容器停止时,即调用ConfigurableApplicationContext接口的stop()时发布事件,关闭的容器可以通过start()重启;
- ContextClosedEvent:Spring容器关闭时,即调用ConfigurableApplicationContext接口的close()时发布事件,所有的Bean已被销毁,无法重启容器;
- RequestHandledEvent:当一个请求被处理完成时发布事件。
2.2、事件发布(ApplicationContext)
通过ApplicationEventPublisher中的publishEvent发布事件。
2.3、事件监听(ApplicationListener)
观察者,监听事件。事件发布后,会通知观察者执行监听器中的业务逻辑。
2.4、事件管理(ApplicationEventMulticaster)
- 用于事件监听器的注册和事件的广播。
- 注册:将监听器与事件关联。
- 广播:事件发布后,通知与事件关联的所有监听器。
3、demo
3.1、事件任务类
-
/**
-
* 用户
-
*/
-
-
-
public class User {
-
private String name;
-
private int age;
-
}
3.2、事件
-
/**
-
* 用戶注册事件
-
*/
-
-
public class UserRegisterEvent extends ApplicationEvent{
-
-
private User user;
-
-
public UserRegisterEvent(User user) {
-
super("user-register-event");
-
this.user = user;
-
}
-
}
3.3、事件监听器
3.3.1、继承ApplicationListener接口
-
/**
-
* 继承ApplicationListener接口监听事件
-
* 监听器:当有用户注册事件发生时,记录日志
-
*/
-
-
-
public class RecordUserRegisterListener implements ApplicationListener<UserRegisterEvent> {
-
-
-
public void onApplicationEvent(UserRegisterEvent event) {
-
User user = event.getUser();
-
Thread.sleep(3000L);
-
log.info("用户[" user.getName() "]注册成功,记录日志");
-
}
-
}
3.3.2、@EventListener注解
-
/**
-
* @EventListener注解监听事件
-
*/
-
-
-
public class GlobalEventListener {
-
-
-
public void onUserGrant1(UserRegisterEvent event) {
-
User user = event.getUser();
-
log.info("用户[" user.getName() "]注册成功,年龄[" user.getAge() "]>=18,授予成人权限");
-
}
-
-
-
public void onUserGrant2(UserRegisterEvent event) {
-
User user = event.getUser();
-
log.info("用户[" user.getName() "]注册成功,年龄[" user.getAge() "]<18,授予未成年权限");
-
}}
3.4、事件发布
-
-
-
-
public class UserController {
-
-
-
private ApplicationEventPublisher publisher;
-
-
-
public void register(String name, int age) {
-
User user = User.builder().name(name).age(age).build();
-
// 事件发布
-
publisher.publishEvent(new UserRegisterEvent(user));
-
log.info("用户[" user.getName() "]注册完成");
-
}
-
}
3.5、启动类
-
-
-
-
public class BootstrapApplication {
-
public static void main(String[] args) {
-
SpringApplication.run(BootstrapApplication.class, args);
-
}
-
}
3.6、注意事项
- 事务监听器
@EnableTransactionManagement开启事务支持,@TransactionalEventListener标识事务监听器。
发布事件的操作必须在事务(@Transactional)内进行,否则监听器不会生效,除非将fallbackExecution标志设置为true(@TransactionalEventListener(fallbackExecution = true))
可以配置在事务的哪个阶段来监听事务(默认在事务提交后监听),@TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)。
- 异步支持
@EnableAsync开启异步支持,@Async标识监听器异步处理。
开启异步执行后,方法的异常不会抛出,只能在方法内部处理。
- 监听器顺序
@Order控制多个监听器的执行顺序,值越小,监听器越先执行。
4、原理简介
4.1、广播器
- ApplicationEventMulticaster接口:提供了添加/移除监听器以及广播事件给监听器的行为。
- AbstractApplicationEventMulticaster抽象类:提供了基础的监听器注册/移除以及查找能力。
- SimpleApplicationEventMulticaster类:提供了事件广播功能。
4.2、注册广播器和监听器
Spring容器初始化时,在refresh()方法中,会进行广播器和监听器的注册。
4.2.1、初始化事件广播器(initApplicationEventMulticaster)
-
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
-
protected void initApplicationEventMulticaster() {
-
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
-
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
-
this.applicationEventMulticaster =
-
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
-
if (logger.isTraceEnabled()) {
-
logger.trace("Using ApplicationEventMulticaster [" this.applicationEventMulticaster "]");
-
}
-
}
-
else {
-
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
-
simpleApplicationEventMulticaster.setApplicationStartup(getApplicationStartup());
-
this.applicationEventMulticaster = simpleApplicationEventMulticaster;
-
// 将事件广播器作为单例bean注册到BeanFactory中
-
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
-
if (logger.isTraceEnabled()) {
-
logger.trace("No '" APPLICATION_EVENT_MULTICASTER_BEAN_NAME "' bean, using "
-
"[" this.applicationEventMulticaster.getClass().getSimpleName() "]");
-
}
-
}
-
}
4.2.2、注册监听器(registerListeners)
-
protected void registerListeners() {
-
// 获取监听器然后注册到广播器中
-
for (ApplicationListener<?> listener : getApplicationListeners()) {
-
getApplicationEventMulticaster().addApplicationListener(listener);
-
}
-
-
// 获取bean Name数组String[] listenerBeanNames 然后注册到广播器中
-
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
-
for (String listenerBeanName : listenerBeanNames) {
-
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
-
}
-
-
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
-
-
// 处理以前的事件,先将 earlyApplicationEvents 赋予null,然后判断 earlyEventsToProcess 如果不为空就广播出去
-
this.earlyApplicationEvents = null;
-
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
-
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
-
getApplicationEventMulticaster().multicastEvent(earlyEvent);
-
}
-
}
-
}
4.3、事件广播
通过ApplicationEventPublisher的publishEvent方法将事件广播出去。
4.3.1、publistEvent
-
protected void publishEvent(Object event, { ResolvableType eventType)
-
Assert.notNull(event, "Event must not be null");
-
-
ApplicationEvent applicationEvent;
-
if (event instanceof ApplicationEvent) {
-
applicationEvent = (ApplicationEvent) event;
-
}
-
else {
-
applicationEvent = new PayloadApplicationEvent<>(this, event);
-
if (eventType == null) {
-
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
-
}
-
}
-
-
//如果早期事件不为null,则将事件放入早期事件集合中--说明广播器还没有实例化好
-
if (this.earlyApplicationEvents != null) {
-
this.earlyApplicationEvents.add(applicationEvent);
-
}
-
else {
-
// 获取广播器进行事件广播
-
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
-
}
-
-
//将事件也交给父类处理
-
if (this.parent != null) {
-
if (this.parent instanceof AbstractApplicationContext) {
-
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
-
}
-
else {
-
this.parent.publishEvent(event);
-
}
-
}
-
}
4.3.2、multicastEvent
-
-
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
-
// 解析事件类型
-
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
-
// 尝试获取任务执行器
-
Executor executor = getTaskExecutor();
-
-
// 获取合适的ApplicationListener,循环调用监听器的onApplicationEvent方法
-
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
-
// 判断executor 是否不为null
-
if (executor != null) {
-
executor.execute(() -> invokeListener(listener, event));
-
}
-
// 判断applicationStartup
-
else if (this.applicationStartup != null) {
-
StartupStep invocationStep = this.applicationStartup.start("spring.event.invoke-listener");
-
invokeListener(listener, event);
-
invocationStep.tag("event", event::toString);
-
if (eventType != null) {
-
invocationStep.tag("eventType", eventType::toString);
-
}
-
invocationStep.tag("listener", listener::toString);
-
invocationStep.end();
-
}
-
else {
-
// 否则,直接调用listener.onApplicationEvent,内部通过反射调用监听器方法
-
invokeListener(listener, event);
-
}
-
}
-
}
以上内容为个人学习理解,如有问题,欢迎在评论区指出。
部分内容截取自网络,如有侵权,联系作者删除。
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhfhahfh
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
photoshop蒙版画笔没反应怎么办
PHP中文网 06-24