一、Springboot
简介
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
Spring Boot 是一个开源的 Java 框架,用于简化 Spring 应用程序的开发。它是 Spring 生态系统的一部分,旨在帮助开发人员更快速和方便地创建独立、生产级的 Spring 应用。
Spring Boot 是一个强大的框架,极大地简化了 Spring 应用的开发过程,特别适合构建微服务和 RESTful API。
主要特点
-
快速入门:Spring Boot 通过约定优于配置的原则,使开发者可以快速上手,无需繁琐的 XML 配置。
-
自带嵌入式服务器:Spring Boot 支持嵌入式服务器(如 Tomcat、Jetty 和 Undertow),开发者无需单独部署应用服务器,可以直接运行 Java 应用。
-
自动配置:Spring Boot 提供了自动配置功能,根据项目的类路径和配置自动设置 Spring 应用的各种组件,减少了大量的配置工作。
-
生产级特性:Spring Boot 内置了许多生产环境所需的功能,如健康检查、指标监控、应用配置管理等。
-
开箱即用:Spring Boot 提供了一系列的 starter 依赖,简化了依赖管理。例如,
spring-boot-starter-web
可以帮助快速构建基于 Spring MVC 的 web 应用。 -
支持微服务架构:Spring Boot 适合构建微服务应用,结合 Spring Cloud,能够实现分布式系统的开发。
核心概念
-
启动器(Starters):启动器是一组方便的依赖描述符,允许用户在构建时快速引入常用的库。例如:
-
spring-boot-starter-data-jpa
:用于与 JPA 数据库交互。 -
spring-boot-starter-web
:用于构建 Web 应用。
-
-
自动配置类:Spring Boot 中的
@EnableAutoConfiguration
注解可以启用自动配置,开发者可以通过@Configuration
注解创建自定义配置。 -
应用配置:Spring Boot 使用
application.properties
或application.yml
文件来集中管理应用配置。支持不同环境的配置(如开发、测试、生产)通过文件名后缀进行管理。 -
命令行界面(CLI):Spring Boot 提供命令行界面,支持快速创建和测试 Spring 应用。
二、启动流程
1.@SpringBootApplication注解
众所周知,在 SpringBoot 启动类上都少不了@SpringBootApplication注解。可以肯定的是,所有的标准的springboot的应用程序都是从run方法开始的。
每个Spring Boot应用程序都包含一个 main 方法,这是程序的入口,也是 JVM 启动的切入点。常见的 main 方法例子如下:
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
查看@SpringBootApplication注解源码如下,
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
可以看到,其核心使用了@SpringBootConfiguration 和 @EnableAutoConfiguration,前者的作用类似于 @Configuration 表明是一个Java 配置类。而@EnableAutoConfiguration 则是将所有符合配置条件的 Bean 都加载到 IOC 容器中。
2.SpringApplication.run() 方法
接下来是调用SpringApplication.run() 方法启动整个Spring Boot程序。
让我们深入了解一下SpringApplication.run的内部工作原理。这个方法首先创建了一个SpringApplication对象,然后调用了run方法。
SpringApplication.run(MyApplication.class, args)实际上执行了多步操作。这行代码主要做了以下事情:
- 创建
SpringApplication
实例。 - 配置 SpringApplication(应用启动前的一些准备工作)。
- 运行该实例。
3.SpringApplication 实例创建
在 SpringApplication
的构造方法中,进行了一些初始化操作:
- 确定应用程序的类型(是
Servlet
应用还是Reactive
应用)。 - 初始化应用上下文(
ApplicationContext
)。 - 读取和存储初始的运行监听器(
SpringApplicationRunListeners
)。 - 初始化应用程序引导程序(
ApplicationContextInitializers
)。
确定应用程序类型
在SpringApplication的构造方法内,首先会通过 WebApplicationType.deduceFromClasspath()方法来进行 Web 应用类型的推断。判断当前应用程序的容器,默认使用的是Servlet 容器,除了servlet之外,还有NONE 和 REACTIVE (响应式编程)。
其中,WebApplicationType是一个枚举类, 它定义了可能的 Web 应用类型,该枚举类提供了三类定义:枚举类型、推断类型的方法和用于推断的常量。枚举类型包括非 Web 应用、基于 SERVLET 的 Web 应用和基于 REACTIVE 的 Web 应用,代码如下。
WebApplicationType内针对Web应用类型提供了两个推断方法:deduceFromClasspath 方法和
deduceFromApplicationContext 方法。
那我们就进入WebApplicationType.deduceFromClasspath()方法,下面重点分析该方法的实现。
deduceFromClasspath方法是基于 classpath 中类是否存在来进行类型推断的,就是判断指定的类是否存在于 classpath 下, 并根据判断的结果来进行组合推断该应用属于什么类型。deduceFromClasspath 在判断的过程中用到了 ClassUtils 的 isPresent 方法。isPresent方法的核心机制就是通过反射创建指定的类,根据在创建过程中是否抛出异常来判断该类是通过上面的源代码,我们可以看到 deduceFromClasspath 的推断逻辑如下。
- 当 DispatcherHandler 存在,并且 DispatcherServlet 和 ServletContainer 都不存在,则返回类型为WebApplicationType.REACTIVE。
- 当 SERVLET 或ConfigurableWebApplicationContext 任何一个不存在时,说明当前应用为非 Web 应用,返回 WebApplicationType NONE。
- 当应用不为 REACTIVE Web 应用,并且 SERVLET 和ConfigurableWebApplicationContext都存在的情况下,则为 SERVLET 的 Web 应用,返回 WebApplicationType .SERVLET。
加载ApplicationContextlnitializer系列初始化器
ApplicationContextlnitializer是SpringIOC 容器提供的一个接口,它是一个回调接口。
主要目的是允许用户在 ConfigurableApplicationContext 类型(或其子类型)的 ApplicationContext 做 refresh 方法调用刷新之前,对 ConfigurableApplicationContext 实例做进一步的设 置或处理。通常用于应用程序上下文进行编程初始化的 Web 应用程序中。
ApplicationContextlnitializer 接口的 initialize() 方法主要是为了初始化指定的应用上下文。而对应的上下文由参数传入,参数为 ConfigurableApplicationContext 的子类。
在完成了 Web 应用类型推断之后,ApplicationContextlnitializer 便开始进行加载工作,该过程可分两步骤:获得相关实例和设置实例。对应的方法分别为 getSpringFactoriesInstances和 setlnitializers。
(1)进入getSpringFactoriesInstances()方法,
可以看到,getSpringFactorieslnstances 方 法 依 然 是 通 过 SpringFactoriesL oader 类 的loadFactoryNames() 方法来获得 META-INF/spring.factories 文件中注册的对应配置。
当获取到这些配置类的全限定名之后,便可调用 createSpringFactoriesInstances() 方法进行相应的实例化操作。
完成获取配置类集合和实例化操作之后,调用 setlnitializers 方法将实例化的集合添加到SprinaApplication的成员变量initializers中,类型为List<ApplicationContextlnitiali-zer<?>>。
(2)退出getSpringFactoriesInstances()方法,下一步是setlnitializers()方法,
进入setlnitializers()方法,
setlnitializers()方法将接收到的 initializers 作为参数创建了一个新的 List,并将其赋值给SpringApplication 的 initializers 成员变量。由于是创建了新的 List,并且直接赋值,因此该方法一旦被调用,便会导致数据覆盖,使用时需注意。
加载ApplicationListener系列监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class))这行代码是 SpringApplication 的核心构造方法中的逻辑。它用于加载实现了 ApplicationListener 接口的监听器实例集合,并将该监听器实例集合设置到 SpringApplication 的 listeners 变量中。
加载监听器也是从 META-INF/spring.factories 配置文件中加载的,与初始化不同的是,监听器加载的是实现了 ApplicationListener 接口的类。
我们进入getSpringFactoriesInstances()方法,就可以看到SpringFactoriesLoader.loadFactoryNames(type, classLoader)这里是通过 SpringFactoriesLoader 类的 loadFactoryNames 方法来获取 META-INF/spring.factories 中配置 key 为 org.springframework.context.ApplicationListener 的数据。
ApplicationListener 是 Spring 中应用程序事件监听器实现的接口。它基于观察者设计模式的java.util.EventListener 接口的标准。在注册到 Spring ApplicationContext 时,事件将进行相应的过滤,只有匹配的事件对象才会使该监听器被调用。
(1)在 ApplicationListener 接口中,我们可以看到它定义了一个 onApplicationEvent(E event) 方法,当监听事件被触发时,onApplicationEvent 方法就会被调用执行。onApplicationEvent 方法一般用于处理应用程序事件,参数 event 为 ApplicationEvent 的子类,也就是具体要响应处理的各种类型的应用程序事件。
例如,当某个特定事件发生时,你可能想要记录日志、更新数据库、发送电子邮件等等。
(2)另外,ApplicationListener 接口还提供了一个静态方法 forPayload(Consumer<T> consumer),用于创建一个新的 ApplicationListener 实例。
这个方法接受一个 Consumer<T> 类型的参数,这个参数是一个函数接口,它接受一个泛型参数 T,并对其执行一些操作。通过这个方法,你可以将一个 Consumer 函数作为参数,然后返回一个对应的事件监听器。
这个监听器会在事件发生时,调用 Consumer 函数处理事件的有效载荷【即事件中包含的有效信息或数据】。
自定义监听器也跟初始化器一样,依葫芦画瓢就可以了,这里不再举例。
推断应用入口类
deduceMainApplicationClass()这个方法仅仅是找到main方法所在的类,为后面的扫包作准备。deduce是推断的意思。所以准确地说,这个方法作用是推断出主方法所在的类。
deduceMainApplicationClass的实现原理比较巧妙,新建了一个运行时异常对象,通过这个对象获取当前的调用函数堆栈数组StackTrace,之后遍历这个堆栈数组,找到方法名为main的类,返回这个类。
SpringBoot将deduceMainApplicationClass方法推断出来的类赋值给了this.mainApplicationClass。
通过idea的Find Usages查看属性的使用情况,发现只是在banner打印或者log上有涉及,好像没发现有很大的重要性。
虽然没有发现this.mainApplicationClass比较重要的使用价值,但是推断这个类的实现方法deduceMainApplicationClass的实现原理还是挺巧妙的,值得学习一下。
4.配置和执行SpringApplication
在SpringApplication.run()方法中,配置并启动应用程序。
这段代码概述了启动过程的各个阶段:
- 初始化启动时钟:用于测量启动时间。
- 创建并配置环境:设置系统环境变量。
-
创建上下文:根据应用类型创建正确的
ApplicationContext
类型。 - 准备上下文:执行预启动操作,包括应用引导程序和启动监听器。
- 刷新上下文:启动 Spring 应用上下文,加载所有资源和 Bean。
-
调用 Runners:调用实现了
CommandLineRunner
或ApplicationRunner
接口的 Bean。 - 启动监听器:通知所有监听器,应用启动完成。
public ConfigurableApplicationContext run(String... args) {
// 为了计时用的,老版本和新版本不一样
long startTime = System.nanoTime();
// 初始化一个引导器的上下文,这是属于Spring Boot的上下文。后边还有一个Spring的上下文。apach好喜欢context这个东西,证明写框架这个context是真的好用。
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// 这是Spring的上下文,在这里定义,在下面进行的初始化
ConfigurableApplicationContext context = null;
// 配置一个系统属性
configureHeadlessProperty();
// 获取配置文件的监听器 重点 也是扩展点,凡是读取配置文件的地方都是扩展点,因为配置在配置文件中的initializer、listener都会在某个阶段被调用
SpringApplicationRunListeners listeners = getRunListeners(args);
// 调用监听器发送启动事件,这里可以通过自定义监听器消费这个事件,处理自己的逻辑
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 解析命令行参数 将其封装成一个ApplicationArguments,这个类的变量name被设置成commandLineArgs字符串,变量source是解析args封装的CommandLineArgs对象。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 环境预处理
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 配置忽略beanInfo
configureIgnoreBeanInfo(environment);
// 打印banner信息
Banner printedBanner = printBanner(environment);
// 创建ApplicationContext容器
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
创建引导上下文createBootstrapContext:createBootstrapContext()方法
SpringApplication会调用createBootstrapContext方法创建引导上下文。
这里就可以看到,在SpringApplication构造方法中的BootstrapRegistryInitializers就会应用到DefaultBootstrapContext中。这也是SpringBoot提供的扩展点之一。
当前扩展点图谱
创建SpringApplicationRunListeners
进入getRunListeners()方法中,
进入其中的SpringApplicationRunListeners类看看,
可以看到,SpringApplicationRunListeners是一个封装类,其中封装了一个SpringApplicationRunListener的列表,当触发某个事件时,就挨个调用其中的SpringApplicationRunListener的对应方法。
其中,SpringApplicationRunListener负责监听run方法的各个阶段,在不同的阶段监听不同的事件。
SpringBoot默认使用EventPublishingRunListener作为run方法的监听者,我们来看看其源代码。
从代码中可以看到,EventPublishingRunListener会从SpringApplication中获取其Listener,即前面我们在构造方法中看到的ApplicationListener实例。在触发事件时,就是利用Spring的事件机制发布事件,触发ApplicationListener进行触发。
这里需要注意的是,ApplicationListener的来源是spring.factories,而不是我们平时使用的@EventListener。也就是说,如果不写入到spring.factories,那么ApplicationListener就不会出现在这里的EventPublishingRunListener中。
当前扩展点图谱
准备好prepareEnvironment:prepareEnvironment方法
可以看到,把启动参数 args 包装成了 ApplicationArguments 类型的对象,接下来把运行时监听器、启动参数,送到了 prepareEnvironment 方法中创建环境对象。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 1. 创建一个Environment实例
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 2. 对Environment进行配置
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 对environment增加configurationProperty的属性源
ConfigurationPropertySources.attach(environment);
// 3. 触发监听SpringApplicationRunListener的environmentPrepared事件
listeners.environmentPrepared(bootstrapContext, environment);
// 将名为defaultProperties的属性源移动到最后
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
// 将environment的属性设置到SpringApplication中
bindToSpringApplication(environment);
// 根据情况对Environment进行一次转换
if (!this.isCustomEnvironment) {
EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
创建Environment实例,getOrCreateEnvironment() 方法
在SpringApplication.prepareEnvironment方法中,首先会调用getOrCreateEnvironment() 方法创建ConfigurableEnvironment对象。
进入getOrCreateEnvironment() 方法内,可以看到,
创建出来的ConfigurableEnvironment的实际类型会根据SpringApplication初始化时推断出来的WEB应用程序类型而定,如果WEB应用程序类型为SERVLET,则创建出来的ConfigurableEnvironment实际类型为StandardServletEnvironment,并且在初始化StandardServletEnvironment时还会一并将JAVA系统属性和操作系统环境变量这两个外部化配置加载到StandardServletEnvironment中。
printBanner()方法
从 Spring Boot 的 run 方法可以看出,printBanner 方法在 prepareEnvironment 之后,这是因为 application.properties 中有一些关于 Banner 的配置项。需要先解析 application.properties 的值,并将其绑定到对应的 bean 之后,才好进行后续的操作。
具体的流程如下:
- 判断 bannerMode,如果是OFF则表示不打印,如果是LOG则表示打印到文件,否则打印到控制台。
- 依据指定位置是是否存在文件,判断 Banner 类型是文本还是图片,文本类型则使用 ResourceBanner,图片类型则使用 ImageBanner, 如果都不是则使用 Spring Boot 默认的 SpringBootBanner。
- 调用 Banner 对象的的 printBanner()方法。不同类型的 Banner 的 printBanner()方法不同。
如下是获取 Banner 对象方法,
可以看出,Spring Boot 首先获得 ImageBanner,然后是 ResourceBanner。需要注意的是,这两者可以同时存在则会一次性打印两种 Banner。如果都不满足,还会去获得 fallbackBanner,这个是用户自己设定的兜底 Banner,但是我们很少使用,因为 Spring Boot 内置了兜底方案,也就是 SpringBootBanner。
创建应用程序上下文createApplicationContext:createApplicationContext()方法
此方法主要是用于创建一个应用程序上下文(ApplicationContext),它是一个用于管理Bean的容器。通过该容器,可以实现Bean的实例化、初始化、依赖注入以及销毁等操作。
在Spring框架中,ApplicationContext是核心接口之一,它提供了许多有用的功能,例如:
-
管理Bean的生命周期:自动实例化、初始化、销毁Bean;
-
解析Bean之间的依赖关系:自动装配Bean的依赖;
-
提供国际化支持:根据不同的语言环境加载相应的资源文件;
-
支持AOP:可以实现面向切面编程;
-
支持事件传播:可以实现事件的发布和订阅等。
因此,该函数在Spring框架中具有重要的作用,往往是在应用程序的初始化阶段被调用,用于创建和管理Bean的容器。
本次只看它的初始化过程,不对它的功能做过多的分析。
这里的代码体现出了工厂设计模式。 通过SpringApplication类本身实例化的时候,初始化的一个ApplicationContextFactory对象创建了ApplicationContext对象实例。
所以这里的applicationContextFactory.create()方法就是DefaultApplicationContextFactory的create()方法。这里create方法传入的参数当前应用的类型,根据前面分析,当前应用的类型是SERVLET。所以这里的this.webApplicationType的值就是SERVLET。
setApplicationStartup()方法
ApplicationStartup是一个应用启动对象。该函数的作用是将应用启动对象与上下文对象关联起来,以便在上下文对象中可以访问应用启动对象的相关属性和方法。
准备应用程序上下文preparedContext:prepareContext()方法
传入的参数绑定到特定的上下文对象中,为应用程序的启动和运行做好准备。
这个方法主要做了以下操作:
-
将bootstrapContext绑定到context中,以便在应用程序中访问引导上下文信息。
-
将environment绑定到context中,以便在应用程序中访问环境变量和属性。
-
将listeners绑定到context中,以便在应用程序启动和停止时触发相应的监听器事件。
-
将applicationArguments绑定到context中,以便在应用程序中访问命令行参数。
-
将printedBanner绑定到context中,以便在应用程序启动时控制是否打印欢迎信息。
通过执行这些操作,prepareContext函数为应用程序的上下文环境准备了必要的信息和配置,使得应用程序能够正常启动和运行。
我们进入到方法看一下。
刷新应用程序上下文refreshContext:refreshContext(context)方法
refreshContext(context)方法用于刷新应用程序的上下文。它会从配置文件中加载所有的Bean并实例化它们,然后将它们注册到应用程序的上下文中。这个方法通常在应用程序启动时调用,以确保所有的Bean都已经被实例化并准备就绪。
下面我们来看一下源码,进入SpringApplication.run方法的refreshContext方法,看到refreshContext的方法内容,继续点击refresh方法,
Refresh the underlying {@link ApplicationContext}也就是刷新底层的ApplicationContext,继续跟进去,这里要选择AbstractApplicationContext,
refresh方法主要是刷新应用程序上下文,这里主要涉及到准备刷新上下文,调用上下文注册为bean的工厂处理器,初始化上下文的消息源,初始化特定上下文子类中的其他特殊bean,检查监听器bean并注册,最后发布相应的事件并销毁已经创建的单例及重置active标志。
afterRefresh()方法
接下来调用的是afterRefresh这个方法,
目前,这个方法是个空方法。
listeners.started()方法
调用SpringApplicationRunListeners的started方法,实际上就是调用了所有监听器的started方法,广播SpringBoot已经完成了ApplicationContext初始化的全部过程。
调用callRunners:callRunners()方法
最后,run方法会通过执行callRunners方法来调用ApplicationRunner和CommandLineRunner,目的通过它们来实现在容器启动时执行一些操作。
callRunners()方法大致流程是通过context拿到ApplicationRunner和CommandLineRunner的bean,放入集合然后排序,然后遍历集合将ApplicationArguments 参数传入执行callRunner方法。
我们具体分析一下,
从代码中,我们可以看到,如果有bean的类型是ApplicationRunner或者CommandLineRunner,那么就会执行该bean下的run方法。
以下为ApplicationRunner和CommandLineRunner的源码,我们可以看到两个接口,都只有一个run方法而已。
这两个接口的区别只是run方法的参数不一样,ApplicationRunner 的run方法参数为ApplicationArguments 对象,而CommandLineRunner 的run方法参数为字符串。
- CommandLineRunner接口,用于指示当一个bean被包含在一个SpringApplication中时应该运行它。可以在同一个应用程序上下文中定义多个CommandLineRunner bean,并且可以使用ordered接口或@Order注释进行排序。
- ApplicationRunner接口,用于指示当一个bean被包含在一个SpringApplication中时应该运行它。可以在同一个应用程序上下文中定义多个ApplicationRunner bean,并且可以使用ordered接口或@Order注释进行排序。
所以,我们如果想在上下文启动成功之后,做一些操作的话,只需要自定义类实现CommandLineRunner或ApplicationRunner接口就可以了,在实现的run()方法里执行想要自动执行的代码。
另外,当有多个类实现了CommandLineRunner和ApplicationRunner接口时,可以通过在类上添加@Order注解来设定运行顺序。
总结一下就是,该过程可以理解为是SpringBoot完成ApplicationContext初始化前的最后一步工作,我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。
当前扩展点图谱
至此run方法就执行完了,程序启动完成。
总结
Spring Boot 的启动流程通过高度自动化的步骤简化了配置和初始化工作。
大致步骤包括:
- 启动入口:
main
方法及SpringApplication.run
。 - 初始化
SpringApplication
实例。 - 配置和执行启动
- 环境准备
- 创建和刷新应用上下文
- 加载和实例化 Bean
- 调用
Runner
接口 - 通知启动监听器
稍微细致一点的Springboot 启动过程如下,
- 新建module,在主程序类加入断点,启动springboot;
- 首先进入SpringAplication类run方法;
- run方法新建SpringApplication对象;
- SpringApplication对象的run方法,首先创建并启动计时监控类;
- 接着通过configureHeadlessProperty设置java.awt.headless的值;
- 接着调用getRunListeners创建所有spring监听器;
- 接着DefaultApplicationArguments初始化应用应用参数;
- 接着prepareEnvironment根据运行监听器和参数准备spring环境;
- 接着调用createApplicationContext方法创建应用上下文;
- 通过prepareContext准备应用上下文;
- refreshContext方法刷新上下文;
- 调用stop方法停止计时监控器类;
- 调用started发布应用上下文启动完成事件;
- callRunners方法执行所有runner运行器;
- 调用running发布应用上下文就绪事件;
- 最后返回应用上下文。
下面的图片对启动流程做了一个总结:
每个步骤都执行特定的初始化任务,从而实现了 Spring Boot 应用的快速启动和运行。
SpringBoot的启动是一个非常复杂的流程,本文仅仅对SpringBoot的启动做了一些简要的梳理,同时总结了一些比较常见的SpringBoot的扩展点