上次简单的学习了一下spring boot的整个启动的流程,今天主要详细的学习一下具体每一步做了什么事情!今天主要是学习一下创建SpringApplication实例时比较重要的两步,即initializers和listeners的两个变量的创建过程和它们的作用。开始之前首先改正一个错误,在上次学习spring boot启动分析时,initializers是获取ApplicationContextInitializer其实现类的所有名字,listeners是获取ApplicationListener的所有实现类名称,然后通过反射去创建实例。今天看源码的时候发现我是错的,很尴尬,接下来仔细看一下到底是怎么样一个过程吧!!
一、initializers的初始化
在SpringApplication的构造方法里面是通过set方法完成initializers注入的:
A this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
B private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return this.getSpringFactoriesInstances(type, new Class[0]);
}
C private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
a Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
b List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
a public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
b private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList(names.size());
Iterator var7 = names.iterator();
while(var7.hasNext()) {
String name = (String)var7.next();
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
} catch (Throwable var12) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
}
}
return instances;
}
上面的方法是initializers完成注入的一个整体流程,A获取到所有initializers实例完成initializers的初始化,它调用的是B方法,且入参为ApplicationContextInitializer的类对象,而后B方法调用了C方法,入参为ApplicationContextInitializer的类对象和一个大小为0的Class数组。关键是C方法,C方法里面又分为a和b方法,a方法是获取所有initializer类的名称,b方法是根据类名成创建出initializer实例(b方法是一个通用方法,创建listener实例也是通过它完成的),另外在C方法里面会对所有的initializer进行排序。
1、获取相关initializer类的名称
先来看下a方法,a方法new了一个HashSet用户存储所有initializer类的名称,而这些类的名称则来自下面的方法:
A public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
// 可以简单的这样看:
Map<String, List<String>> map = loadSpringFactories(classLoader);
return (List)map.getOrDefault(factoryClassName, Collections.emptyList());
}
B private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
result.addAll((String)entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var9) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
}
}
}
上面的方法中A方法先根据ApplicationContextInitializer类对象,获取到它的类名成,即:
org.springframework.context.ApplicationContextInitializer
然后调用B方法,先试着从cache变量中根据类加载器作为key去获取相应的值(这是一个map变量,只是稍微复杂一点,它的类型是Map<ClassLoader, MultiValueMap<String, String>>),如果获取到相应的值,直接返回;否则才会去获取"META-INF/spring.factories",这个是一个URL的枚举,然后遍历该枚举,获取到到每一个"META-INF/spring.factories"配置文件,再遍历所有的配置文件,最后将对应的key(一些类的名称)和对应的value(一个或多个类名称,转成List<String>)添加到result中,然后会将这些值都放到cache变量中,避免再次使用时重新加载,最后返回结果。
在方法A中在B方法返回结果的基础上调用getOrDefault方法,B方法返回结果是一个map,getOrDefault方法就是使用"org.springframework.context.ApplicationContextInitializer"作为key,获取所有对应的initializer的类的名称。所以上次说获取所有ApplicationContextInitializer实现类的名称是错误的,其实获取的是配置文件中指定的以"org.springframework.context.ApplicationContextInitializer"为key的所有类的名称。
自己也查看了一下所有的"spring.factories"配置文件,主要是在3个jar包下面,分别是:
spring-boot-2.0.6.RELEASE.jar
spring-boot-autoconfigure-2.0.6.RELEASE.jar
spring-beans-5.0.10.RELEASE.jar
打开相应的配置文件看一下,就会看到相应的配置,因为配置还是比较多的,所以这里简单的截图看一下:
通过图片也可以很直观的看到以"org.springframework.context.ApplicationContextInitializer"为key的value值是多个相关的类名(当然这些类确实也都实现类ApplicationContextInitializer接口)。
2、创建initializer类的实例
方法a返回的是所有需要实列话的initializer类的名称集合,然后在b方法中,先根据类的名称和类加载器获取到对应的Class实例,然后通过反射获取对应类的构造方法,然后调应构造方法的newInstance方法创建出实列,最后将这些initializer返回,最终返回到A方法,并完成initializers变量的初始化,到这里initializers初始化就完成了。
二、listeners的初始化
listeners的初始化和initializer过程完全一样,只是接口类型不同而已,所以这里就不过多介绍了。
三、initializer的作用
因为程序启动的顺序的原因,initializer会在准备context阶段才执行,这里只说initializer的initialize方法执行,不包括一些内部类的相关方法。
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
// initializer执行
this.applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
initializer执行顺序不太一样,自己大概跟踪了一下每个initializer的执行过程,但是比较乱,所以我就单独的把每一个initializer拿出来,大概说一下它的作用,有的initializer还是非常复杂的。自己就直接根据相应类中的方法简单的介绍一下:
1、org.springframework.boot.context.config.DelegatingApplicationContextInitializer
用户自定义应用环境或者应用上下文时执行的一些初始化操作.
2、org.springframework.boot.context.ContextIdApplicationContextInitializer
主要作用获取当前应用名称,并将名称赋给applicationContext,最后将ContextIdApplicationContextInitializer.ContextId实例作为单例注入到applicationContext的beanFactory中。
3、org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer
将ConfigurationWarningsPostProcessor实例注入到context的beanFactoryPostProcessors集合中。
4、org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
这是一个比较特殊的类,因为它同时实现了ApplicationContextInitializer接口和ApplicationListener接口
它的initialize方法主要是将其本身注入到context的applicationListeners中去。
而它的onApplicationEvent方法监听的则是WebServerInitializedEvent事件,获取web服务器的启动端口,然后添加到环境变量的propertySources中去。
5、org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
initialize方法将CachingMetadataReaderFactoryPostProcessor注入到context的beanFactoryPostProcessors
集合中。
此外它的另一个内部类SharedMetadataReaderFactoryBean的实现类的onApplicationEvent方法监听ContextRefreshedEvent事件,以清除缓存。
6、org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
它的initialize方法主要是将context注入到其实例当中,并创建其内部类ConditionEvaluationReportListener的实例,添加到应用context的applicationListener集合中,最后是其report(类型ConditionEvaluationReport)的注入。
其内部类ConditionEvaluationReportListener监听事件ContextRefreshedEvent。
四、listener的作用
listener的作用主要和其监听的事件有关系,一个监听器可能会监听多个事件,并根据事件类型做不同的操作,下面也是根据代码来推断每个监听器的大概功能:
1、org.springframework.boot.context.config.ConfigFileApplicationListener
监听事件ApplicationEnvironmentPreparedEvent和ApplicationPreparedEvent。
在ApplicationEnvironmentPreparedEvent事件中,先以"org.springframework.boot.env.EnvironmentPostProcessor"作为key,获得"META-INF/spring.factories"配置文件夹下的所有类名(这里其实不需要再去加载配置文件的,因为在加载initializer时已经全部加载过了),然后和initializer一样,通过反射创建出三个EnvironmentPostProcessor实现类,然后循环调用它们以及ConfigFileApplicationListener实例的postProcessEnvironment方法。而ConfigFileApplicationListener实例,获取配置文件的加载器,最终会加载用户自定义的配置文件以及指定的配置文件。这个方法有点复杂,感兴趣可以耐心的看看。
在ApplicationPreparedEvent事件中会创建一个内部类PropertySourceOrderingPostProcessor对象,然后添加到context的beanFactoryPostProcessors变量中。
2、org.springframework.boot.context.config.AnsiOutputApplicationListener
监听事件ApplicationEnvironmentPreparedEvent,并通过配置文件设置ANSI是否通过console输出。具体是什么我也不是很清楚。
3、org.springframework.boot.context.logging.LoggingApplicationListener
日志监听器,监听的事件有ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、ContextClosedEvent和ApplicationFailedEvent。
ApplicationStartingEvent阶段:日志系统的初始化之前的准备工作
ApplicationEnvironmentPreparedEvent阶段:日志系统初始化,初始化日志级别等。
ApplicationPreparedEvent阶段,把日志系统作为单列注入到bean工厂。这里主要是日志的一些内容,简单了解一下就好。
4、org.springframework.boot.context.logging.ClasspathLoggingApplicationListener
这个感觉也没什么用,判断日志级别是否为debug级别,是的话则输出监听事件信息,否则不做任何操作。监听事件包括ApplicationEnvironmentPreparedEvent和ApplicationFailedEvent。
5、org.springframework.boot.autoconfigure.BackgroundPreinitializer
监听事件ApplicationStartingEvent、ApplicationReadyEvent和ApplicationFailedEvent,
主要是做一些准备工作,比如:字符集的设置、JSON和对象转换相关内容、http请求消息转换器以及tomcat中MBeanFactory的创建等等。
6、org.springframework.boot.context.config.DelegatingApplicationListener
监听事件ApplicationEnvironmentPreparedEvent作用和DelegatingApplicationContextInitializer类似,都是判断用户自定义的一些内容。 spring boot文档 有一些介绍,有兴趣可以看一下。
7、org.springframework.boot.builder.ParentContextCloserApplicationListener
主要监听的是ParentContextAvailableEvent事件,也就是父context可用时,创建一个ContextCloserListener实例,并注入到父context的监听器集合中,父context必须是"ConfigurableApplicationContext"的实例。
8、org.springframework.boot.ClearCachesApplicationListener
监听的ContextRefreshedEvent事件,目的就是清楚类加载器的缓存的方法和字段信息,当前类加载器的类对象通过方法名"clearCache"得到相应的Method对象,然后执行该方法,最后递归调用当前类加载器的父加载器执行以上内容。
9、org.springframework.boot.context.FileEncodingApplicationListener
监听事件ApplicationEnvironmentPreparedEvent,主要是查看环境中的propertySources有没有配置文件字符集,有的话和当前系统的文件字符集比较是否相等,不想等就日志打印错误信息,个人感觉没什么用。
10、org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
监听ApplicationStartingEvent事件,这个监听器具体做什么的我也不太清楚,跟踪源码发现是使用
ApplicationStartingEvent事件springApplication对象的类加载器去尝试加载名字为"liquibase.servicelocator.CustomResolverServiceLocator"的类或者"liquibase.servicelocator"中子类名称为"CustomResolverServiceLocator"的类。如果没有加载不到就不进行任何操作,加载到则创建LiquibaseServiceLocatorApplicationListener的子类LiquibasePresent的实例,并执行其replaceServiceLocator方法。具体有什么用我也不清楚,我的项目是没有加载到相关的类。
总结:
以上就是initializer和listener的作用,当然这些都是spring boot在配置文件中指定的类的实例。然后单独的分析了每个initializer和listener的用途,实际项目启动过程中要比我们说的复杂的多。另外很多东西都不是上面介绍的那么简单,尤其像ConfigFileApplicationListener自己看了很久都还没有完全的看清整个过程。另外还有一些其他的内容,像beanFactory、postProcessors的等等有哪些作用,tomcat又是如何启动的,这些都是需要去了解的。本以为spring boot简单,但是实际上还是有太多的东西需要去了解,过程远比想象中艰难。只能后面一步步的继续去研究了。