- 一、java监听机制
- 二、SpringBoot 监听机制
- 三、环境搭建
- 1、创建springboot项目
- 2、实现上述四个接口
- MyApplicationContextInitializer
- MyApplicationRunner
- MyCommandLineRunner
- MySpringApplicationRunListener
- 四、分析`MyApplicationRunner` 和`MyCommandLineRunner`
- 相同点:
- 不同点:
- 作用:
- 两个方法的参数有何区别
- 五、分析`MyApplicationContextInitializer` 和`MySpringApplicationRunListener`
springboot的监听机制,其实就是对java提供监听机制的封装。
为此我们可以先了解一下java的监听机制:
Java中的事件监听机制定义了以下几个角色:
- 事件(Event):要求所有的event继承 java.util.EventObject 类的对象
- 事件源(Source):可以监听任意对象Object、属性的创建和销毁,等 一些生命周期的变化情况。
- 监听器(Listener):要求所有的监听器实现 java.util.EventListener 接口的对象
之所以说springboot就是对java监听机制的封装,是因为SpringBoot 在项目启动时,会对几个监听器进行回调,我们可以实现这些监听器接口,在项目启动时完成一些操作。
今天我们主要介绍这四个:
- ApplicationContextInitializer
- SpringApplicationRunListener
- CommandLineRunner
- ApplicationRunner
接下来我们通过代码来演示效果。
pom文件为:
4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.8.RELEASE com.example springboot-listener 0.0.1-SNAPSHOT springboot-listener Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin
创建启动类:
package com.example.springbootlistener;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringbootListenerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootListenerApplication.class, args);
}
}
2、实现上述四个接口
代码结构为:
下面依次列出各个实现类:
MyApplicationContextInitializerpackage com.example.springbootlistener.listener;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("ApplicationContextInitializer....initialize");
}
}
MyApplicationRunner
package com.example.springbootlistener.listener;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("ApplicationRunner...run");
}
}
MyCommandLineRunner
package com.example.springbootlistener.listener;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("CommandLineRunner...run");
}
}
MySpringApplicationRunListener
package com.example.springbootlistener.listener;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.stereotype.Component;
@Component
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
@Override
public void starting() {
System.out.println("starting...项目启动中");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println("environmentPrepared...环境对象开始准备");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("contextPrepared...上下文对象开始准备");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("contextLoaded...上下文对象开始加载");
}
@Override
public void started(ConfigurableApplicationContext context) {
System.out.println("started...上下文对象加载完成");
}
@Override
public void running(ConfigurableApplicationContext context) {
System.out.println("running...项目启动完成,开始运行");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("failed...项目启动失败");
}
}
另外,我们在每个实现类上都加入了@Component 注解,主要目的就是为了将上面四个实现类注入到ioc容器中。
下面我们启动一下项目,看一下日志:
通过日志我们可以发现,仅仅有两个监听器MyApplicationRunner 和MyCommandLineRunner生效了。另外两个没有生效执行,是因为MyApplicationContextInitializer 和MySpringApplicationRunListener 需要配置才能生效。
MyApplicationRunner
MyCommandLineRunner
通过图片上的代码我们可以看出来,这两个接口中的方法基本是一样的,包括方法名,返回值,执行时间等,执行时间都是在项目启动后,唯一不一样的就是参数类型。
都是在项目启动后执行。
不同点:方法参数不一样。
作用:因为都是在项目启动完成后执行,并且是被自动调用。那么我们可以在项目启动后执行一些方法,比如加载数据缓存(将数据库数据加载到redis),设置初始值,初始化数据库等。
两个方法的参数有何区别我们先打印一下两个方法的参数:
MyApplicationRunner
MyCommandLineRunner
再次启动项目,查看日志:
均显示为空。
我们可以在idea 中设置值:
启动项目,查看日志:
为此,我们发现这两个接口并没直接的区别,我们在项目中需要的话,任选其一就好了。
首先让这个监听器生效的话,需要我们配置一下:spring.factories
我们需要在resources下创建meta-INF,然后再meta-INF 下创建spring.factories。
那么应该如何配置呢,比如key和value应该是什么呢。
举个例子,MyApplicationContextInitializer是实现的ApplicationContextInitializer接口,那么我们就复制ApplicationContextInitializer接口的全路径名作为key值,MyApplicationContextInitializer的全路径名为value值。
为此我们先配置MyApplicationContextInitializer,配置如下:
org.springframework.context.ApplicationContextInitializer=com.example.springbootlistener.listener.MyApplicationContextInitializer
启动项目,查看日志:
我们可以看到这个监听器是在图表banner打印之后,容器加载之前执行的,为此我们可以在这个监听器中做一些检查工作,比如检查资源是否存在等。
接下来我按照上面的方式配置MySpringApplicationRunListener。
org.springframework.context.ApplicationContextInitializer=com.example.springbootlistener.listener.MyApplicationContextInitializer org.springframework.boot.SpringApplicationRunListener=com.example.springbootlistener.listener.MySpringApplicationRunListener
启动项目,查看日志:
我们可以看到报错的原因是,没有init的匹配方法。这是一个构造方法,需要两个参数,参数一为:SpringApplication,参数二为:字符串。
我可以进入接口SpringApplicationRunListener 中,看一下他的实现类:
我们发现有两个实现类,一个是我们自己实现的,一个是spring实现的,我们进入spring的实现类EventPublishingRunListener。
发现实现类EventPublishingRunListener中有个一个构造方式,有两个参数,分别是SpringApplication 和字符串数组,,其实参数SpringApplication就是我们项目的启动事件源。为此我们也可以在自己的监听器MySpringApplicationRunListener中增加一个构造函数:
package com.example.springbootlistener.listener;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.stereotype.Component;
@Component
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
public MySpringApplicationRunListener(SpringApplication application, String[] args) {
}
.....省略
}
此时代码是报错的,是因为我们上面增加 @component注解的,所以要注入application和args。为此我们将@component注解删除就可以了。然后启动项目查看日志:
根据日志,大家可以探索一下springboot的启动生命周期的东西,在对应的时机实现自己的功能业务。。
springboot定义的事件我们可以在springboot包下找到:



