Spring 源码系列
1、Spring 学习之扩展点总结之后置处理器(一)
2、Spring 学习之扩展点总结之后置处理器(二)
3、Spring 学习之扩展点总结之自定义事件(三)
4、Spring 学习之扩展点总结之内置事件(四)
5、Spring 学习之扩展点总结之@Import(五)
6、Spring 学习之AOP总结之基础介绍(六)
7、Spring 学习之AOP总结之实现代理(七)
8、SpringBoot 学习之自动配置基本原理(八)
9、SpringBoot 学习之启动原理(九)
10、ElasticSearch学习随笔之SpringBoot Starter 操作
11、图数据库 Neo4j 学习之SpringBoot整合
12、SpringBoot 学习之常用 Filter / Advice 总结
13、SpringBoot+FastJson 优雅的过滤 Response Body
14、Spring Security + Oauth2 认证授权
文章目录
前言
在 Spring学习之扩展点总结之自定义事件中介绍了什么是 Event事件以及自定义事件的用法,但是还没有完,Spring还提供了四种内置事件供我们使用,本文就主要讲解Spring 内置事件。
一、什么是内置事件?
什么是 Spring 内置事件,在 自定义事件 篇中介绍了 Spring 事件以及自定义事件的用法,在 Spring 内部也整合了不同的事件供我们使用,就是内置事件。
内置事件总共有 四种,如下:
事件名称 | 事件说明 |
---|---|
ContextStartedEvent | 此 Start 事件是当Spring容器启动时发布,即调用 start() 方法是执行,意味着所有Lifecyc Bean 都监听到了 start 事件。 |
ContextRefreshedEvent | 此 Refreshed 事件是当容器实例化时发布,即执行 refresh() 方法,此时所有的 Bean 都已加载,后置处理器被激活,容器中所有的对象就可以使用,如果容器支持热重载,则 refresh 可以被触发多次(XmlWebApplicatonContext支持热刷新,而GenericApplicationContext则不支持) |
ContextStoppedEvent | 此 stoped 事件是当容器停止是发布,即调用 stop() 方法,所有的 Lifecyc Bean 都监听到了 stop 事件,关闭的容器可以通过 start() 重启。 |
ContextClosedEvent | 此 closed 事件和 stop 事件类似,当容器关闭是发布,即调用 close() 方法,意味着容器所有的 Bean 都已被销毁,和 stoped 事件不同点就是,容器在 stop() 之后可以通过 start() 重启,但是 close() 之后则不能重启或 refresh() 。 |
RequestHandledEvent | 此 Request handled 事件只在使用spring的DispatcherServlet时有效,当一个请求被处理完成时发布 |
Spring 内置事件已经提供好了,我们只需要实现监听器即可,内置监听器类图如下:
二、内置事件调用
因为 Spring 提供的内置事件是在 Spring 容器启动过程中执行的,所以我们可以实现完四个事件监听器,对四个内置事件进行监听,然后统一调用。
2.1 ContextStartedEvent
@Component
public class ContextStartedEventListener {
@EventListener(ContextStartedEvent.class)
public void onApplicationEvent(ContextStartedEvent event){
System.out.println("进入ContextStartedEvent事件监听");
if(event.getApplicationContext().getParent() == null){
System.out.println("Spring 容器初始化完成了,执行事件方法。");
System.out.println("================================");
}
}
}
2.2 ContextRefreshedEvent
@Component
public class ContextRefreshEventListener {
@EventListener(ContextRefreshedEvent.class)
public void onApplicationEvent(ContextRefreshedEvent event){
System.out.println("进入ContextRefreshedEvent事件监听");
if(event.getApplicationContext().getParent() == null){
System.out.println("容器加载完毕...头一个事件监听");
System.out.println("================================");
}
}
}
2.3 ContextStoppedEvent
@Component
public class ContextStoppedEventListener {
@EventListener(ContextStoppedEvent.class)
public void onApplicationEvent(ContextStoppedEvent event){
System.out.println("进入ContextStopEvent事件监听");
if (event.getApplicationContext().getParent() == null) {
System.out.println("spring容器停止发布");
System.out.println("================================");
}
}
}
2.4 ContextClosedEvent
@Component
public class ContextClosedEventListener {
@EventListener(ContextClosedEvent.class)
public void onApplicationEvent(ContextClosedEvent event){
System.out.println("进入ContextClosedEvent事件监听");
System.out.println("spring 容器关闭......");
System.out.println("================================");
}
}
2.5 初始化容器测试
在main 方法中实例化容器(AnnotationConfigApplicationContext),这里定义的类型也是 AnnotationConfigApplicationContext 类型的,如果定义成了 ApplicationContext 上下文类型,是调用不了 start、stop、close 这几个方法的,因为 Spring 的内置事件是单独 EventListener 接口。
@ComponentScan("com.self.test.event")
@EnableAsync
public class MainStartApp {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainStartApp.class);
applicationContext.start();
applicationContext.stop();
applicationContext.close();
}
}
执行测试结果:
从测试结果中可以看到,这四个内置事件都执行了。
注意:并没有在 main 方法中调用 refresh() 方法,也执行了 ContextRefreshedEvent 事件,那是因为 Spring 在启动容器是内部就调用了 refresh() 方法。
三、内置事件监听器的异步调用
其实在自定义事件篇中也讲到过异步使用,内置事件当然也可以用 @Async 注解来实现异步执行。
@Component
public class ContextStoppedEventListener {
@Async
@EventListener(ContextStoppedEvent.class)
public void onApplicationEvent(ContextStoppedEvent event){
System.out.println("进入ContextStopEvent事件监听");
if (event.getApplicationContext().getParent() == null) {
System.out.println("spring容器停止发布");
System.out.println("================================");
}
}
}
四、内置事件监听器的具体使用
上面说到了 Spring 的四个内置使用如何调用,可以说是简单便捷,但是具体的没事内置事件中如何使用Spring 提供的功能,我们来简单研究一下,以 ContextRefreshedEvent 为例,因为这个事件的监听器在 Spring 启动的时候就会调用到,代码如下:
1、新建一个 UserService 服务类,并且提供 init() 的初始化方法。
@Component
public class UserService {
public void init(){
System.out.println("执行 UserService 初始化方法");
}
}
2、在 ContextRefreshedEvent 事件中通过上下文获取到 UserService 服务实体并且调用 init() 方法。
@Component
public class ContextRefreshEventListener {
@EventListener(ContextRefreshedEvent.class)
public void onApplicationEvent(ContextRefreshedEvent event){
System.out.println("进入ContextRefreshedEvent事件监听");
if(event.getApplicationContext().getParent() == null){
System.out.println("容器加载完毕...头一个事件监听");
System.out.println("================================");
}
UserService userService = event.getApplicationContext().getBean(UserService.class);
userService.init();
System.out.println("UserServiceImpl:" + userService);
}
}
3、执行测试
从测试结果中我们可以看到,在 ContextRefreshedEvent 的监听器中,我们可以通过 event.getApplicationContext() 获取到容器上下文,然后通过 getBean() 方法获取到 UserService 的 Bean 实例,然后就可以调用 UserService.init() 方法去做一下业务逻辑了,在项目中这样的操作很多;
也可以通过上下文对获取到的 Bean 进行修改操作,类似于Spring提供的后置处理器。
四、总结
本文对 Spring 的四个内置事件实现进行了讲解,使用注解的方式使用起来可以说是简单快捷,通过内置事件提供的 ContextXxxxxEvent 参数我们可以获取到容器的上下文,就可以做很多操作了,比如在项目启动的时候进行常用缓存的加载,或者是缓存清理操作,或者是启动日志打印等等。
其实有些功能用起来很方便,也很简单,在此记录一下可以加深印象,自己亲手总结,测试写出来的东西,自己看的最明白,自己也记得最牢固,也有点成就感,万一火了呢。