事件驱动模型开发
一. 监听器与事件与发布事件
概述
-
什么是事件驱动模型开发: 由Spring的: 事件,监听器, 播放器三大部分构成,Spring底层提供了播放器, 只需要发布事件,监听器监听指定事件进行指定的业务操作,主要是为系统业务进行解耦,提高扩展性,维护性(实际开发中像小应用很少使用这种模式,大应用又有MQ,主要是Spring底层有利于事件监听来做扩展),
主要的核心机制是观察者模式
,监听器对应观察者模式中的观察者,发布的事件对应被观察者 - 监听器: ApplicationListener< E extends ApplicationEvent> ,监听 Spring 容器中发布的事件,
ApplicationListener< E extends ApplicationEvent>是一个接口,该接口中有onApplicationEvent(E var1) 该方法就是触发事件后的回调方法,
- 什么叫发布事件: 先简单理解为通过 AnnotationConfigApplicationContext IOC 容器对象调用 publishEvent() 方法,向方法中传递一个 ApplicationEvent 类型的对象,就是发布事件, 自定义发布事件示例
@org.junit.Test
public void test(){
//创建ioc容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
//创建 ApplicationEvent 事件对象,由于ApplicationEvent是一个抽象类,此处使用匿名类的方法创建
ApplicationEvent event = new ApplicationEvent(new String("aaa")){
};
//通过 IOC 容器对象调用 publishEvent() 发布事件
context.publishEvent(event);
}
- 事件: ApplicationEvent 在代码层面看就是 ApplicationListener 接口中的泛型对象,监听器监听这个类型的事件是否发布触发,不同的监听器根据泛型中设置的事件类型不同,监听不同的事件
-
Spring 关于容器的,默认提供了,继承ApplicationEvent 接口的,容器刷新事件,容器开始事件,容器停止事件,容器关闭事件,也有对应事件的监听器
二. 通过 Spring 提供的容器刷新事件查看原理
发布事件的原理
-
Spring启动创建IOC容器时调用 refresh() 方法,该方法中调用了this.finishRefresh() 方法,会执行一个创建容器事件
-
查看 finishRefresh() 方法,该方法中调用了publishEvent() ,创建了ContextRefreshedEvent 刷新容器事件对象,传递给了 publishEvent() 方法, ContextRefreshedEvent 继承了 ApplicationContextEvent 接口是一个事件对象,
此处调用的publishEvent() 方法就是发布事件
- 查看 publishEvent() 方法,事件的派发,获取 ApplicationEventMulticaster 多播器,通过多播器将事件派发给对应的监听器
protected void publishEvent(Object event, ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (this.logger.isTraceEnabled()) {
this.logger.trace("Publishing event in " + this.getDisplayName() + ": " + event);
}
Object applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent)event;
} else {
applicationEvent = new PayloadApplicationEvent(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
}
}
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
} else {
//1.调用 getApplicationEventMulticaster() 方法获取一个ApplicationEventMulticaster"多播器"也叫派发器
//2.通过多播器调用 multicastEvent() 方法执行派发事件
this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
}
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
} else {
this.parent.publishEvent(event);
}
}
}
- 查看 multicastEvent() 方法
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
//获取所有通过发布的事件对象获取对应的 ApplicationListener 监听器
Iterator var4 = this.getApplicationListeners(event, type).iterator();
//遍历获取到的监听器
while(var4.hasNext()) {
final ApplicationListener<?> listener = (ApplicationListener)var4.next();
Executor executor = this.getTaskExecutor();
//判断是否可以异步执行,如果可以则使用多线程方式进行异步执行
if (executor != null) {
//多线程方式异步执行
executor.execute(new Runnable() {
public void run() {
SimpleApplicationEventMulticaster.this.invokeListener(listener, event);
}
});
} else {
//同步执行
this.invokeListener(listener, event);
}
}
}
- 同样,例如在调用close() 方法关闭容器时,在该方法中也是调用了publishEvent() 方法,传递了一个关闭事件,获取多播器,通过多播器,找到对象的监听器,执行监听器中的逻辑
IOC 容器注册多播器
- 发布事件时通过多播器,将事件传递给对应的监听器,进而调用监听器中的方法,实现该事件要完成的功能,我们也可以自定义多播器,指定多播器使用的 Executor,进而实现事件执行的同步或异步功能
-
多播器的注册过程: 在Spring启动调用refresh() 方法后,该方法中调用了 initApplicationEventMulticaster()在创建其它 bean 对象之前通过该方法初始化 ApplicationEventMulticaster 多播器
- 查看 initApplicationEventMulticaster() 方法默认会创建SimpleApplicationEventMulticaster事件多播器注入到容器中
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
//1.通过BeanFactory 在IOC 容器中寻找有没有 id 为"applicationEventMulticaster"的bean,这个bean 就是多播器
if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
//如果有,直接通过id 在容器你中获取
this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
} else {
//如果没有new 一个多播器
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
//将new 出来的多播器注册到容器中
beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [" + this.applicationEventMulticaster + "]");
}
}
}
IOC 容器注册监听器
-
在创建 IOC 容器时 调用 refresh() 方法,该方法内部调用 this.finishRefresh() 刷新容器发布创建容器事件以前调用了 registerListeners() 方法,注册监听器
- 查看 registerListeners() 注册监听器的方法
- 获取所有监听器到ApplicationListener
- 遍历监听器注册到多播器中
protected void registerListeners() {
//1.获取 ApplicationListener 监听器迭代器(第一次执行时可能没有)
Iterator var1 = this.getApplicationListeners().iterator();
//如果有遍历获取每一个监听器,将监听器注册到多播器中
while(var1.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var1.next();
//将监听器注册到多播器中
this.getApplicationEventMulticaster().addApplicationListener(listener);
}
//如果没有,通过类型,在容器中获取所有监听器的名字
String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
String[] var7 = listenerBeanNames;
int var3 = listenerBeanNames.length;
//注册监听器,将将监听器注册到多播器中
for(int var4 = 0; var4 < var3; ++var4) {
String listenerBeanName = var7[var4];
this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
Iterator var9 = earlyEventsToProcess.iterator();
while(var9.hasNext()) {
ApplicationEvent earlyEvent = (ApplicationEvent)var9.next();
this.getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
三. 自定义事件驱动模型开发
-
步骤:
- 创建自定义事件对象(实现 ApplicationEvent 接口)
- 创建与自定义事件对应的监听器,
- 实现 ApplicationListener 接口 方式:重写接口中的 onApplicationEvent() 回调方法编写监听到对应的实际后要做的逻辑功能,接口中的泛型中指定要监听的事件类型
- 还有使用@EventListener注解方式: 该方式修饰方法,任何方法都可以监听事件,通过该注解的class属性值指定该方法要监听的事件类型”@EventListener(classes=事件Event.class)”
- 设置自定义监听器注入到容器中
- 接受请求执行发布自定义事件的逻辑代码
- 创建自定义事件
import org.springframework.context.ApplicationEvent;
//自定义事件,继承 ApplicationEvent 接口,事件一般都会传递数据
//在发生事件后,监听器通过事件对象获取要处理的数据,有个带参构造
//通过带参构造传递数据,对属性赋值
public class MyApplicationEvent extends ApplicationEvent {
//这个变量可以看为事件要处理的数据
public Object source;
public MyApplicationEvent(Object source) {
super(source);
this.source = source;
}
@Override
public Object getSource() {
return source;
}
}
- 创建自定义监听器
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
//不要忘记将自定义监听器注入到容器中
@Component
public class MyApplicationListener <MyApplicationEvent>implements ApplicationListener {
//实现 ApplicationListener 接口,泛型中指定要监控的事件类型
//重写onApplicationEvent()方法编写事件发生后要执行的逻辑
//当向容器中发布事件时,,会触发该方法
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
//获取事件中保存的数据
applicationEvent.getSource();
System.out.println("我是对应 MyApplicationEvent 事件的监听器,执行...");
}
}
- 触发事件
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
//不要忘记注入到容器中
@Component
public class PublishEvent implements ApplicationContextAware {
//通过继承 ApplicationContextAware 接口的方式,
//在启动 Spring 时,自动获取Spring创建的IOC容器,赋值给,当前类中的属性
//通过 ApplicationContext 调用 publishEvent() 方法发布事件使用
private ApplicationContext context;
//重写 ApplicationContextAware 接口中的 setApplicationContext() 方法拿到 Spring 自己创建的 IOC 容器
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
//通过该方法可以触发事件
public void publishTest(){
System.out.println("我被触发,我要发布事件");
//创建事件对象,设置事件数据
MyApplicationEvent event = new MyApplicationEvent(new String("我是事件数据"));
//通过 IOC 容器调用 publishEvent() 方法发布事件
context.publishEvent(event);
}
}
@EventListener 设置监听方法注解
通过方法监听事件示例
//使用注解方式,设置监听事件的方法(该方法的类不要忘记注入到容器中)
@EventListener(classes = {MyApplicationEvent.class})
public void listenerMethod(MyApplicationEvent event){
System.out.println("我是监听方法,监听 MyApplicationEvent 事件");
//获取事件中的数据
System.out.println(event.source);
}
四. 了解 SmartInitializingSingleton
-
SmartInitializingSingleton 是一个接口,该接口中有一个 afterSingletonsInstantiated() 抽象方法
该方法在什么时候执行
: 当创建 IOC 容器时,调用 refresh() 方法,在refresh() 方法中调用 finishBeanFactoryInitialization(beanFactory) 方法,该方法中又调用了preInstantiateSingletons(),实现对单实例 bean 的创建初始化,当所有注意,是所有 bean 创建初始化完成以后,遍历所以bean的名字获取出来,判断这个bean是否是 SmartInitializingSingleton 类型,如果是执行 SmartInitializingSingleton 中的 afterSingletonsInstantiated() 方法
注意点: 是所有 bean 创建初始化完成后,再遍历执行这个 afterSingletonsInstantiated() 方法的
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Pre-instantiating singletons in " + this);
}
//==================1.获取所有 bean 的名字===================
List<String> beanNames = new ArrayList(this.beanDefinitionNames);
Iterator var2 = beanNames.iterator();
while(true) {
while(true) {
String beanName;
RootBeanDefinition bd;
//========do while循环,查看下面的条件,判断所有bean是否全部初始化完成===================================
do {
do {
do {
if (!var2.hasNext()) {
var2 = beanNames.iterator();
//=========2.遍历获取到的 bean 的名字======================
while(var2.hasNext()) {
beanName = (String)var2.next();
//=======3.通过获取到额benaName,去容器中查找这个bean=================
Object singletonInstance = this.getSingleton(beanName);
//=====4.判断这个bean是否是SmartInitializingSingleton类型====================
if (singletonInstance instanceof SmartInitializingSingleton) {
//===========5.如果是转换回来=====================
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton)singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
smartSingleton.afterSingletonsInstantiated();
return null;
}
}, this.getAccessControlContext());
} else {
//6.=======执行afterSingletonsInstantiated ()方法===================
smartSingleton.afterSingletonsInstantiated();
}
}
}
return;
}
beanName = (String)var2.next();
bd = this.getMergedLocalBeanDefinition(beanName);
} while(bd.isAbstract());
} while(!bd.isSingleton());
//========判断所有bean是否全部初始化完成===================================
} while(bd.isLazyInit());
if (this.isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean)this.getBean("&" + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = (Boolean)AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
return ((SmartFactoryBean)factory).isEagerInit();
}
}, this.getAccessControlContext());
} else {
isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean)factory).isEagerInit();
}
if (isEagerInit) {
this.getBean(beanName);
}
} else {
//=在Spring容器首次启动时,前面的都没有,会首先走这里创建所有的bean,初始化============
this.getBean(beanName);
}
}
}
}
SmartInitializingSingleton 使用示例
@EventListener 注解设置监听方法,底层就是通过这个接口,对该接口中的方法在 EventListenerMethodProcessor 子类中进行了实现
五. 异步发布事件的核心机制是什么
- 利用多线程,在发布事件时使用@Async,或使用taskExecutor来实现
-
以taskExecutor为例,通过EventMulticaster调用setTaskExecutor()方法,设置SimpleAsyncTaskExecutor
-
当我们调用publishEvent()发布事件后,底层会判断是否存在异步多播也就是taskExecutor,如果存在会通过Executor创建异步线程,进行异步发布
版权声明:本文为qq_29799655原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。