spring的ApplicationEvent事件通知机制原理与应用
spring框架抽象出了一套事件机制,通过发布,订阅方式来实现,类似观察者模式。先来搭建一个基本的spring环境,然后做个简单的案例演示事件的应用,最后在做源码分析。
1.环境搭建
引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
创建一个配置类和启动类
@Configuration
public class AppConfig {
}
public class TestSpring {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
}
}
简单的环境搭建完毕。
2.案例演示
熟悉发布-订阅模型都清楚,整个模型肯定要包括3种角色,发布者,订阅者(也叫监听者),事件。这三个角色有什么作用呢?
-
发布者:触发事件的角色,触发以后,所有监听,也叫订阅该事件的监听者都会收到通知,发布者不需要知道有多少个监听者存在。
-
订阅者:监听某个事件的发生,一旦发生事件,执行相应处理逻辑。
-
事件:代表事件的实体,这个事件实体通常会包含某些信息,发布者将信息包装到事件内,订阅者可以获取该信息,达到传递信息的作用
spring本身定义了一个发布事件的方法,可以看成是发布者。
订阅者在spring容器内叫做ApplicationListener,是一个泛型接口,用户可以自己定义某一事件的订阅者,注册到容器中。
spring内部定义了一个ApplicationEvent抽象类,所有spring管理的事件都实现了ApplicationEvent类,他的一个子类ApplicationContextEvent也是一个抽象类,spring又定义了几个ApplicationContextEvent的子类,用于代表spring容器生命周期的典型事件,包括ContextStartedEvent,ContextRefreshedEvent,ContextStoppedEvent,ContextClosedEvent,发布这些容器生命周期事件的方法嵌入在容器的整个生命周期中。spring的实现原来在后面的原理分析中详细讲解。
发布者,订阅者spring已经有自己的实现机制,这里我们自定义一个实现ApplicationEvent的事件类,用于测试。
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
spring的事件机制有两种使用方式,分别是ApplicationListener接口方式和基于EventListener注解方式。
2.1 实现ApplicationListener接口方式
ApplicationListener是一个泛型接口,我们实现该接口,定义一个监听器。
@Component
public class MyApplicationListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println("MyEvent事件发生");
System.out.println("收到信息:" + event.getSource());
}
}
在TestSpring启动类的main方法中加入下面两行代码
MyEvent myEvent = new MyEvent(context);
context.publishEvent(myEvent);
启动TestSpring类的main方法,显示结果
MyEvent事件发生
收到信息:org.springframework.context.annotation.AnnotationConfigApplicationContext@2e0fa5d3, started on Thu Feb 04 09:47:23 CST 2021
2.2 EventListener注解方式
创建一个测试类AnnotationEventTest,类中定义一个方法,注意方法必须有形参,不然会报错,方法添加EventListener注解,这就相当于监听了MyEvent事件的处理方法。
@Component
public class AnnotationEventTest {
@EventListener
public void test(MyEvent event)
{
System.out.println("事件测试:"+event);
}
}
启动TestSpring类的main方法,显示结果:
事件测试:com.li.study.event.MyEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e0fa5d3, started on Fri Feb 05 08:22:11 CST 2021]
这种基于EventListener注解的方式,省掉了定义监听器类,用起来更方便。
很多人会有疑问,spring为什么会做这种事件通知机制?我理解主要为了解耦,事件的发布者不需要知道有多少监听者在监听该事件的发生。有人说这种功能也可以通过依赖注入的方式实现,把监听逻辑的实现类注入到发布事件的对象中,直接通过方法调用即可。我只能说,呃。。。你说的对,太郭敬明了。
3.原理分析
其实这个实现原理大家仔细一想,也很简单,无非就是spring自动识别所有的事件监听器,不管是实现ApplicationListener还是添加EventListener注解的方法,将所有的监听器注入到容器,每个监听器绑定了一个事件类型,ApplicationListener通过泛型的参数类型绑定,EventListener注解通过方法形参绑定。当容器触发事件时,遍历所有监听器,找到类型匹配的事件监听器,执行监听方法。
下面就从源码角度来分析spring的实现方式,分为两个主要步骤:事件监听器的自动注册和发布事件
3.1 事件监听器的注册原理
前面也提到,spring的事件监听器有两种形式,实现ApplicationListener接口和标记EventListener注解的方法。
3.1.1 实现ApplicationListener接口的事件监听器发现
自动注册ApplicationListener接口的实现类,实现原理是BeanPostProcessor后置处理器,后置处理器的实现原理参考BeanPostProcessor后置处理器原理与应用
限于篇幅,这里不详细说明。在上面的案例调用AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);时,会启动该并刷新容器,会执行AbstractApplicationContext类的refresh() 方法,在refresh() 方法中会执行很多复杂的逻辑,这个方法时spring的合新方法。其中有一行代码prepareBeanFactory(beanFactory);我们查看一下,在代码中有一行 beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));就是向spring容器中注册了一个发现事件监听器的BeanPostProcessor。
//省略无关代码
protected void prepareBeanFactory(ConfigurableListableBeanFactory bean