Spring 事件监听
Spring 的核心是 ApplicationContext,它负责管理 Bean的完整生命周期;当加载 Bean 时,ApplicationContext 发布某些类型的事件;例如,当上下文启动时,ContextStartedEvent 发布消息,当上下文停止时,ContextStoppedEvent 发布消息;
通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件;如果一个 Bean 实现 ApplicationListener 接口,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 Bean实例会被通知;类似MQ的发布订阅;
ApplicationEvent UML图如下:
Spring常用事件
序号 | Spring 事件 |
---|---|
1 | ContextRefreshedEvent:ApplicationContext 被初始化或刷新时,该事件被发布;这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法来发生;容器刷新完成(所有bean都完全创建)会发布这个事件; |
2 | ContextStartedEvent:当使用 ConfigurableApplicationContext 接口中的 start() 方法启动 ApplicationContext 时,该事件被发布; |
3 | ContextStoppedEvent:当使用 ConfigurableApplicationContext 接口中的 stop() 方法停止 ApplicationContext 时,发布这个事件; |
4 | ContextClosedEvent:当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布; |
5 | RequestHandledEvent:在Web应用中,当一个http请求结束触发该事件; |
ApplicationContext
ApplicationContext即应用上下文,继承自BeanFactory接口,可用于获取Spring 管理的Bean对象实例;
ApplicationContextAware:Aware是属性注入,ApplicationContextAware与ApplicationContext不同的是,当Spring容器初始化的时候,实现了 ApplicationContextAware 接口的 Bean会自动注入ApplicationContext;
使用如下:
自定义ApplicationContextAware
@Component
public class ApplicationContextProvider implements ApplicationContextAware {
private ApplicationContext applicationContext; /**
* 上下文实例
* @param applicationContext
* @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
} /**
* 获取上下文
* @return
*/
public ApplicationContext getApplicationContext() {
return this.applicationContext;
} /**
* 通过类名获取Bean实例
* @param name
* @return
*/
public Object getBean(String name) {
return this.applicationContext.getBean(name);
} /**
* 通过字节码获取Bean实例
* @param clazz
* @param <T>
* @return
*/
public <T> T getBean(Class<T> clazz) {
return this.applicationContext.getBean(clazz);
}
}
自定义监听器
@Slf4j
@Component
public class MyListener implements ApplicationListener { @Override
public void onApplicationEvent(ApplicationEvent event) { if (event instanceof MyApplicationEvent) {
log.info("recv:" + event);
} else {
log.info("recv 容器本身:" + event);
}
}
}
自定义事件
public class MyApplicationEvent extends ApplicationEvent {
private Person person; public Person getPerson() {
return person;
} public void setPerson(Person person) {
this.person = person;
} public MyApplicationEvent(Object source, Person person) {
super(source);
this.person = person;
} /**
* Create a new ApplicationEvent.
*
* @param source the object on which the event initially occurred (never {@code null})
*/
public MyApplicationEvent(Object source) {
super(source);
}
}
在controller添加一个发布事件的方法
@Autowired
private ApplicationContextProvider provider; @GetMapping("/publish")
public String publish() {
ApplicationContext applicationContext = provider.getApplicationContext();
Person person = new Person();
person.setId(1);
person.setAddress("chn");
person.setName("test");
person.setPhone("123"); applicationContext.publishEvent(new MyApplicationEvent(person) {
});
log.info("applicationContext:{}", applicationContext); return "applicationContext: " + applicationContext;
}
当往/publish这个映射发请求的时候,publishEvent方法会发送事件;监听器就会就会接收;
ApplicationListener
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { /**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event); }
ApplicationListener
监听ApplicationEvent
及其子类的事件;
AbstractApplicationContext.java
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
...
}
ConfigurableApplicationContext:SPI接口将由大多数应用程序上下文实现;除了提供中的应用程序上下文客户端方法外,还提供了配置应用程序上下文的功能在ApplicationContext接口;
refresh方法执行容器的加载
public void refresh() throws BeansException, IllegalStateException {
...
// Initialize event multicaster for this context.
initApplicationEventMulticaster(); ...
// Last step: publish corresponding event.
finishRefresh();
... } protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//查找容器中是否存在"applicationEventMulticaster"的组件
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
//没有则new "applicationEventMulticaster"的组件,并加入到容器中,注入到"applicationEventMulticaster",registerSingleton会在存储所有注册的监听器map中查找监听器是否存在,不存在则往map中执行put操作
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]");
}
}
}
finshRefresh方法
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches(); // Initialize lifecycle processor for this context.
initLifecycleProcessor(); // Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh(); // Publish the final event.
publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
publishEvent方法 发布事件
@Override
public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
} protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
} // Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
}
} // Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
} // Publish event via parent context as well...
// 判断当前ApplicationContext有没有父级ApplicationContext
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
AbstractApplicationEventMulticaster提供基本的监听器注册功能
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { }
SimpleApplicationEventMulticaster.java
//将给定的应用程序事件分发到对应的监听器
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
} //调用给定事件的监听器
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
} private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
//调用实现ApplicationListener接口的onApplicationEvent方法
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
Spring 事件监听的更多相关文章
- Spring事件监听Demo
Spring事件监听实现了观察者模式.本Demo在junit4测试环境中实现 主要有三个类事件类.监听器类.事件发布类(入口) 事件类必须继承 ApplicationEvent,代码如下: impor ...
- Spring事件监听机制源码解析
Spring事件监听器使用 1.Spring事件监听体系包括三个组件:事件.事件监听器,事件广播器. 事件:定义事件类型和事件源,需要继承ApplicationEvent. package com.y ...
- spring事件监听(eventListener)
原理:观察者模式 spring的事件监听有三个部分组成,事件(ApplicationEvent).监听器(ApplicationListener)和事件发布操作. 事件 事件类需要继承Applicat ...
- Spring 事件监听机制及原理分析
简介 在JAVA体系中,有支持实现事件监听机制,在Spring 中也专门提供了一套事件机制的接口,方便我们实现.比如我们可以实现当用户注册后,给他发送一封邮件告诉他注册成功的一些信息,比如用户订阅的主 ...
- Spring事件监听机制
前言 Spring中的事件机制其实就是设计模式中的观察者模式,主要由以下角色构成: 事件 事件监听器(监听并处理事件) 事件发布者(发布事件) 首先看一下监听器和发布者的接口定义 public int ...
- Spring事件监听ApplicationListener源码流程分析
spring的事件机制是基于观察者设计模式的,ApplicationListener#onApplicationEvent(Event)方法,用于对事件的处理 .在容器初始化的时候执行注册到容器中的L ...
- Spring之事件监听(观察者模型)
目录 Spring事件监听 一.事件监听案例 1.事件类 2.事件监听类 3.事件发布者 4.配置文件中注册 5.测试 二.Spring中事件监听分析 1. Spring中事件监听的结构 2. 核心角 ...
- Spring的事件监听机制
最近公司在重构广告系统,其中核心的打包功能由广告系统调用,即对apk打包的调用和打包完成之后的回调,需要提供相应的接口给广告系统.因此,为了将apk打包的核心流程和对接广告系统的业务解耦,利用了spr ...
- SpringBoot事件监听机制及观察者模式/发布订阅模式
目录 本篇要点 什么是观察者模式? 发布订阅模式是什么? Spring事件监听机制概述 SpringBoot事件监听 定义注册事件 注解方式 @EventListener定义监听器 实现Applica ...
随机推荐
- 2.Buffer详解
- JVM学习(九)volatile应用
一.初认volatile 首先学习volatile关键字时,我们先简单的了解一下它能干啥: 工作内存与主内存同步延迟现象导致的可见性问题: 可通过synchronized或volatile关键字解决, ...
- 如何设置一个生产级别的高可用etcd集群
在之前的文章中,我们详细介绍了K3s的架构以及部署场景,给尚未了解K3s的朋友提供了一个很好的入门方向.那么,在本文中我们将探索如何配置一个3节点的etcd集群,它将会被用于高可用.多节点的K3s集群 ...
- 微服务实战系列(六)-网关springcloud zuul
1. 场景描述 今天接着介绍springcloud,今天介绍下springcloud的路由网关-Zuul,外围系统或者用户通过网关访问服务,网关通过注册中心找到对应提供服务的客户端,网关也需要到注册中 ...
- 解决 SecureCRT 和 SecureFX 中文乱码
引言 最近老是有小伙伴给我发消息说,下载的 SecureCRT 和 SecureFX 安装打开后连接了自己的服务器或虚拟机后会出现中文乱码,每次都要给一一回复,我倒没事,主要是有时候因为工作的原因,所 ...
- js自动生成条形码插件-JsBarcode
JsBarcode.html <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...
- mysql笔记--基础知识
SQL基础 SQL语句的分类: DQL: 数据库查询语句,基本的就是select查询命令,用于查询数据 DML: 数据操纵语句,用于插入,更新,删除数据,即INSERT, UPDATE,DELETE ...
- LeetCode刷题总结-DFS、BFS和回溯法篇
本文总结LeetCode上有关深度优先搜索(DFS).广度优先搜索(BFS)和回溯法的算法题,推荐刷题总数为13道.具体考点分析如下图: 一.深度优先搜索 1.字符匹配问题 题号:301. 删除无效的 ...
- [Angular JS教程] HeroService: getHeroes failed: undefined 问题解决方法
最近在学习入门Angular JS,学习资源是https://angular.cn/tutorial, 在学习到 "https://angular.cn/tutorial/toh-pt6模拟 ...
- Python实现的数据结构与算法之双端队列详解
一.概述 双端队列(deque,全名double-ended queue)是一种具有队列和栈性质的线性数据结构.双端队列也拥有两端:队首(front).队尾(rear),但与队列不同的是,插入操作在两 ...