SpringBoot启动流程分析1–SpringApplication实例化
一、概述
源码基于SpringBoot 2.7.xx版本
1.1 简介
SpringBoot启动流程大致分为两个阶段,第一个阶段是SpringApplication 实例化,第二个阶段为执行 run 方法,本章关注第一阶段
第一个阶段:
- 设置primarySources属性
- 推断应用类型–WebApplicationType
- 加载Spring应用上下文初始化器–ApplicationContextInitializer
- 加载Spring应用事件监听器–ApplicationListener
- 推断应用引导类–mainApplicationClass
第二阶段:
- 初始化引导上下文–bootStrapContext
- 创建应用上下文环境–ConfigurableEnvironment
- 创建应用上下文–createApplicationContext
- 准备应用上下文–prepareContext
- 刷新应用上下文–refreshContext
- 刷新应用上下文后的扩展接口–afterRefresh(空方法)
- 调用ApplicationRunner和CommandLineRunner对应的run方法
1.2 名词解释
- Instantiation
实例化,是加载class文件,并在内存中开辟一块内容空间,创建一个属性值为默认值的对象 - Initialization
初始化,初始化是对对象的属性进行赋值即属性填充,是在Bean实例化完成后执行的。
二、详解
2.1 mian方法
// 能够扫描Spring组件并自动配置Spring Boot
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
在这个类中需要关注的是
- @SpringBootApplication
- SpringApplication.run()
关于 @SpringBootApplication 注解,在后面SpringBoot启动流程分析知识点–自动装配的章节会展开去分析。
2.2 SpringApplication的实例化
SpringBoot启动的时候,会创建一个SpringApplication的实例,实例化的时候会做以下几件事:
- 把primarySources(启动类Class对象)设置到SpringApplication的primarySources属性中,后续prepareContext方法会根据该属性加载启动类,
并将启动类注入到容器,后续会根据该类进行包扫描; - 推断应用类型,后面会根据类型初始化对应的环境,一般为WebApplicationType.SERVLET类型;
- 实例化classpath下 META-INF/spring.factories中已配置的 ApplicationContextInitializer;
- 实例化classpath下 META-INF/spring.factories中已配置的 ApplicationListener;
- 根据调用栈,推断出 main 方法的类名,用于发布事件,打印banner。
public class SpringApplication {
// 调用重载方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[]{primarySource}, args);
}
// 两件事:1.实例化SpringApplication 2.执行run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
new SpringApplication(primarySources);
return run(args);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// resourceLoader参数默认为null
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 1.把primarySources设置到SpringApplication的primarySources属性中--启动类Class对象
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 2.推断应用类型,后面会根据类型初始化对应的环境,一般为WebApplicationType.SERVLET类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 3.实例化classpath下 META-INF/spring.factories中已配置的 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 4.实例化classpath下 META-INF/spring.factories中已配置的 ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 5.根据调用栈,推断出 main 方法的类名
this.mainApplicationClass = deduceMainApplicationClass();
}
}
2.3 推断应用类型
具体的判断逻辑如下:
- WebApplicationType.REACTIVE
classpath下 存在org.springframework.web.reactive.DispatcherHandler,
并且不存在org.springframework.web.servlet.DispatcherServlet和org.glassfish.jersey.servlet.ServletContainer; - WebApplicationType.SERVLET
classpath下 存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext; - WebApplicationType.NONE
不满足以上条件。
public enum WebApplicationType {
NONE,
SERVLET,
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";
/**
* 判断 应用的类型
* NONE: 应用程序不是web应用,也不应该用web服务器去启动
* SERVLET: 应用程序应作为基于servlet的web应用程序运行,并应启动嵌入式servlet web(tomcat)服务器。
* REACTIVE: 应用程序应作为 reactive web应用程序运行,并应启动嵌入式 reactive web服务器。
* @return
*/
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;
}
}
// classpath下 存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext
return WebApplicationType.SERVLET;
}
}
2.4 加载Spring应用上下文初始化器
实例化classpath下 META-INF/spring.factories中已配置的 ApplicationContextInitializer。
public class SpringApplication {
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// ……
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// ……
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[]{});
}
/**
* 从META-INF/spring.factories获取指定的Spring的工厂实例
*/
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 通过指定的classLoader从 META-INF/spring.factories 的资源文件中读取 key 为 type.getName() 的 value
// type为接口,value为逗号分割的实现类
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 创建Spring工厂实例,实际上就是type接口的实例对象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 对Spring工厂实例排序(org.springframework.core.annotation.Order注解指定的顺序)
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
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;
}
}
2.4.1 loadFctoryNames()方法
loadFctoryNames(type,classLoader)方法就是从spring.factories中获取类型名称(其实就是一接口及其实现列表)
ApplicationContextInitializer 是Spring框架的类, 这个类的主要目的就是在 ConfigurableApplicationContext 调用refresh()方法之前,
回调这个类的initialize方法。通过 ConfigurableApplicationContext 的实例获取容器的环境Environment,从而实现对配置文件的修改完善等工作。
spring-boot中ApplicationContextInitializer的实现如下:
- spring-boot-autoconfigure
- SharedMetadataReaderFactoryContextInitializer
- ConditionEvaluationReportLoggingListener
- spring-boot
- ConfigurationWarningsApplicationContextInitializer
- ContextIdApplicationContextInitializer
- DelegatingApplicationContextInitializer
- RSocketPortInfoApplicationContextInitializer
- ServerPortInfoApplicationContextInitializer
如果pom有依赖其他jar中存在spring.factories,则会一同加载进来,并缓存到cache中。
public final class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
static final Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap<>();
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
// 在这里进行返回的时候会进行loadFactories
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
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 {
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
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();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
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;
}
}
2.5 加载Spring应用事件监听器
实例化classpath下 META-INF/spring.factories中已配置的 ApplicationListener。
ApplicationListener 的加载过程和上面的 ApplicationContextInitializer 类的加载过程是一样的。
spring-boot中ApplicationListener的实现如下:
- spring-boot-autoconfigure
- BackgroundPreinitializer
- spring-boot
- ClearCachesApplicationListener
- ParentContextCloserApplicationListener
- FileEncodingApplicationListener
- AnsiOutputApplicationListener
- DelegatingApplicationListener
- LoggingApplicationListener
- EnvironmentPostProcessorApplicationListener