SpringApplicationRunListeners系统运行监听器,监听者springboot项目运行各个生命周期,这节讲解starting,用于系统很早的初始化扩展,如果不想看细节,直接想了解SpringApplicationRunListeners各个周期的朋友,直接划到文章末尾,有对各个周期的详细注释,它的周期是围绕着项目启动来做的。
在【springboot源码解读系列】(一、springboot创建SpringApplication实例,定制SpringApplication)中我们讲到了,在创建SpringApplication实例的时候,构造函数中有如下两段代码:
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
分别是设置系统启动器和设置监听器,而他们的原理就是通过SpringFactoriesLoader来实现的,然后将加载的存放在SpringApplication的属性中,之前没有讲解具体的实现。
今天来具体将一下getRunListeners方法获取所有的SpringApplicationRunListener(启动监听器,用于非常早的初始化),和设置启动器和系统监听器原理一模一样,调用的方法都是一样的。
// 所有的系统启动监听器都存放在listeners对象中,这里只暴露出来一个对象,将集合放在实例中。
// 调用的时候直接调用一个方法,封装得特别好
SpringApplicationRunListeners listeners = getRunListeners(args);
此方法在run方法体中,在实际启动开始之前。
public ConfigurableApplicationContext run(String... args) {
// 创建一个简单的秒表实例(其实并不简单)
StopWatch stopWatch = new StopWatch();
// 开始计时,内部使用的是System.nanoTime(),纳秒级别的
stopWatch.start();
// 声明一个上下文实例:context,并且复制为null
ConfigurableApplicationContext context = null;
// 创建一个SpringBootExceptionReporter集合。他是用于定制异常报告的,
// 简单点来说,我们可以对抛出的异常进行定制化输入,
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 设置无头属性:java.awt.headless,
// 该应用程序,即使没有检测到显示器,也允许其启动.
// 对于服务器来说,是不需要显示器的,所以要这样设置.
configureHeadlessProperty();
// 获取所有的spring应用启动监听器,同样也是通过SpringFactoriesLoader获取的监听器
// 并将其方式SpringApplicationRunListeners中的listeners属性中,他是使用final修饰的,
// 在调用SpringApplicationRunListeners构造函数的时候赋值的。赋值之后不能修改
SpringApplicationRunListeners listeners = getRunListeners(args);
// 调用所有监听器的starting方法,通知各个监听器,spring开始启动了。要干事情的准备开干了
listeners.starting();
...
return context;
}
看getRunListeners方法:
// 获取所有的SpringApplicationRunListener的实例
private SpringApplicationRunListeners getRunListeners(String[] args) {
// 创建一个Class数组,里面包含SpringApplication和String[]的Class,具体是干什么的,还不清楚
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// 这里分两步走
// 1、调用getSpringFactoriesInstances方法。获取所有的SpringApplicationRunListener实现类的实例,
// 2、创建SpringApplicationRunListeners实例,并将所有的自定义应用启动监听器放入listeners属性中
// 然后将其返回
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
第一步:调用getSpringFactoriesInstances方法。获取所有的SpringApplicationRunListener实现类的实例,
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
// 获取ClassLoader类加载器
ClassLoader classLoader = getClassLoader();
// 使用名称,并确保唯一,所以使用Set集合。因为加载的是全限定类名
// 如果名称重复,那么就会创建多个实例,最后导致同一个监听器调用两次的情况
// SpringFactoriesLoader是spring.core.io包下面的一个加载器
// 后面讲解关于SpringFactoriesLoader的
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 获取到所有的names,然后根据反射创建实例。
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 对所有的实例进行排序(根据类上面的Order注解进行排序)
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
通过反射创建所有的实例createSpringFactoriesInstances。
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
// 创建一个实例集合
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
// 通过反射获取Class
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
// 判断Class是否数据传进来的类型。如果不是,则报错。
Assert.isAssignable(type, instanceClass);
// 通过获取构造器,parameterTypes : SpringApplication.class, String[].class
// 通过getDeclaredConstructor获取构造器,可以根据构造器创建有参或者无参的实例
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
// 通过BeanUtils获取对象实例
// BeanUtils是spring.bean下面的一个工具类
T instance = (T) BeanUtils.instantiateClass(constructor, args);
// 将创建的实例加入到实例集合中
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
2、创建SpringApplicationRunListeners实例,并将所有的自定义应用启动监听器放入listeners属性中
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
获取完之后调用listeners.starting();方法通知所有的监听器:
此方法为run方法第一次启动时立即调用的,可以用于非常早期的初始化。
void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
starting方法是SpringApplicationRunListener接口的默认方法,如果实现的话,就调用其实现的,如果没有实现,那么他就调用默认空方法。
public interface SpringApplicationRunListener {
/**
*
* 当run方法第一次启动时立即调用。可以用于非常早期的初始化。
*/
default void starting() {
}
}
SpringApplicationRunListener 接口中有比较多的生命周期函数提供给我们扩展,我们可以在各个周期对自己的项目进行定制扩展。
public interface SpringApplicationRunListener {
/**
*
* 当run方法第一次启动时立即调用。可以用于非常早期的初始化。
*/
default void starting() {
}
/**
* 在准备环境之后,但在创建ApplicationContext之前调用。
*/
default void environmentPrepared(ConfigurableEnvironment environment) {
}
/**
* 一旦创建并准备好了ApplicationContext,但是在加载源之前调用。
*/
default void contextPrepared(ConfigurableApplicationContext context) {
}
/**
* 在加载应用程序上下文之前调用
* @param context the application context
*/
default void contextLoaded(ConfigurableApplicationContext context) {
}
/**
* 上下文已经刷新并且应用程序已经启动,但是ApplicationRunner还没有通知去执行的,
* @since 2.0.0
*/
default void started(ConfigurableApplicationContext context) {
}
/**
* 当应用程序上下文被刷新并且所有
* {@link ApplicationRunner ApplicationRunners}和{@link ApplicationRunner ApplicationRunners}
* 都被调用时,在run方法结束之前立即调用。
* @param context the application context.
* @since 2.0.0
*/
default void running(ConfigurableApplicationContext context) {
}
/**
* 在运行应用程序时发生故障时调用。
*
* @param context the application context or {@code null} if a failure occurred before
* the context was created
* @param exception the failure
* @since 2.0.0
*/
default void failed(ConfigurableApplicationContext context, Throwable exception) {
}
}
一个相信努力就会有结果的程序员,以兴趣驱动技术! ------ CoderOu