系列文章目录
Spring Boot启动原理
- 系列文章目录
- 一、Spring Boot启动的宏观流程图
- 二、Spring Boot启动流程
- 2.1 初始化new SpringApplication
- 2.1.1Spring Boot入口
- 2.1.2初始化SpringApplication
- 2.1.2.1判断当前应用程序类型
- 2.1.2.2设置应用程序的所有初始化器(initializers)
- 2.1.2.3设置应用程序的所有监听器(listeners)
- 2.1.2.4设置应用程序运行主类
- 2.2执行run方法
- 2.2.1开启计时器
- 2.2.2初始化应用上下文和初始化异常报告
- 2.2.3设置Headless属性
- 2.2.4获取所有注册的listeners并发布ApplicationStartingEvent事件。
- 2.2.5获取并解析应用程序在命令行上传入的参数
- 2.2.6读取环境配置信息
- 2.2.7设置忽略BeanInfo
- 2.2.8.打印Banner
- 2.2.9创建上下文createApplicationContext
- 2.2.10.获取所有异常报告器
- 2.2.11准备上下文prepareContext
- 2.2.12.刷新上下文refreshContext
- 2.2.13上下文后置处理
- 2.2.14停止计时器
- 2.2.15.打印应用程序启动信息
- 2.2.16 通知监听器,应用程序正在运行
- 2.2.17执行所有Runner
- 2.2.18.返回上下文对象context
一、Spring Boot启动的宏观流程图
二、Spring Boot启动流程
2.1 初始化new SpringApplication
2.1.1Spring Boot入口
@SpringBootApplication
public class SampleWebJspApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SampleWebJspApplication.class, args);
}
}
2.1.2初始化SpringApplication
准备阶段,在程序运行之前初始化一些属性,用于在后序启动应用程序过程中。
//创建 SpringApplication 实例时,初始化各种重要的属性,包括应用程序的主要来源、Web 应用类型、初始化器和监听器等
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//根据类路径来推断 Spring Boot 应用程序的 Web 应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//获取所有实现了 ApplicationContextInitializer 接口的实例,并将它们设置为应用程序上下文的初始化器(initializers)。
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//获取所有实现了 ApplicationListener 接口的实例,并将它们设置为应用程序的监听器(listeners)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//4.设置应用程序运行主类
this.mainApplicationClass = deduceMainApplicationClass();
}
2.1.2.1判断当前应用程序类型
//根据类路径来推断 Spring Boot 应用程序的 Web 应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//根据类路径中存在的特定类来推断 Spring Boot 应用程序的 Web 应用类型
static WebApplicationType deduceFromClasspath() {
//检查类路径中是否存在 WEBFLUX_INDICATOR_CLASS 并且不存在 WEBMVC_INDICATOR_CLASS 和 JERSEY_INDICATOR_CLASS。
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
//即响应式 Web 应用程序
return WebApplicationType.REACTIVE;
}
//检查类路径中是否存在当前遍历的类名。
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
//表示非 Web 应用程序。
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
2.1.2.2设置应用程序的所有初始化器(initializers)
//获取所有实现了 ApplicationContextInitializer 接口的实例,并将它们设置为应用程序上下文的初始化器(initializers)。
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
//通过加载 "spring.factories" 配置文件中指定类型的工厂名称,并创建对应的工厂实例,然后根据工厂实例的排序顺序返回一个排序后的工厂实例的集合。
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.factories" 配置文件中加载指定类型的工厂名称,并将它们放入一个有序的集合中
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//通过工厂名称创建指定类型的工厂实例,并将它们放入一个列表中。
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
//对工厂实例列表进行排序。
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
上面这段代码主要是通过加载 “spring.factories” 配置文件中指定类型的工厂名称,并创建对应的工厂实例,然后根据工厂实例的排序顺序返回一个排序后的工厂实例的集合。关键方法是SpringFactoriesLoader.loadFactoryNames()方法。
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
//只会返回key为org.springframework.context.ApplicationContextInitializer的value
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
MultiValueMap<String, String> result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Map.Entry<?, ?> entry = (Map.Entry)var6.next();
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
loadFactoryNames()该方法会从类路径的 META-INF/spring.factories 读取相应配置文件,读取配置文件中key为org.springframework.context.ApplicationContextInitializer对应的value。例如,spring-boot这个jar包它的 META-INF/spring.factories 部分定义为:
获取到全类名之后,之后会进行实例化,并返回实例。如下代码:
//通过工厂名称创建指定类型的工厂实例,并将它们放入一个列表中。
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, 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;
}
2.1.2.3设置应用程序的所有监听器(listeners)
这只应用程序的listeners与设置初始化器(initializers)的方式相同,会从类路径的 META-INF/spring.factories 读取相应配置文件,读取配置文件中key为org.springframework.context.ApplicationListener对应的value。再根据拿到的全类名进行实例化,最后完成设置应用程序的所有监听器。
2.1.2.4设置应用程序运行主类
//4.设置应用程序运行主类
this.mainApplicationClass = deduceMainApplicationClass();
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
到这里SpringApplication实例的初始化和一些准备工作就结束了。
2.2执行run方法
宏观上run方法都做了哪些工作:
public ConfigurableApplicationContext run(String... args) {
//1.这里首先创建了一个 StopWatch 对象用于计时监控整个应用程序的启动时间。
StopWatch stopWatch = new StopWatch();
//启动计时监控
stopWatch.start();
//2.初始化应用上下文
ConfigurableApplicationContext context = null;
//初始化异常报告
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//3.配置 Headless 属性:Headless 模式用于在没有显示器、键盘或鼠标的环境中运行应用程序。
// 这里设置了 Headless 属性,以便在没有 GUI 的环境中正确启动应用程序。
configureHeadlessProperty();
//4.获取 SpringApplicationRunListeners:这一步是为了获取所有注册的 SpringApplicationRunListener 监听器。
// 这些监听器在应用程序的不同生命周期阶段会被通知,从而能够对应用程序进行扩展和自定义处理。
SpringApplicationRunListeners listeners = getRunListeners(args);
//通知监听者,开始启动
// 这里通过调用 listeners.starting() 来通知所有的 SpringApplicationRunListener 监听器,应用程序即将开始启动。
listeners.starting();
try {
//5.获取并解析应用程序在命令行上传入的参数。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
/*
6.根据SpringApplicationRunListeners以及参数来准备环境:Spring 环境包含了应用程序的配置信息,
它由多个属性源组成,包括默认属性、命令行参数、系统属性、配置文件等。
*/
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//7.配置忽略 BeanInfo:这是一个特定的处理,用于在某些环境中忽略对 JavaBean 的分析。
configureIgnoreBeanInfo(environment);
//8.打印 Banner:Banner 是在应用程序启动时打印的一个 ASCII 艺术字或自定义标志。
Banner printedBanner = printBanner(environment);
//9.创建Spring上下文,其中包含了所有的 Spring Bean 和配置。
context = createApplicationContext();
//10.获取所有注册的 SpringBootExceptionReporter 实现类的实例。
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//11.Spring上下文前置处理:包括设置环境、添加 BeanPostProcessor、注册 BeanDefinition 等。
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//12.Spring上下文刷新:这将触发 Spring Bean 的实例化和依赖注入等过程。
refreshContext(context);
//13.spring上下文后置处理:可以在这里执行额外的初始化操作。
afterRefresh(context, applicationArguments);
//14.停止计时器
stopWatch.stop();
//15.如果设置了 logStartupInfo,则会记录应用程序启动信息,包括启动时间和主类等。
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//16.调用 listeners.running(context) 来通知所有监听者,应用程序正在运行。
listeners.started(context);
//17.执行所有注册的 ApplicationRunner 和 CommandLineRunner,这些是在应用程序启动完成后自动执行的任务。
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
//18.返回上下文对象
return context;
}
下面将会详细的介绍各个环节:
2.2.1开启计时器
//1.这里首先创建了一个 StopWatch 对象用于计时监控整个应用程序的启动时间。
StopWatch stopWatch = new StopWatch();
//启动计时监控
stopWatch.start();
//start 方法时,它会开始记录给定任务的运行时间,确保不会重复计时同一个任务。如果你尝试在计时器运行时再次调用
//start 方法,它将会抛出异常,防止重复启动计时器。
public void start(String taskName) throws IllegalStateException {
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
} else {
this.currentTaskName = taskName;
this.startTimeMillis = System.currentTimeMillis();
}
}
2.2.2初始化应用上下文和初始化异常报告
//2.初始化应用上下文
ConfigurableApplicationContext context = null;
//初始化异常报告
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
2.2.3设置Headless属性
//配置 Headless 属性:Headless 模式用于在没有显示器、键盘或鼠标的环境中运行应用程序。
// 这里设置了 Headless 属性,以便在没有 GUI 的环境中正确启动应用程序。
configureHeadlessProperty();
/*
* @description:这段代码用于配置 Java AWT 的 Headless 属性。Headless 模式是在没有显示器、键盘或鼠标的环境中运行 Java 应用程序,
* 通常在服务器上或无界面的操作系统中使用。当应用程序在 Headless 模式下运行时,Java AWT 将不会初始化图形界面,以减少资源消耗。
* @author: wangwei
* @date: 2023/7/26 19:39
* @param: []
* @return: void
**/
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
2.2.4获取所有注册的listeners并发布ApplicationStartingEvent事件。
这里依然是使用了getSpringFactoriesInstances()方法来获取实例。
还是从 META-INF/spring.factories 中读取Key为 org.springframework.boot.SpringApplicationRunListener 的Values,最后根据全类名进行实例化。
//获取 SpringApplicationRunListeners:这一步是为了获取所有注册的 SpringApplicationRunListener 监听器。
// 这些监听器在应用程序的不同生命周期阶段会被通知,从而能够对应用程序进行扩展和自定义处理。
SpringApplicationRunListeners listeners = getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
//通过加载 "spring.factories" 配置文件中指定类型的工厂名称,并创建对应的工厂实例,然后根据工厂实例的排序顺序返回一个排序后的工厂实例的集合。
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.factories" 配置文件中加载指定类型的工厂名称,并将它们放入一个有序的集合中
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//通过工厂名称创建指定类型的工厂实例,并将它们放入一个列表中。
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
//对工厂实例列表进行排序。
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
最后回到SpringApplication中的run方法中
-------------------------------------------------
//通知监听者,开始启动
// 这里通过调用 listeners.starting() 来通知所有的 SpringApplicationRunListener 监听器,应用程序即将开始启动。
listeners.starting();
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
//事件传播机制的实现。它用于广播(multicast)一个应用程序事件给所有对该事件感兴趣的监听器,并支持在多线程环境中异步执行监听器的处理逻辑。
public void multicastEvent(ApplicationEvent event) {
this.multicastEvent(event, this.resolveDefaultEventType(event));
}
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Executor executor = this.getTaskExecutor();
Iterator var5 = this.getApplicationListeners(event, type).iterator();
while(var5.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var5.next();
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
this.invokeListener(listener, event);
}
}
}
2.2.5获取并解析应用程序在命令行上传入的参数
//获取并解析应用程序在命令行上传入的参数。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
public DefaultApplicationArguments(String[] args) {
Assert.notNull(args, "Args must not be null");
this.source = new Source(args);
this.args = args;
}
//个命令行参数解析器的实现,用于将传入的命令行参数数组 args 解析成一个
//CommandLineArgs 对象。
//CommandLineArgs 是一个自定义类,用于存储解析后的命令行参数信息。
public CommandLineArgs parse(String... args) {
CommandLineArgs commandLineArgs = new CommandLineArgs();
String[] var3 = args;
int var4 = args.length;
for(int var5 = 0; var5 < var4; ++var5) {
String arg = var3[var5];
if (arg.startsWith("--")) {
String optionText = arg.substring(2, arg.length());
String optionValue = null;
String optionName;
if (optionText.contains("=")) {
optionName = optionText.substring(0, optionText.indexOf(61));
optionValue = optionText.substring(optionText.indexOf(61) + 1, optionText.length());
} else {
optionName = optionText;
}
if (optionName.isEmpty() || optionValue != null && optionValue.isEmpty()) {
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
}
commandLineArgs.addOptionArg(optionName, optionValue);
} else {
commandLineArgs.addNonOptionArg(arg);
}
}
return commandLineArgs;
2.2.6读取环境配置信息
/*
根据SpringApplicationRunListeners以及参数来准备环境:Spring 环境包含了应用程序的配置信息,
它由多个属性源组成,包括默认属性、命令行参数、系统属性、配置文件等。
*/
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
/*
* @description:为应用程序创建并准备一个配置环境(ConfigurableEnvironment)。它将应用程序的命令行参数、配置属性等信息设置到环境中,
* 并通知相关的监听器。通过这个准备环境的过程,应用程序可以在后续的启动过程中正确加载和配置所需的属性和配置信息。
* @author: wangwei
* @date: 2023/7/26 20:14
* @param: [listeners, applicationArguments]
* @return: org.springframework.core.env.ConfigurableEnvironment
**/
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
//获取或创建应用程序的环境对象。环境是一个配置容器,用于保存应用程序的配置信息、属性和其他环境相关的内容。
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置环境,将应用程序的命令行参数(applicationArguments)设置到环境中。
configureEnvironment(environment, applicationArguments.getSourceArgs());
//将配置属性源(ConfigurationPropertySource)附加到环境中,用于加载配置属性。
ConfigurationPropertySources.attach(environment);
//通知监听器,应用程序的环境已准备好。这是应用程序启动过程中通知监听器的一个生命周期事件。
listeners.environmentPrepared(environment);
//将环境对象与当前的 Spring 应用程序实例进行绑定,以便可以在应用程序中访问该环境。
bindToSpringApplication(environment);
//如果当前环境不是自定义环境,则执行下面的逻辑。
if (!this.isCustomEnvironment) {
/*
创建一个 EnvironmentConverter 对象,并将现有的环境对象 environment 作为参数传入。
调用 convertEnvironmentIfNecessary 方法,将当前环境转换成指定类型的环境。这通常是为了适应不同的环境配置。
*/
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//再次将配置属性源(ConfigurationPropertySource)附加到环境中,用于加载配置属性。
ConfigurationPropertySources.attach(environment);
return environment;
}
2.2.7设置忽略BeanInfo
//配置忽略 BeanInfo:这是一个特定的处理,用于在某些环境中忽略对 JavaBean 的分析。
configureIgnoreBeanInfo(environment);
2.2.8.打印Banner
//打印 Banner:Banner 是在应用程序启动时打印的一个 ASCII 艺术字或自定义标志。
Banner printedBanner = printBanner(environment);
2.2.9创建上下文createApplicationContext
//创建Spring上下文,其中包含了所有的 Spring Bean 和配置。
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
//获取应用程序上下文的类类型。这个类类型可以在应用程序配置中指定,表示要使用的自定义上下文类。
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
//如果 this.webApplicationType 为 SERVLET,则使用默认的 Servlet Web 上下文类
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
//如果 this.webApplicationType 为 SERVLET,则使用默认的 Servlet Web 上下文类
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
//对于其他情况,默认使用普通的上下文类类型 DEFAULT_CONTEXT_CLASS。
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
ex);
}
}
//实例化 contextClass 对象,得到一个应用程序上下文的实例。
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
2.2.10.获取所有异常报告器
//获取所有注册的 SpringBootExceptionReporter 实现类的实例。
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
//加载指定 type 类型的工厂类名称。
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//根据工厂类名称创建实例对象。
//这个方法将通过反射实例化工厂类,并调用其对应的静态工厂方法或构造函数。
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
2.2.11准备上下文prepareContext
/*
* @description:准备应用程序的上下文。它将配置环境设置到上下文中,调用初始化器进行上下文初始化,注册一些特定的单例 Bean,加载应用程序的资源,
* 并通知监听器应用程序的上下文已准备好和已加载完成。
* 这个过程是 Spring Boot 应用程序启动过程中的重要一环,确保应用程序上下文正确配置和初始化。
* @author: wangwei
* @date: 2023/7/26 20:55
* @param:
* @return:
**/
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//将应用程序的配置环境 environment 设置到上下文 context 中。
context.setEnvironment(environment);
//对应用程序的上下文进行后置处理。这是 Spring 的扩展点,可以在应用程序上下文创建后,对其进行额外的定制。
postProcessApplicationContext(context);
//调用应用程序的初始化器(ApplicationContextInitializer)对上下文进行初始化。初始化器是一种在 Spring Boot 应用程序启动时自动执行的扩展机制。
applyInitializers(context);
//通知监听器,应用程序的上下文已准备好。这是应用程序启动过程中通知监听器的一个生命周期事件。
listeners.contextPrepared(context);
if (this.logStartupInfo) {
//如果应用程序的上下文没有父级上下文,则记录启动信息。
logStartupInfo(context.getParent() == null);
//记录应用程序的启动配置信息和配置文件。
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
//向上下文注册特定的单例 Bean:
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//注册 applicationArguments,应用程序的命令行参数对象。
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
//注册 printedBanner,打印的横幅对象。
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
//设置是否允许覆盖 Bean 定义,即是否允许多次注册同一名称的 Bean。
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
//设置是否允许覆盖 Bean 定义,即是否允许多次注册同一名称的 Bean。
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//设置是否允许覆盖 Bean 定义,即是否允许多次注册同一名称的 Bean。
load(context, sources.toArray(new Object[0]));
//设置是否允许覆盖 Bean 定义,即是否允许多次注册同一名称的 Bean。
listeners.contextLoaded(context);
}
2.2.12.刷新上下文refreshContext
//Spring上下文刷新:这将触发 Spring Bean 的实例化和依赖注入等过程。
refreshContext(context);
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
//Spring 应用程序上下文刷新的核心方法,负责进行初始化、配置和处理各种 Bean,使得应用程序上下文准备好运行,可以在后续的运行中使用和管理 Bean。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//准备应用程序上下文进行刷新操作。
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//准备应用程序上下文进行刷新操作。
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//准备 Bean 工厂供在上下文中使用
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//允许上下文子类后处理 Bean 工厂,做一些额外的配置。
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//调用注册在上下文中的 Bean 工厂后置处理器,这些后置处理器用于在 Bean 工厂实例化 Bean 之前修改、扩展或配置 Bean 工厂。
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//注册 Bean 后置处理器(BeanPostProcessor)到 Bean 工厂中。Bean 后置处理器在 Bean 的实例化、初始化以及销毁的过程中,可以对 Bean 进行拦截和处理。
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//初始化消息资源,用于国际化和本地化消息处理。
initMessageSource();
// Initialize event multicaster for this context.
//初始化应用程序事件广播器,用于发布和处理应用程序事件。
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//在特定上下文子类中,进行其他特殊 Bean 的初始化和配置。
onRefresh();
// Check for listener beans and register them.
//检查并注册事件监听器
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//实例化所有剩余的(非懒加载)单例 Bean。
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
//完成刷新操作,发布 Spring 容器刷新事件。
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
//清空 Spring 核心中的常见的元数据缓存,以便在运行时不再需要单例 Bean 的元数据信息。
resetCommonCaches();
}
}
}
2.2.13上下文后置处理
//在默认情况下不执行任何操作,留给子类去扩展和实现。
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
2.2.14停止计时器
//停止
StopWatch 的计时,并记录上一次计时任务的执行时间。
public void stop() throws IllegalStateException {
if (this.currentTaskName == null) {
throw new IllegalStateException("Can't stop StopWatch: it's not running");
} else {
long lastTime = System.currentTimeMillis() - this.startTimeMillis;
this.totalTimeMillis += lastTime;
this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
if (this.keepTaskList) {
this.taskList.add(this.lastTaskInfo);
}
++this.taskCount;
this.currentTaskName = null;
}
}
2.2.15.打印应用程序启动信息
//如果设置了 logStartupInfo,则会记录应用程序启动信息,包括启动时间和主类等。
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
2.2.16 通知监听器,应用程序正在运行
//是通知所有注册的 SpringApplicationRunListener 实现类,应用程序上下文已经启动完成。
public void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
2.2.17执行所有Runner
//它在应用程序上下文创建完成后,调用所有注册的 ApplicationRunner 和 CommandLineRunner bean 的 run() 方法。
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
2.2.18.返回上下文对象context
return context;