spring boot 2.1.X 学习之路——监听器配置
- 前言
- Spring Boot的几个常见事件
- 配置方式
- 代码示例
- 实现`ApplicationListener`类
- 注册监听事件
- 执行
- 添加其他监听事件(单容器)
- 添加其他监听事件(多容器)
- 执行
- 自定义事件
- 配置方式
- 代码示例
- 执行
前言
在使用Spring Boot的时候,我们想要在触发特定的时间与事件,去做些特定的事情,这个时候就用到了监听器,所谓的监听器,就是一个专门用于对其对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时,立即采取相应的行动,就好比火警报警器一样,当有烟雾发生的时候,就会触发报警器鸣响,从而通知人们此处可能发生明火或者有火灾隐患,需要及时处理。
Spring Boot的几个常见事件
Spring Boot在启动过程中增加事件监听机制,为用户功能拓展提供极大的便利,Spring Boot支持的几个常见事件有(按加载顺序):
- ApplicationStartingEvent :应用开始运行事件,在调用其
run()
方法后执行,在该事件中可以获取到SpringApplication
对象,可做一些执行前的设置,SpringApplication application = applicationStartingEvent.getSpringApplication();
- ApplicationEnvironmentPreparedEvent :应用环境准备时触发,Spring Boot 对应
Enviroment
已经准备完毕,但此时上下文context
还没有创建。在该监听中获取到ConfigurableEnvironment
后可以对配置信息做操作,例如:修改默认的配置信息,增加额外的配置信息等等 - ApplicationPreparedEvent:上下文
context
准备时触发,但此时spring中的bean是没有完全加载完成的。在获取完上下文后,可以将上下文传递出去做一些额外的操作。值得注意的是:在该监听器中是无法获取自定义bean并进行操作的 - ContextRefreshedEvent:当
ApplicationContext
被初始化或刷新触发 ,此时,bean实例化完成 - ApplicationStartedEvent:应用启动完成事件,可在此处运行特定的程序,如定时等
- ApplicationReadyEvent:应用启动完成进入就绪状态事件
- ApplicationFailedEvent:应用启动失败后事件,在这里可以配置启动失败机制,比如回收资源等操作,优雅的处理异常
- ContextClosedEvent:应用关闭事件,可以添加虚拟机对应的钩子进行资源的回收与释放
配置方式
- 添加类并继承
ApplicationListener<E extends ApplicationEvent>
类 - 在主方法中添加监听器
app.addListeners(new ApplicationFailedEventListener());
- 以bean的方式注入进去,只需要在监听类加上注解
@Configuration
即可,不需要在主方法中进行addListeners()
操作
代码示例
实现ApplicationListener
类
import org.springframework.boot.context.event.ApplicationStartingEvent;
import org.springframework.context.ApplicationListener;/*** @author YML* @date 2019/11/28 10:24*/
public class ApplicationStartingEventListener implements ApplicationListener<ApplicationStartingEvent> {@Overridepublic void onApplicationEvent(ApplicationStartingEvent applicationStartingEvent) {System.out.println("ApplicationStartingEvent=====项目开始启动监听执行。。。。");}
}
注册监听事件
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class SystemApplication {public static void main(String[] args){SpringApplication app = new SpringApplication(SystemApplication.class);app.addListeners(new ApplicationEnvironmentPreparedEventListener());app.addListeners(new ApplicationStartingEventListener());app.run(args);}
}
执行
添加其他监听事件(单容器)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;/*** @author YML* @date 2019/10/18 14:01*/
@SpringBootApplication
@MapperScan(value = {"com.dream.system.dao"})
public class SystemApplication {public static void main(String[] args){SpringApplication app = new SpringApplication(SystemApplication.class);app.addListeners(new ApplicationStartingEventListener());app.addListeners(new ApplicationListener<ApplicationPreparedEvent>(){@Overridepublic void onApplicationEvent(ApplicationPreparedEvent event) {SpringApplication application = event.getSpringApplication();application.setBannerMode(Banner.Mode.OFF);System.out.println(event.getClass().getSimpleName() + "事件监听!!");}});//jdk8以后可以用lambda表达式app.addListeners((ApplicationListener<ApplicationEnvironmentPreparedEvent>) event ->{System.out.println(event.getClass().getSimpleName() + "事件监听!!");});app.addListeners((ApplicationListener<ApplicationPreparedEvent>) event ->{System.out.println(event.getClass().getSimpleName() + "事件监听!!");});app.addListeners((ApplicationListener<ApplicationStartedEvent>) event ->{System.out.println(event.getClass().getSimpleName() + "事件监听!!");});app.addListeners((ApplicationListener<ApplicationReadyEvent>) event ->{System.out.println(event.getClass().getSimpleName() + "事件监听!!");});app.addListeners((ApplicationListener<ContextRefreshedEvent>) event ->{System.out.println(event.getClass().getSimpleName() + "事件监听!!");});app.addListeners((ApplicationListener<ContextClosedEvent>) event ->{System.out.println(event.getClass().getSimpleName() + "事件监听!!");});app.run(args);}
}
添加其他监听事件(多容器)
在web 项目中(spring mvc),系统会存在两个容器,一个是root application context ,另一个就是我们自己的 projectName-servlet context(作为root application context的子容器)。
这种情况下,就会造成onApplicationEvent方法被执行两次。为了避免上面提到的问题,我们可以只在root application context初始化完成后调用逻辑代码,其他的容器的初始化完成,则不做任何处理,修改后代码如下
app.addListeners((ApplicationListener<ContextClosedEvent>) event ->{//root application context 没有parent,他就是老大if (event.getApplicationContext().getParent() == null){System.out.println(event.getClass().getSimpleName() + "事件监听!!");}});
执行
主要看执行事件顺序
自定义事件
配置方式
- 创建一个事件类
MyEvent
并继承ApplicationEvent
,默认是需要重写其构造函数的 - 添加自定义监听器
代码示例
1、创建事件类
package com.dream.system.configuration;import org.springframework.context.ApplicationEvent;/*** @author YML* @date 2019/11/28 14:48*/
public class MyEvent extends ApplicationEvent {private String msg;/*** 构造函数,除了第一个参数不能动,其他的可以自行添加*/public MyEvent(Object source,String msg) {super(source);this.msg = msg;}/*** 当事件触发后执行的自定义方法*/public void printMsg(){System.out.println(this.msg);}
}
2、创建监听器
package com.dream.system.configuration;import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;/*** @author YML* @date 2019/11/28 14:59*/
@Configuration
public class MyEventListener implements ApplicationListener<MyEvent> {/*** 当监听到事件被触发时,执行事件的自定义方法*/@Overridepublic void onApplicationEvent(MyEvent myEvent) {myEvent.printMsg();}
}
3、发布。为了验证效果,定义了一个类,并在项目启动完成后立即执行
import com.dream.system.configuration.MyEvent;
import org.springframework.context.ApplicationContext;import javax.annotation.PostConstruct;
import javax.annotation.Resource;/*** @author YML* @date 2019/10/30 10:21*/
@Component
public class MyEventService {/** 上下文 */@Resourceprivate ApplicationContext applicationContext;@PostConstructpublic void publish(){//通过上下文对象发布监听applicationContext.publishEvent(new MyEvent(this,"哈哈哈"));}
}