Alian解读SpringBoot 2.6.0 源码(一):SpringApplication对象创建(Spring工厂加载机制)
一、背景二、SpringApplication实例化2.1、实例化方法入口2.2、推断应用程序类型2.3、Spring工厂加载机制2.3.1、获取Spring工厂实例(重要)2.3.2、loadFactoryNames2.3.3、loadSpringFactories(核心)2.4、获取引导注册初始化器2.5、设置容器初始化器2.6、设置容器监听器2.7、推断main方法所在类结语
目录
一、背景
本文中使用Spring Boot 2.6.0来进行源码解读,不同的版本间会有细微的改动,大家自行区别,本文中使用的依赖如下。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.alian</groupId>
<artifactId>springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot</name>
<description>Spring Boot源码解读</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
二、SpringApplication实例化
2.1、实例化方法入口
package com.alian.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
//直接调用SpringApplication的静态run方法
SpringApplication.run(SpringbootApplication.class, args);
}
}
接下里的方法所在类的具体路径:org.springframework.boot.SpringApplication,第一个参数primarySource:加载的主要资源类,第二个参数 args:传递给应用的参数
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);
}
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//从上面方法传参知道,此处resourceLoader为null
this.resourceLoader = resourceLoader;
//判断primarySources不能为空,从main方法我们知道primarySources是SpringbootApplication.class
Assert.notNull(primarySources, "PrimarySources must not be null");
//将primarySources放入Set集合中(SpringApplication的全局变量primarySources)
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//从类路径中推断应用程序类型,放到SpringApplication的全局变量webApplicationType
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//从META-INF/spring.factories文件中获取BootstrapRegistryInitializer的实现类
//并利用反射创建对象返回放入SpringApplication的全局变量bootstrapRegistryInitializers ,List集合中
this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
//从META-INF/spring.factories文件中获取ApplicationContextInitializer的实现类(缓存中获取)
//并利用反射创建对象返回放入SpringApplication的全局变量initializers,List集合中
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//从META-INF/spring.factories文件中获取ApplicationListener的实现类(缓存中获取)
//并利用反射创建对象返回放入SpringApplication的全局变量listeners,List集合中
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//通过获取当前调用栈,找到入口方法main所在的类,放入SpringApplication的全局变量mainApplicationClass
this.mainApplicationClass = deduceMainApplicationClass();
}
- 推断应用程序类型(本文为SERVLET)
- 通过Spring工厂加载机制获取引导注册初始化器(获取并设置到缓存)
- 通过Spring工厂加载机制获取并设置初始化器(缓存中获取)
- 通过Spring工厂加载机制获取并设置监听器(缓存中获取)
- 推断主应用程序类
2.2、推断应用程序类型
我们看下是如何推断应用程序类型的
//从类路径中推断应用程序类型放到SpringApplication的全局变量webApplicationType
this.webApplicationType = WebApplicationType.deduceFromClasspath();
此方法所在类的具体路径:org.springframework.boot.WebApplicationType
package org.springframework.boot;
public enum WebApplicationType {
/**
* 非WEB项目
*/
NONE,
/**
* SERVLET WEB项目
*/
SERVLET,
/**
* 响应式WEB项目
*/
REACTIVE;
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
//其他代码省略...
}
实际上就是判断默认的classloader中是否存在指定的类:
- 响应式WEB项目:存在 org.springframework.web.reactive.DispatcherHandler,并且不存在org.springframework.web.servlet.DispatcherServlet 和 org.glassfish.jersey.servlet.ServletContainer
- 非WEB项目:不存在org.springframework.web.context.ConfigurableWebApplicationContext和javax.servlet.Servlet
- SERVLET WEB项目:以上两种都不满足
本文中因为本文中加入的依赖是:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
所以推断出来的是:WebApplicationType.SERVLET
2.3、Spring工厂加载机制
为了解读接下里的内容,我们先要引出了一个很重要的概念:spring 工厂加载机制,看看它是怎么实现的,做了一些什么。SpringBoot 的 SpringFactoriesLoader 工厂的加载机制类似Java提供的SPI机制一样,是Spring提供的一种加载方式。只需要在classpath路径下新建一个文件META-INF/spring.factories,并在里面按照Properties格式填写好接口和实现类即可通过SpringFactoriesLoader 来实例化相应的Bean。其中key可以是接口、注解、或者抽象类的全名,value为相应的实现类,当存在多个实现类时,用逗号进行分割。
2.3.1、获取Spring工厂实例(重要)
此方法所在类的具体路径:org.springframework.boot.SpringApplication,这也是工厂方法调用的常用的调用入口(包括要分析的引导注册初始化器、容器初始化器、容器监听器)。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
//第二个参数是一个空的Class数组,用于实例化的默认构造方法
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
//通过Spring工厂加载机制,根据类加载器获取指定的类名的实现类的Set集合(下一小节的重点)
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//创建上述结果的实例化对象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//对实例化对象根据order进行升序排序
AnnotationAwareOrderComparator.sort(instances);
//返回实例化对象列表
return instances;
}
@SuppressWarnings("unchecked")
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;
}
上面这段代码的整体流程是:
- 获取类加载器
- 通过Spring工厂加载机制,根据类加载器获取指定的类名的实现类的Set集合
- 遍历集合里的实现类名,通过类加载器,用反射的方式加载类
- 校验是否是指定类名的实现类(必须是)
- 根据参数类型获取类的构造方法,参数类型列表为空则是获取的默认构造方法
- 实例化实现类,包含了默认的构造方法,并把结果加入到实例化好的列表中
- 返回实例化结果列表
2.3.2、loadFactoryNames
此方法所在类的具体路径:org.springframework.core.io.support.SpringFactoriesLoader
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
// 此处的类加载器是appClassLoader
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
//如果为空则再次获取
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
// 类的全限定名称(即解析properties文件中需要的key值)
String factoryTypeName = factoryType.getName();
// 根据类加载器,加载classpath下/META-INF/spring.factories下所有的类名称列表
// 从结果Map<String, List<String>>中,根据指定类型获取所有实现类名称的集合List<String>
// 核心,下一节具体分析loadSpringFactories
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
- loadSpringFactories(ClassLoader classLoader)返回的是所有META-INF/spring.factories文件解析完的结果
- 根据指定类名获取结果,没有则返回空列表
2.3.3、loadSpringFactories(核心)
此方法所在类的具体路径:org.springframework.core.io.support.SpringFactoriesLoader
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
// 根据类加载器去缓存中获取加载好的结果集
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
//结果不为空,则返回
return result;
}
result = new HashMap<>();
try {
// 常量FACTORIES_RESOURCE_LOCATION的值为META-INF/spring.factories
// 使用类加载器扫描classpath上所有JAR中的文件:META-INF/spring.factories
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
// 得到的是一个个的META-INF/spring.factories文件的具体路径的集合,遍历集合
while (urls.hasMoreElements()) {
// 得到每一个META-INF/spring.factories文件的具体路径
URL url = urls.nextElement();
// 把META-INF/spring.factories文件转为UrlResource对象
UrlResource resource = new UrlResource(url);
// 使用属性加载工具类把文件解析为key,value形式的Properties
// 其中key为具体的类名,value为用逗号分隔的实现类的类名
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
// 遍历Properties
for (Map.Entry<?, ?> entry : properties.entrySet()) {
// 获取限定类名的名称
String factoryTypeName = ((String) entry.getKey()).trim();
// 将value也就是实现类的配置按照","符号分割开,得到实现类的类名的字符串数组
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
// 遍历实现类数组
for (String factoryImplementationName : factoryImplementationNames) {
// 把限定类名key和实现类名value放到Map中
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()).add(factoryImplementationName.trim());
}
}
}
// Replace all lists with unmodifiable lists containing unique elements
// 用包含唯一元素的不可修改列表替换所有列表
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
// 把解析的结果加入到缓存,后续就不用再次加载了
cache.put(classLoader, result);
} catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
// 返回结果
return result;
}
整体的加载流程:
- 根据类加载器去缓存获取Spring工厂加载结果,如果获取到了则直接返回
- 如果缓存中没有数据,初始化一个Map,用于存储Spring工厂加载的结果集
- 使用类加载器扫描classpath上所有JAR中的文件:META-INF/spring.factories,得到一个包含所有spring.factories文件路径 的集合
- 遍历上述的集合,得到的是一个个的META-INF/spring.factories文件的具体路径,通过属性加载工具类加载文件,得到key、value形式的Properties
- 再次遍历Properties得到key为限定类名,value则为它的实现类,多个实现类是用逗号分隔的
- 解析value的值,并把key和value加入到之前新建的Map中
- 把所有META-INF/spring.factories文件解析完的数据加载到缓存中
- 返回解析完的结果
本版本Spring Boot 2.6.0 一共获取的到18个结果:
2.4、获取引导注册初始化器
我们再看构造方法中获取引导注册初始化器,此方法所在类的具体路径:org.springframework.boot.SpringApplication
//从META-INF/spring.factories文件中获取BootstrapRegistryInitializer的实现类
//并利用反射创建对象返回放入SpringApplication的全局变量bootstrapRegistryInitializers ,List集合中
this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
通过上一章节得到的结果,我们这里是没有没有找到引导注册初始化器。
2.5、设置容器初始化器
我们再看设置容器初始化器
//从META-INF/spring.factories文件中获取ApplicationContextInitializer的实现类(缓存中获取)
//并利用反射创建对象返回放入SpringApplication的全局变量initializers,List集合中
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
此方法所在类的具体路径:org.springframework.boot.SpringApplication
private List<ApplicationContextInitializer<?>> initializers;
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>(initializers);
}
之前的Spring工厂机制我们已经讲过了,这里也只是调用获取到结果而已,实际上这里获取的结果有7个:
那这些初始化器具体在哪里呢?我们在spring-boot-2.6.0.jar 的META-INF/spring.factories 文件中找到如下配置:
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
spring-boot-autoconfigure-2.6.0.jar 的META-INF/spring.factories 文件中找到如下配置
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
到这里几个初始化器就已经获取到并且存起来了,等待后续操作。
2.6、设置容器监听器
此方法所在类的具体路径:org.springframework.boot.SpringApplication
//从META-INF/spring.factories文件中获取ApplicationListener的实现类(缓存中获取)
//并利用反射创建对象返回放入SpringApplication的全局变量listeners,List集合中
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
此方法所在类的具体路径:org.springframework.boot.SpringApplication
private List<ApplicationListener<?>> listeners;
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<>(listeners);
}
同样的,根据之前的Spring工厂机制,我们可以从缓存中获取到容器的监听器有8个:
同样的我们在spring-boot-2.6.0.jar 的META-INF/spring.factories 文件中找到如下配置:
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener
spring-boot-autoconfigure-2.6.0.jar 的META-INF/spring.factories 文件中找到如下配置
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
到这里几个容器监听器就已经获取到并且存起来了,也需等待后续操作。
2.7、推断main方法所在类
此方法所在类的具体路径:org.springframework.boot.SpringApplication
private Class<?> deduceMainApplicationClass() {
try {
// 获取StackTraceElement数组,也就是这个栈的信息
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
//stackTraceElement.getClassName(),得到定义类,即main方法所在类
//通过类名使用反射得到真正的主类
return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
mainApplicationClass是通过异常类堆栈的方式,获取main启动类,这里可以看出:main启动类和primarySources对应的类可以不一样。
结语
到这里我们的SpringApplication对象已经创建好了,下一篇我们分析SpringApplication对像的run方法。
更多推荐
所有评论(0)