关联博文:
SpringBoot启动时都做了哪些事(一)?
SpringBoot启动时都做了哪些事(二)?
SpringBoot启动时都做了哪些事(三)?
SpringBoot启动时都做了哪些事(四)?
本文环境是SpringBoot 2.2.4.RELEASE,我们尝试去跟踪研究启动流程都做了哪些事情。
本文分析SpringApplication的实例化。核心有四点:
- 确定primarySources 和主启动类
- 确定webApplicationType
- 检测并实例化ApplicationContextInitializer
- 检测并实例化ApplicationListener
以main方法为入口。
public static void main(String[] args) {
SpringApplication.run(RecommendApplication.class, args);
}
SpringApplication的run方法。
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
这里primarySource就是我们的主启动类,args默认为空。其分为SpringApplication的实例化,进而触发其run方法。
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
这里要特别注意的是setInitializers和setListeners。前者检索jar并实例化ApplicationContextInitializer,后者检索jar并实例化ApplicationListener然后赋予SpringApplication实例。
① getSpringFactoriesInstances
SpringApplication 的getSpringFactoriesInstances方法如下所示。
// SpringApplication 这里type为ApplicationContextInitializer.class
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 检索符合目标类型的所有beanName
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//创建bean实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//对bean实例进行排序
AnnotationAwareOrderComparator.sort(instances);
//返回实例结果
return instances;
}
② loadFactoryNames
将会从每个jar包下的META-INF/spring.factories
路径扫描目标类型(factoryType),比如本文这里的ApplicationContextInitializer和ApplicationListener。
// SpringFactoriesLoader
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
首先调用loadSpringFactories方法从每个jar的FACTORIES_RESOURCE_LOCATION==META-INF/spring.factories
检索并获取配置信息放到MultiValueMap<String, String> result
中。
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
这里拿到的result如下所示,此时是一个全集,还没有进行过滤。
这个检索结果同时会往cache中放一下:
private static final Map<ClassLoader, MultiValueMap<String, String>> cache =
new ConcurrentReferenceHashMap<>();
③ createSpringFactoriesInstances
遍历Set<String> names
,反射进行实例化。
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<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
④ 实例结果
这里获取的ApplicationContextInitializer如下所示:
0 = "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer"
1 = "org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"
2 = "org.springframework.boot.devtools.restart.RestartScopeInitializer"
3 = "org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer"
4 = "org.springframework.boot.context.ContextIdApplicationContextInitializer"
5 = "org.springframework.boot.context.config.DelegatingApplicationContextInitializer"
6 = "org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer"
7 = "org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer"
排序前后的instances(有8个):
获取的ApplicationListener如下(有13个):
0 = "org.springframework.boot.autoconfigure.BackgroundPreinitializer"
1 = "org.springframework.boot.devtools.restart.RestartApplicationListener"
2 = "org.springframework.boot.devtools.logger.DevToolsLogFactory.Listener"
3 = "org.springframework.boot.ClearCachesApplicationListener"
4 = "org.springframework.boot.builder.ParentContextCloserApplicationListener"
5 = "org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor"
6 = "org.springframework.boot.context.FileEncodingApplicationListener"
7 = "org.springframework.boot.context.config.AnsiOutputApplicationListener"
8 = "org.springframework.boot.context.config.ConfigFileApplicationListener"
9 = "org.springframework.boot.context.config.DelegatingApplicationListener"
10 = "org.springframework.boot.context.logging.ClasspathLoggingApplicationListener"
11 = "org.springframework.boot.context.logging.LoggingApplicationListener"
12 = "org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener"
排序后的ApplicationListener:
0 = {RestartApplicationListener@1940}
1 = {CloudFoundryVcapEnvironmentPostProcessor@1941}
2 = {ConfigFileApplicationListener@1942}
3 = {AnsiOutputApplicationListener@1943}
4 = {LoggingApplicationListener@1944}
5 = {BackgroundPreinitializer@1945}
6 = {ClasspathLoggingApplicationListener@1946}
7 = {DelegatingApplicationListener@1947}
8 = {ParentContextCloserApplicationListener@1948}
9 = {DevToolsLogFactory$Listener@1949}
10 = {ClearCachesApplicationListener@1950}
11 = {FileEncodingApplicationListener@1951}
12 = {LiquibaseServiceLocatorApplicationListener@1952}
接下来就会触发SpringApplication实例的run方法了。