SpringBoot源码系列文章
SpringBoot源码解析(一):启动流程之SpringApplication构造方法
在之前的文章中,我们深入研究了Tomcat、Spring、以及SpringMVC的源码。这次,我们终于来到SpringBoot的源码分析。接下来的几篇文章将重点关注SpringBoot的启动原理
和自动配置原理
。本篇文章将聚焦于SpringApplication的构造方法。基于 2.7.18
版本,这也是SpringBoot3发布前的最后一个版本。
SpringApplication.run()
方法是启动SpringBoot应用的核心入口。我们从这个方法开始,逐步深入。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
SpringApplication的构造方法
,run方法后面介绍public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
private Set<Class<?>> primarySources;
// web应用类型
private WebApplicationType webApplicationType;
// 引导注册组件初始化器
private List<BootstrapRegistryInitializer> bootstrapRegistryInitializers;
// 上下文初始化器
private List<ApplicationContextInitializer<?>> initializers;
// 监听器
private List<ApplicationListener<?>> listeners;
// 启动类Class
private Class<?> mainApplicationClass;
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
// primarySources也就是SpringApplication.run(Application.class, args)的Application不能为空
Assert.notNull(primarySources, "PrimarySources must not be null");
// 将Application的Class对象添加进来
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 1.根据类路径来推断 Web 应用程序的类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 2. 读取spring.factories文件
// 2.1 查找并实例化BootstrapRegistryInitializer类型的工厂类
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 2.2 查找上下文初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 2.3 查找监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 3.推断启动类Class
this.mainApplicationClass = deduceMainApplicationClass();
}
// 1.根据类路径来推断 Web 应用程序的类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
Servlet
的Web应用程序,将启动嵌入式ServletWeb服务器(如Tomcat)响应式
风格的Web应用程序,将启动嵌入式响应式Web服务器(如Netty)// WebApplicationType枚举类
public enum WebApplicationType {
// 表示该应用程序不是 Web 应用,不会启动嵌入式 Web 服务器
NONE,
// 表示一个传统的基于 Servlet 的 Web 应用程序,将启动嵌入式 Servlet Web 服务器(如 Tomcat)
SERVLET,
// 表示一个响应式风格的 Web 应用程序,将启动嵌入式响应式 Web 服务器(如 Netty)
REACTIVE;
// 适合运行在基于 Servlet 的环境中
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
// 是 Spring MVC 应用程序的主要调度器
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
// 用于 Spring WebFlux。这表明是一个响应式 Web 应用程序
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
// 用于基于 Jersey 的应用程序
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
// 推断方法
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;
}
}
先介绍下ClassUtils.isPresent
方法,用它来检查类路径中是否有特定类。使用Class.forName('类路径')
方法,成功返回true,表示存在此类,报错返回fals,表示没有此类。
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
forName(className, classLoader);
return true;
}
catch (IllegalAccessError err) {
throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
className + "]: " + err.getMessage(), err);
}
catch (Throwable ex) {
// Typically ClassNotFoundException or NoClassDefFoundError...
return false;
}
}
reactive.DispatcherHandler
类,当不存在servlet.DispatcherServlet
类表示响应式Web应用
Servlet
或ConfigurableWebApplicationContext
都存在表示传统Web应用
文件格式
键值对
的形式表示
全限定名
全限定名
,多个类可以用逗号
分隔示例
\
表示续行符,用来将长行分成多行写# 自动配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.config.MyAutoConfiguration1,\
com.example.config.MyAutoConfiguration2
# 自定义监听器
org.springframework.context.ApplicationListener=\
com.example.listener.MyApplicationListener
// 2. 读取spring.factories文件
// 2.1 查找并实例化BootstrapRegistryInitializer类型的工厂类
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 2.2 查找上下文初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 2.3 查找监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
所有依赖
的JAR文件的META-INF/spring.factories
文件中加载指定类型的多个工厂类名称Class.forName
反射获取实例化多个对象,并根据对象上@Order
注解排序// SpringApplication类方法
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();
// 用于从所有依赖的 JAR 文件的 META-INF/spring.factories 文件中加载指定类型的工厂类名称
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 根据权限定类名字符串,Class.forName反射实例化对象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 它能够根据@Order注解和Ordered接口的优先级来对对象进行排序,从而决定对象的执行顺序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
接口或抽象类型
的全限定名,值是实现类
的全限定名列表。读取结果为Map<String, List<String>>
type(类或接口)
匹配的所有工厂类名称(就是通过接口获取多个实现类
)// SpringFactoriesLoader类方法
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
// 加载spring.factories文件,获取Map,通过key获取多个实现
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
// 读取spring.factories文件的缓存结果
static final Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap<>();
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 {
// String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
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;
}
通过debug发现会加载三个jar的META-INF/spring.factories文件
spring-boot-2.7.18.jar
spring-boot-autoconfigure-2.7.18.jar
spring-beans-5.3.31.jar
查询引导注册组件初始化器、上下文初始化器、监听器就是从以上三个spring.factories文件中获取BootstrapRegistryInitializer、ApplicationContextInitializer、ApplicationListener这三个接口的实现类。
BootstrapRegistryInitializer在ApplicationContext创建之前
对注册表进行配置,并注册一些启动时的关键组件。它主要应用于SpringCloud
的场景中,用来初始化那些在应用上下文加载之前需要配置的组件,比如配置中心
、服务注册和发现
等。
@FunctionalInterface
public interface BootstrapRegistryInitializer {
void initialize(BootstrapRegistry registry);
}
从以上三个jar的spring.factories文件没有获取到初始化器,表示这里也没用到它,等以后解析SpringCloud源码时候再做深究。
ApplicationContextInitializer主要作用是在Spring应用上下文 (ApplicationContext) 刷新之前
进行自定义的初始化操作。它允许开发者在应用上下文完全启动和加载所有Bean定义之前
进行特定的配置和设置。
@FunctionalInterface
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}
从以上三个jar的spring.factories文件获取到7个上下文初始化器,前5个来自spring-boot-2.7.18.jar,最后2个来自spring-boot-autoconfigure-2.7.18.jar。
ApplicationContext
设置一个唯一的上下文ID,尤其在多上下文应用程序中有助于区分和管理不同的上下文实例。默认情况下,ID 会基于应用的环境和属性生成ApplicationContextInitializer
的初始化任务,实现灵活组合多个初始化任务。此组件将任务委派给自定义的ApplicationContextInitializer
列表,使初始化更灵活和可定制MetadataReaderFactory
实例,以便于Spring扫描类路径和读取类元数据,减少I/O操作和开销,提高性能后续篇章会单独解析每一个初始化器。
ApplicationListener作用是监听Spring框架中内置的各种事件(如上下文刷新事件、上下文关闭事件等),也可以监听自定义的事件。基于事件触发执行逻辑,常用于对生命周期事件的监听以及自定义事件的处理
,帮助实现松耦合的事件驱动架构。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
// 处理应用程序事件
void onApplicationEvent(E event);
static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
return event -> consumer.accept(event.getPayload());
}
}
从以上三个jar的spring.factories文件获取到8个监听器,前7个来自spring-boot-2.7.18.jar,最后一个来自spring-boot-autoconfigure-2.7.18.jar。
ApplicationListener
的代理,将事件转发给多个监听器。这使得可以集中管理多个监听器application.properties
或环境配置设置日志级别和格式。Spring Boot 的日志系统初始化通常是由该监听器负责Environment
准备阶段后调用 EnvironmentPostProcessor
,允许对环境变量进行进一步处理,例如动态配置属性值后续篇章会单独解析每一个监听器器。
// 3.推断启动类Class
this.mainApplicationClass = deduceMainApplicationClass();
通过创建一个异常栈追踪来找到调用
main
方法的类。以下是对它的逐步分析
new RuntimeException().getStackTrace()
获取当前执行线程的堆栈追踪信息
方法调用的顺序
,每个元素都是一个StackTraceElement
对象StackTraceElement
,查找方法名为main
的元素
main
的元素,则可以通过stackTraceElement.getClassName()
获取该类的全限定名,然后使用Class.forName()
加载该类ClassNotFoundException
,但这里捕获了异常并选择继续执行null
// SpringApplication类方法
private Class<?> mainApplicationClass;
...
this.mainApplicationClass = deduceMainApplicationClass();
...
private Class<?> deduceMainApplicationClass() {
try {
// 获取当前执行线程的堆栈追踪信息
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
// 遍历每个元素
for (StackTraceElement stackTraceElement : stackTrace) {
// 获取方法名为main的元素
if ("main".equals(stackTraceElement.getMethodName())) {
// 通过元素类权限定名的Class.forName()获取Class对象
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
SpringApplication.run()
:作为SpringBoot应用的启动入口,它负责创建SpringApplication
对象,并调用其run
方法进行启动推断Web应用类型
:SpringBoot应用分为三种类型:NONE
、SERVLET
、REACTIVE
,根据类路径中的特定类进行推断读取spring.factories文件
:在SpringBoot启动过程中,从META-INF/spring.factories
文件加载初始化器和监听器,以便实现自动配置和事件处理推断启动类
:通过堆栈追踪找到调用main
方法的类,即应用的主启动类