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方法。允许我们对ConfiurableApplicationContext的实例做进一步的设置和处理。例如:通过 ConfigurableApplicationContext 的实例获取容器的环境Environment,从而实现对配置文件的修改完善等工作。
spring-boot中ApplicationContextInitializer的实现如下:
- spring-boot
- DelegatingApplicationContextInitializer
- ContextIdApplicationContextInitializer
- ConfigurationWarningsApplicationContextInitializer
- RSocketPortInfoApplicationContextInitializer
- ServerPortInfoApplicationContextInitializer
- spring-boot-autoconfigure
- SharedMetadataReaderFactoryContextInitializer
- ConditionEvaluationReportLoggingListener
如果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;
}
}
部分ApplicationContextInitializer作用:
- spring-boot
- DelegatingApplicationContextInitializer:委托(代理)initializer,其核心功能是通过环境变量context.initializer.classes(类的全限定名,多个用,分割)动态加载自定义初始化器,若未指定,则不执行任何操作。这个初始化器的优先级是Spring Boot定义初始化器中优先级别最高的,因此会被第一个执行
- ContextIdApplicationContextInitializer:给ApplicationContext设置一个ID,即spring.application.name,未设置默认为application
- ConfigurationWarningsApplicationContextInitializer: 加载ConfigurationWarningsPostProcessor 处理器
- RSocketPortInfoApplicationContextInitializer
- ServerPortInfoApplicationContextInitializer:添加监听器, 当服务器启动时(WebServerInitializedEvent事件)将内嵌的Web服务器使用的端口添加进 spring 容器
- spring-boot-autoconfigure
- SharedMetadataReaderFactoryContextInitializer:加载CachingMetadataReaderFactoryPostProcessor 处理器
- ConditionEvaluationReportLoggingListener:添加监听器,监听ContextRefreshedEvent和ApplicationFailedEvent事件,通过日志输出条件评估报告(如自动配置类加载情况、条件匹配结果等),便于开发者理解应用启动时的配置逻辑。
2.5 加载Spring应用事件监听器
实例化classpath下 META-INF/spring.factories中已配置的 ApplicationListener。
ApplicationListener 的加载过程和上面的 ApplicationContextInitializer 类的加载过程是一样的。
spring-boot中ApplicationListener的实现如下:
- spring-boot
- EnvironmentPostProcessorApplicationListener:主要监听ApplicationEnvironmentPreparedEvent事件,主要用于在Spring应用环境准备阶段加载环境后置处理器–EnvironmentPostProcessor,并按照优先级排序后调用它们的 postProcessEnvironment 方法。该方法允许开发者动态修改环境配置(如添加属性源、调整属性优先级等)。
- AnsiOutputApplicationListener
- LoggingApplicationListener
- DelegatingApplicationListener
- ParentContextCloserApplicationListener
- ClearCachesApplicationListener
- FileEncodingApplicationListener
- spring-boot-autoconfigure
- BackgroundPreinitializer
在 spring.factories 配置文件中默认定义的 EnvironmentPostProcessor 的实现类(即EnvironmentPostProcessorApplicationListener触发监听事件后调用):
- BackgroundPreinitializer
- RandomValuePropertySourceEnvironmentPostProcessor:用于动态生成随机值(如字符串、数值、UUID等)并将其注入到环境配置中的组件。
- SystemEnvironmentPropertySourceEnvironmentPostProcessor:将环境中已保存的操作系统环境变量相关的属性进行替换,将原本保存环境变量的SystemEnvironmentPropertySource实例替换成其子类OriginAwareSystemEnvironmentPropertySource,两者的协作关系是:后置处理器在环境初始化阶段完成替换,而增强后的属性源在运行时提供Origin查询能力。
- SpringApplicationJsonEnvironmentPostProcessor:解析 spring.application.json 或 SPRING_APPLICATION_JSON 配置的 json 字符串。
- CloudFoundryVcapEnvironmentPostProcessor:是 Spring Boot 中一个专门用于处理 Cloud Foundry 环境(一个开源 PaaS 云平台)的 EnvironmentPostProcessor。 它的主要作用是从 Cloud Foundry 提供的 VCAP_APPLICATION 和 VCAP_SERVICES 环境变量中读取配置信息,并将这些信息作为属性源添加到 Spring Boot 的 Environment 中。
- ConfigDataEnvironmentPostProcessor:配置文件解析application.yml,以及application-dev.yml等,每个yml生成一个配置源(替代旧版ConfigFileApplicationListener)
- DebugAgentEnvironmentPostProcessor
SpringBoot启动流程解析:SpringApplication实例化与自动配置
本文详细分析了SpringBoot2.7.x启动流程中的第一个阶段——SpringApplication实例化过程,涉及primarySources的设置、应用类型推断、初始化器和监听器加载,以及mainApplicationClass的推断。
442

被折叠的 条评论
为什么被折叠?



