前言
上一篇 Spring boot源码深入学习(一) | 搭建源码阅读环境
中介绍了如何搭建Spring boot源码本地阅读环境,那么现在就可以开始我们的源码阅读了。本文简单介绍springboot源码结构以及大概介绍springboot启动流程的各个步骤,先有一个宏观的逻辑概念,学习springboot启动的各个流程,之后再详细各个步骤分析学习。
源码结构
- spring-boot-project:SpringBoot框架的核心,功能都在这里实现
- Spring-boot-samples:这是spring boot提供测试的大量demo,调试阅读源码的时候可以充分利用
- Spring-boot-sample-invoker:这个模块暂时不知道啥,顾名思义应该是跟sample模块有关
- Spring-boot-tests:这个模块跟部署测试和集成测试有关。
由上可知,我们着重学习点应该在spring-boot-project模块中,里面包含了springboot的整个功能实现。下面我们看一看里面的结构:
下面是GitHub介绍翻译而来:
https://github.com/spring-projects/spring-boot/tree/v2.1.0.RELEASE
众所周知,springboot核心在于快速搭建项目,自动配置,简化开发。那么它是如何实现的呢?之后分析学习则重点关注在于spring-boot,spring-boot-autoconfigure,spring-boot-starters这3个模块
- spring-boot
这个模块是springboot中一些基础功能的实现,包括:
1.SpringApplication类,提供了方便的静态方法,使编写独立的Spring应用程序变得容易。它的唯一任务是创建和刷新一个适当的Spring 应用程序上下文
2.可选择容器(Tomcat、Jetty或Undertow)的嵌入式web应用程序
3.一流的外部化配置支持
4.方便的ApplicationContext初始值设定项,包括对合理的日志记录默认值的支持 - spring-boot-autoconfigure
这个模块是springboot自动配置的实现,通过一个@EnableAutoConfiguration注解触发Spring上下文的自动配置。 - spring-boot-starters
这个模块是springboot的启动依赖实现。若想开始使用Spring和JPA进行数据库访问,只需在项目中包含springbootstarterdatajpa依赖项,就可以开始了。 - spring-boot-cli
Spring命令行应用程序编译并运行Groovy源代码,这使得编写绝对最少的代码来运行应用程序变得非常容易。springcli还可以监视文件,在文件更改时自动重新编译和重新启动。 - spring-boot-actuator
-监控相关。执行器端点允许您监视应用程序并与之交互。弹簧引导执行器提供执行器端点所需的基础设施。它包含对执行器端点的注释支持。开箱即用,这个模块提供了许多端点,包括HealthEndpoint、EnvironmentEndpoint、BeansEndpoint等等。 - spring-boot-actuator-autoconfigure
监控的自动配置相关。这将根据类路径的内容和一组属性为执行器端点提供自动配置。例如,如果microller在类路径上,它将自动配置MetricsEndpoint。它包含通过HTTP或JMX公开端点的配置。就像springboot AutoConfigure一样,当用户开始定义自己的bean时,这一功能将退回。 - spring-boot-test
此模块包含核心项和注释,它们在测试应用程序时非常有用。 - spring-boot-test-autoconfigure
与其他springboot自动配置模块一样,springboottestautoconfigure为基于类路径的测试提供自动配置。它包含许多注释,可用于自动配置应用程序中需要测试的部分。 - spring-boot-loader
SpringBootLoader提供了一种秘密方法,允许您构建一个可以使用java-jar启动的jar文件。通常您不需要直接使用springbootloader,而是使用Gradle或Maven插件。 - spring-boot-devtools
热部署等额外开发特性。 springbootdevtools模块提供了额外的开发时特性,如自动重新启动,以获得更流畅的应用程序开发体验。运行完全打包的应用程序时,开发人员工具将自动禁用。
启动流程概括
这里用的samples里面的tomcat项目作为介绍。启动类是SampleTomcatApplication.java
启动类:
// spring boot 启动类main方法
public static void main(String[] args) {
SpringApplication.run(SampleTomcatApplication.class, args);
}
调用run 静态方法,启动spring boot项目
// run 静态方法,启动spring boot项目
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
// 实例化启动类,继续调用静态 run方法,启动spring boot
return run(new Class<?>[] { primarySource }, args);
}
继续调用静态 run方法,这里会实例化SpringApplication对象,里面操作了几步流程,之后再具体分析。之后run方法,启动spring boot。
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
// 实例化SpringApplication对象(这里操作了几步稍后分析),并调用run方法启动
return new SpringApplication(primarySources).run(args);
}
run方法进入后,就是springboot启动的核心流程了,大体步骤如下:
【1】获取监听器
【2】准备环境
【3】控制台打印Banner
【4】创建容器,根据不同类型创建不同的容器
【5】实例化异常报告期实例,用于记录启动过程中的错误。
【6】准备容器,给刚刚创建的容器做一些初始化工作
【7】刷新容器,这一步至关重要。后续再做解析
【8】刷新容器后的一些操作,这里是空方法
// spring boot项目启动的核心run方法
public ConfigurableApplicationContext run(String... args) {
// 实例化一个stopWatch,用于统计spring boot项目启动花费的时间
StopWatch stopWatch = new StopWatch();
// 开始计时
stopWatch.start();
ConfigurableApplicationContext context = null;
// exception reporter,统计异常信息
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 设置 java.awt.headless 为true
// Headless模式是系统的一种配置模式。在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式。服务器不需要外设
configureHeadlessProperty();
// 【1】获取监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 【2】准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 配置spring.beaninfo.ignore属性,默认为true
// 跳过对BeanInfo类的搜索(通常用于首先没有为应用程序中的bean定义此类的情况)
configureIgnoreBeanInfo(environment);
// 【3】控制台打印Banner
Banner printedBanner = printBanner(environment);
// 【4】创建容器,根据不同类型创建不同的容器
context = createApplicationContext();
// 【5】实例化异常报告期实例,用于记录启动过程中的错误。
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 【6】准备容器,给刚刚创建的容器做一些初始化工作
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 【7】刷新容器,这一步至关重要。后续再做解析
refreshContext(context);
// 【8】刷新容器后的一些操作,这里是空方法
afterRefresh(context, applicationArguments);
// 停止计时器
stopWatch.stop();
// 开启日志打印
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 启动容器,加载所有bean实例
listeners.started(context);
// 处理容器启动后的一些操作
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);
}
// 返回容器
return context;
}