- SpringApplication的静态run()方法
- SpringApplication的构造方法
- 整体流程
- 设置基础资源 primarySources
- 推断应用类型 WebApplicationType
- 设置应用上下文初始化器、应用监听器
- 推断主类 deduceMainApplicationClass()
- SpringApplication的实例run()方法
- 整体流程
- 计时器 StopWatch
- spring应用运行监听器 SpringApplicationRunListener
- 准备环境 prepareEnvironment()
- 打印Banner printBanner()
- 创建应用上下文 createApplicationContext()
- 准备上下文 prepareContext()
- springboot的启动过程
使用的 springboot 源码版本 2.3.12.RELEASE,这是2.3.x系列的最后一个版本。
springboot应用的启动方式很多,可以在引导类中配置 ConfigurableApplicationContext,可以继承 SpringBootServletInitializer 重写SpringApplication的配置…不同的启动方式、不同版本的源码,启动过程、调用的一些方法有所差别,整体过程大同小异,此处以默认的启动方式进行讲述。
SpringApplication的静态run()方法
引导类
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
//调用 SpringApplication 的静态方法 run() 进行启动
SpringApplication.run(DemoApplication.class, args);
}
}
实质是调用 SpringApplication 本身的另一个重载的静态 run() 方法
public static ConfigurableApplicationContext run(Class>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
这个方法做了2件事
- 调用 SpringApplication 的构造方法创建 SpringApplication 实例
- 调用 SpringApplication 实例的run()方法进行启动
这个实例 run() 方法是启动的核心|主要方法
springboot的启动思路
- 核心类是 SpringApplication ,核心方法是 SpringApplication 的实例 run() 方法
- 调用 SpringApplication 的静态run()方法进行启动,由静态 run() 方法创建 SpringApplication 实例,调用实例的 run() 方法进行启动。
SpringApplication的构造方法 整体流程
public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
//设置资源加载器 resourceLoader
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//设置要加载的基础资源 primarySources
this.primarySources = new linkedHashSet<>(Arrays.asList(primarySources));
//推断应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置应用上下文的初始化器 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置应用监听器 ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//推断主类
this.mainApplicationClass = deduceMainApplicationClass();
}
主要是做一些初始化工作
设置基础资源 primarySources
//基础资源 private Set> primarySources;
基础资源是springboot应用中的一些核心类,通常是静态run()方法传入的引导类。所谓的加载基础资源,其实就是处理这些核心类,让这些核心类生效,比如解析引导类上的@SpringBootApplication、@MapperScan,使之生效。
推断应用类型 WebApplicationType
枚举类 WebApplicationType
public enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;
//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";
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() {
//使用ClassUtils.isPresent()判断 classpath中是否有servlet或reactive的核心类,是否能用类加载器进行加载
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;
}
static WebApplicationType deduceFromApplicationContext(Class> applicationContextClass) {
//判断是否是 WebApplicationContext 类或其子类
if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.SERVLET;
}
//判断是否是 ReactiveWebApplicationContext 类或其子类
if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.REACTIVE;
}
return WebApplicationType.NONE;
}
private static boolean isAssignable(String target, Class> type) {
try {
return ClassUtils.resolveClassName(target, null).isAssignableFrom(type);
}
catch (Throwable ex) {
return false;
}
}
}
这个枚举类主要
- 定义了3种应用类型:none、servlet、reactive
- 提供了2种推断应用类型的方法:deduceFromClasspath 根据classpath中是否对应的核心类进行推断(默认方式)、deduceFromApplicationContext 根据使用的上下文类型进行推断
设置应用上下文初始化器、应用监听器
spring.factories 文件格式
#实质是 properties 文件 #key是接口,value指定该接口要使用的实现类,可指定多个实现类,都是使用全限定类名 org.springframework.boot.env.PropertySourceLoader= org.springframework.boot.env.PropertiesPropertySourceLoader, org.springframework.boot.env.YamlPropertySourceLoader
private List> initializers; private List > listeners;
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
都是调用 getSpringFactoriesInstances(),根据 spring.factories 文件中的配置获取指定接口(此处是ApplicationContextInitializer接口、ApplicationListener接口)要使用的实现类,给这些实现类创建实例。
创建的 pplicationContextInitializer、ApplicationListener 实例会赋给 SpringApplication 对应的成员变量保存,以完成这些成员变量的初始化。
SpringApplication 的实例方法 getSpringFactoriesInstances()
privateCollection getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) { //获取类加载器:如果 this.resourceLoader 不为空则使用resourceLoader的类加载器,否则使用默认的类加载 ClassLoader classLoader = getClassLoader(); //加载、解析spring.factories,得到指定接口要使用的各个实现类的全限定类名 Set names = new linkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); //通过反射调用构造方法给这些实现类创建实例 List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
SpringFactoriesLoader 的静态方法 loadFactoryNames()
public static ListloadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); //loadSpringFactories()用于加载、解析所有的spring.factories文件,返回 Map >,key是接口,value是该接口要使用的实现类列表 //getOrDefault()用于从map中获取指定key的value,即指定接口对应的实现类。如果map中没有指定的key,或者对应的value为null,则返回指定的默认值 return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map > loadSpringFactories(@Nullable ClassLoader classLoader) { //先从缓存中获取,有就直接返回,没有才加载、解析 spring.factories MultiValueMap result = cache.get(classLoader); if (result != null) { return result; } try { //获取所有jar包中的 meta-INF/spring.factories 文件路径 Enumeration urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); // linkedMultiValueMap,MultiValue 顾名思义 一个key可以对应多个value // 内部用 Map > 存储数据,add(key,value)添加元素时,会把value放到对应的List中 // 用于存储所有spring.factories文件的解析结果,一个键值对即一个接口的要使用的实现类,key是接口名,值List用于存储该接口要使用的实现类 result = new linkedMultiValueMap<>(); //遍历所有的spring.factories文件,逐个解析 while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); //解析为Properties Properties properties = PropertiesLoaderUtils.loadProperties(resource); //遍历各个键值对 for (Map.Entry, ?> entry : properties.entrySet()) { //key是接口名 String factoryTypeName = ((String) entry.getKey()).trim(); //value是包含多个实现类的字符串,切分为字符串数组进行遍历 for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { //添加到 linkedMultiValueMap 中 result.add(factoryTypeName, factoryImplementationName.trim()); } } } //把所有spring.factories文件的解析结果放到缓存中 cache.put(classLoader, result); //返回解析结果 return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
显然,第一次调用 getSpringFactoriesInstances() 时,就加载、解析了所有的 spring.factories 文件,把解析结果放到了缓存中,但只实例化了指定接口要使用的实现类。
后续再调用 getSpringFactoriesInstances() 时,直接从缓存中获取解析结果(map),从中获取指定接口的实现类,对其进行实例化,没有再加载、解析spring.factories文件。
推断主类 deduceMainApplicationClass()
SpringApplication 的实例方法 deduceMainApplicationClass()
private Class> deduceMainApplicationClass() {
try {
//获取各个调用的栈帧,一个栈帧对应一个方法调用
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
//遍历这些栈帧
for (StackTraceElement stackTraceElement : stackTrace) {
//找到方法名为main的栈帧
if ("main".equals(stackTraceElement.getMethodName())) {
//把该栈帧对应的java类作为主类返回
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
SpringApplication的实例run()方法 整体流程
实例run()方法用于启动spring应用,创建并刷新一个新的应用上下文
public ConfigurableApplicationContext run(String... args) {
//创建StopWatch实例(计时器、秒表),并调用StopWatch实例的start()方法开始计时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
//配置系统环境的 java.awt.headless 属性
configureHeadlessProperty();
//创建并开启 spring.factories 中指定的所有的 spring应用运行监听器 SpringApplicationRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//构建应用参数:args是main()方法的参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//配置 spring.beaninfo.ignore 属性
configureIgnoreBeanInfo(environment);
//打印Banner
Banner printedBanner = printBanner(environment);
//创建应用上下文:通过反射调用应用上下文的构造方法创建实例
context = createApplicationContext();
//准备上下文:1、对创建的上下文实例进行配置、初始化,2、配置内置的低级容器,3、加载所有BeanDefinition、完成bean的注册
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新上下文:1、registerShutdownHook() 向jvm注册ShutdownHook关闭钩子,在jvm终止时自动关闭上下文;2、调用应用上下文的 refresh() 方法进行刷新
refreshContext(context);
//预留的扩展点,默认为空实现,可在上下文刷新之后做一些自定义的操作
afterRefresh(context, applicationArguments);
//终止计时器,打印启动应用花费的时长
stopWatch.stop();
if (this.logStartupInfo) {
//日志示例:Started DemoApplication in 4.25 seconds (JVM running for 5.368)
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//至此应用已完成启动,执行 SpringApplicationRunListener 的 started() 回调方法
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// handleRunFailure()处理异常:
// 1、如果是非正常退出(exitCode != 0),则发布应用退出事件 ExitCodeEvent,使用异常处理器 SpringBootExceptionHandler 记录退出码 exitCode;
// 2、执行 SpringApplicationRunListener 的 failed() 回调方法;
// 3、使用异常报告器 SpringBootExceptionReporter 打印、记录异常信息
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
//执行 SpringApplicationRunListener 的 running() 回调方法
listeners.running(context);
}
catch (Throwable ex) {
//和上面的 handleRunFailure() 差不多,只是传入的运行监听器组是null,不会执行 SpringApplicationRunListener 的 failed() 回调方法
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
//返回应用上下文
return context;
}
计时器 StopWatch
StopWatch 是spring提供的计时器、秒表,用于统计任务的执行时间
StopWatch stopWatch = new StopWatch(); stopWatch.start();
spring应用运行监听器 SpringApplicationRunListener
SpringApplicationRunListener
这个监听器用于监听spring应用的启动事件,即监听 SpringApplication 的实例 run() 方法的执行过程,提供了一系列回调方法,可用于在启动过程中的特定阶段做一些额外操作
public interface SpringApplicationRunListener {
default void starting() {
}
default void environmentPrepared(ConfigurableEnvironment environment) {
}
default void contextPrepared(ConfigurableApplicationContext context) {
}
default void contextLoaded(ConfigurableApplicationContext context) {
}
default void started(ConfigurableApplicationContext context) {
}
default void running(ConfigurableApplicationContext context) {
}
default void failed(ConfigurableApplicationContext context, Throwable exception) {
}
}
SpringApplicationRunListeners 运行监听器组
SpringApplicationRunListener 的复数形式,这个类用于封装多个 SpringApplicationRunListener,提供多个 SpringApplicationRunListener 的批量操作
class SpringApplicationRunListeners {
private final Log log;
private final List listeners;
SpringApplicationRunListeners(Log log, Collection extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
void contextPrepared(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
void contextLoaded(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextLoaded(context);
}
}
void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
void running(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}
void failed(ConfigurableApplicationContext context, Throwable exception) {
for (SpringApplicationRunListener listener : this.listeners) {
callFailedListener(listener, context, exception);
}
}
private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context,
Throwable exception) {
//...
}
}
//调用 SpringApplication 本身的 getRunListeners() 方法 SpringApplicationRunListeners listeners = getRunListeners(args); //开启全部的 SpringApplicationRunListener listeners.starting();
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class>[] types = new Class>[] { SpringApplication.class, String[].class };
//先调用 getSpringFactoriesInstances(),根据spring.factories中的配置给要使用的SpringApplicationRunListener实现类创建实例
//再包装为 SpringApplicationRunListeners,以便批量操作所有的 SpringApplicationRunListener 实例
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
准备环境 prepareEnvironment()
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
//获取或创建环境,有现成的环境就直接使用,没有则创建
ConfigurableEnvironment environment = getOrCreateEnvironment();
//设置应用参数
configureEnvironment(environment, applicationArguments.getSourceArgs());
//绑定配置属性源、环境
ConfigurationPropertySources.attach(environment);
//至此环境已配置好,触发所有 SpringApplicationRunListener 的 environmentPrepared() 回调方法
listeners.environmentPrepared(environment);
//将配置好的环境绑定到 SpringApplication 上
bindToSpringApplication(environment);
//如果不是自定义的环境,则根据情况确定是否需要转换环境
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//重新绑定属性源、环境
ConfigurationPropertySources.attach(environment);
return environment;
}
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
//会根据应用类型创建对应的环境
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
打印Banner printBanner()
Banner接口
@FunctionalInterface
public interface Banner {
void printBanner(Environment environment, Class> sourceClass, PrintStream out);
enum Mode {
OFF,
CONSOLE,
LOG
}
}
SpringApplicaition 的 printBanner() 方法
//banner的默认打印模式是输出到控制台
private Banner.Mode bannerMode = Banner.Mode.CONSOLE;
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(null);
//通过 SpringApplicationBannerPrinter 类打印banner
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
//console模式的打印
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
SpringApplicationBannerPrinter 类,用于打印banner
class SpringApplicationBannerPrinter {
//banner的配置项
static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";
static final String DEFAULT_BANNER_LOCATION = "banner.txt";
static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
private static final Banner DEFAULT_BANNER = new SpringBootBanner();
private final ResourceLoader resourceLoader;
private final Banner fallbackBanner;
SpringApplicationBannerPrinter(ResourceLoader resourceLoader, Banner fallbackBanner) {
//...
}
Banner print(Environment environment, Class> sourceClass, Log logger) {
//...
}
Banner print(Environment environment, Class> sourceClass, PrintStream out) {
//...
}
private Banner getBanner(Environment environment) {
//...
}
private Banner getTextBanner(Environment environment) {
//...
}
private Banner getImageBanner(Environment environment) {
//...
}
private String createStringFromBanner(Banner banner, Environment environment, Class> mainApplicationClass)
throws UnsupportedEncodingException {
//...
}
private static class Banners implements Banner {
//...
}
private static class PrintedBanner implements Banner {
//...
}
}
创建应用上下文 createApplicationContext()
private Class extends ConfigurableApplicationContext> applicationContextClass;
protected ConfigurableApplicationContext createApplicationContext() {
//确定要创建的应用上下文类型:如果没有指定应用上下文的类型,则根据推断得到的应用类型自动确定
Class> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
//通过反射调用对应的构造方法创建应用上下文的实例
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
准备上下文 prepareContext()
上一步创建应用上下文,只是通过反射调用应用上下文的构造方法创建实例,尚未对上下文实例进行配置。
这一步用于配置创建好的上下文实例,主要包括
- 配置、初始化应用上下文:给应用上下文设置环境;执行应用上下文的后置处理,给一些尚未填充的属性设置值;执行应用上下文初始化器(ApplicationContextInitializer),对应用上下文进行初始化
- 打印应用的启动信息
- 配置内置的低级容器 (至此,高级容器、内置的低级容器都已创建、配置完毕)
- 加载所有的 BeanDefinition,完成bean的注册
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//设置环境
context.setEnvironment(environment);
//执行应用上下文的后置处理,主要是设置一些尚未填充的属性
postProcessApplicationContext(context);
//应用初始化器:执行各个应用上下文初始化器(ApplicationContextInitializer)的 shaninitialize() 方法,对上下文进行初始化
applyInitializers(context);
//至此上下文准备完毕,执行各个 SpringApplicationRunListener 的 contextPrepared() 回调方法
listeners.contextPrepared(context);
//如果允许打印启动信息,则
if (this.logStartupInfo) {
//打印应用正在启动的信息,示例:Starting DemoApplication on DESKTOP-E74978P with PID 49824
logStartupInfo(context.getParent() == null);
//打印 spring.profiles.active 激活的环境,示例:The following profiles are active: test
logStartupProfileInfo(context);
}
//获取内置的低级容器,以对内置的低级容器进行一些配置
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//注册一些特殊的单例bean
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
//配置是否允许bean定义覆盖
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//配置是否允许bean的懒加载。默认false,高级容器启动时就实例化所有单例
if (this.lazyInitialization) {
//如果允许bean的懒加载,则给内置的低级容器添加懒加载对应的后置处理器
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
//获取所有的资源,所有的资源指的是 this.primarySources、this.sources 2个属性指定的资源
Set
springboot中有两种上下文
1、bootstrap
- 应用程序的父上下文,先于applicaton加载,且优先级高于applicaton(boostrap设置的属性不会被applicaton覆盖)
- 主要用于从外部源加载配置,比如springcloud config从配置中心加载配置,以及配置文件的加/解密
2、application
- 应用程序上下文,主要用于自动配置、spring容器相关操作
run()方法的执行过程
springboot的启动过程
准备阶段
- 配置 Spring Boot Bean
- 推断应用类型
- 推断引导类
- 加载应用上下文初始器
启动阶段
- 运行监听器,监听 Spring Boot 事件
- 创建 Spring 应用上下文
自动化装配



