本文环境是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方法了。