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 ...
随机推荐
- 初学 React native | 环境搭建(在模拟器上运行)
我的电脑是windows 所以就以 windows上+Android 配置React native 环境 网上的安装教程非常多,我总结了一下,配置环境时出错原因主要是node java python ...
- 软件工程与UML作业3(互评作业)
博客班级 https://edu.cnblogs.com/campus/fzzcxy/2018SE1/ 作业要求 https://edu.cnblogs.com/campus/fzzcxy/2018S ...
- git代码管理——克隆项目到本地仓库及上传本地项目到仓库
一.克隆项目到本地仓库 1.github网站操作 1.1 登录github 首先创建一个仓库,点击“New” 1.2 输入仓库信息 1.3 创建完成后,会多出一个仓库 2.安装git客户端 2.1 安 ...
- 217。数据中是否有重复元素(哈希表/set简法)
给定一个整数数组,判断是否存在重复元素. 如果任意一值在数组中出现至少两次,函数返回 true .如果数组中每个元素都不相同,则返回 false . 示例 1: 输入: [1,2,3,1] 输出: t ...
- Netty之ChannelOption的各种参数
ChannelOption.SO_BACKLOG, 1024 BACKLOG用于构造服务端套接字ServerSocket对象,标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最 ...
- token认证、JWT
登录的token操作 #app.models.py :表结构 from django.db import models class User(models.Model): user = models. ...
- 【Java并发编程】从CPU缓存模型到JMM来理解volatile关键字
目录 并发编程三大特性 原子性 可见性 有序性 CPU缓存模型是什么 高速缓存为何出现? 缓存一致性问题 如何解决缓存不一致 JMM内存模型是什么 JMM的规定 Java对三大特性的保证 原子性 可见 ...
- ASP.NET Core 基于声明的访问控制到底是什么鬼?
从ASP.NET 4.x到ASP.NET Core,内置身份验证已从基于角色的访问控制(RBAC)转变为基于声明的访问控制(CBAC). 我们常用的HttpContext.User属性ASP.NET ...
- day61:Linux:权限管理&rpm软件包管理&yum工具
目录 1.权限管理 2.rpm软件包管理 3.yum工具(联网) 权限管理 1.什么是权限? 权限主要用来约束用户能对系统所做的操作 2.为什么要使用权限? 因为系统中不可能只存在一个root用户,一 ...
- 配置hive的元数据到Mysql中
在hive的安装目录下,进入conf目录,创建一个hive-site.xml文件 根据官方文档配置参数,拷贝数据到hive-site.xml文件中 https://cwiki.apache.org/c ...