优质博文:IT-BLOG-CN
一、项目背景
截止2023.05.18,springboot发布了最新版本3.1.0。而在我们开发项目中,springboot一直使用的是1.5.8版本(相差6年的维护更新)。版本差距较大,很多新功能未能得到使用。例如近几年Loom的兴起,springboot也在3.0开始对虚拟线程进行了支持。
所以项目组开始计划对项目中的springboot版本进行升级改造。翻阅了springboot官方升级指引,不建议直接对springboot版本进行较大跨度的升级。小组制定了从1.5.8->2.1.0,2.1.0->2.7.9,2.7.9->3.0.5的升级改造计划。
二、Springboot简介
Springboot是Spring家族中的一个框架,它是用来简单应用程序的创建和开发过程,化繁为简,简化SSM(SpringMVC + Spring + MyBatis)框架的配置。比如说在使用SSM框架开发的时候,我们需要配置web.xml,配置spring,配置mybatis,并将它们整合到一起,而是用Springboot就不同了,它采用了大量的默认配置来简化这些文件的配置过程。
三、Springboot特性
【1】能够快速创建基于Spring的应程序。
【2】能够直接使用Java main方法启动内嵌的Tomcat服务器运行Spring Boot程序,不需要部署war包文件。
【3】提供约定的starter POM来简化Maven配置, 让Maven的配置变得简单。
【4】自动化配置,根据项目的Maven依赖配置,SpringBoot 自动配置Spring、SpringMVC等。
【5】提供了程序的健康检查等功能基本可以不使用XML配置文件,采用注解配置。
四、springboot模块介绍
我们先来对SpringBoot的源码模块来一个大致的了解,如下图:
1.5.x
2.1.x
2.7.x
3.0.x
从上图可以看到,主要有以下四个模块:
【1】spring-boot-project:整个SpringBoot框架全部功能在这个模块实现,SpringBoot项目95%的代码都在这里实现,源码总共有25万行左右。
【2】Spring-boot-samples:这个是SpringBoot给小伙伴们赠送的福利,里面包含了各种各样使用SpringBoot的简单demo,我们调试阅读源码的时候可以充分利用该模块。
【3】Spring-boot-sample-invoker:这个模块应该是跟sample模块有关,注意根pom.xml中有这么一句话:Samples are built via the invoker plugin(Samples模块通过invoker插件生成),该模块无代码。
【4】Spring-boot-tests:这个模块SpringBoot的测试模块,跟部署测试和集成测试有关。
因为SpringBoot的全部功能在spring-boot-project模块实现,因此下面重点来介绍下 spring-boot-project 模块。
五、spring-boot-project源码模块详解
先来看下spring-boot-project整体模块结构
1.5.x
2.1.x
2.7.x
3.0.x
模块众多我们真正要看的模块有spring-boot,spring-boot-autoconfigure,spring-boot-starters和spring-boot-actuator模块
【1】spring-boot-parent: 这个模块没有代码,是spring-boot模块的父项目,被其他子模块继承。
【2】spring-boot: 这个模块是SpringBoot项目的核心,可以说一些基础核心的功能都在这里实现,为SpringBoot的其他模块组件功能提供了支持,主要包括以下核心功能:
1、SpringApplication类,这个是SpringBoot的启动类,提供了一个静态的run方法来启动程序,该类主要用来创建并且刷新Spring容器 ( https://cloud.tencent.com/product/tke?from=20065&from_column=20065 )ApplicationContext.
2、支持选择不同的容器比如Tomcat,Jetty、等来作为应用的嵌入容器。
3、外部配置支持,这个指的是我们执行java -jar xxx.jar命令时可以带一些参数,比如执行java -jar demo.jar --server.port=8888来将应用端口修改为8888.
4、该模块内置了一些SpringBoot启动时的生命周期事件和一些容器初始化器(ApplicationContext initializers),来执行一些SpringBoot启动时的初始化逻辑。
【3】spring-boot-autoconfigure: 这个模块跟SpringBoot的自动配置有关。比如SpringBoot能基于类路径来自动配置某个项目模块,自动配置最为关键的注解是@EnableAutoConfiguration,这个注解能触发Spring上下文的自动配置。
除了根据类路径来进行自动配置外,还有根据容器中是否存在某个bean等方式来进行自动配置。
【4】spring-boot-starters: 这个模块是跟SpringBoot的启动依赖有关。SpringBoot通过提供众多起步依赖降低项目依赖的复杂度。起步依赖其实就是利用maven项目模型将其他相关的依赖给聚合起来,里面各种依赖的版本号都给定义好,避免用户在引入依赖时出现各种版本冲突,方便了我们的使用。
注意,该模块没有代码,主要是通过maven的pom.xml来组织各种依赖。
【5】 spring-boot-actuator: 这个跟SpringBoot的监控有关。可以通过HTTP端点或JMX等来管理和监控应用。审计、运行状况和度量收集可以自动应用到应用程序。这个监控模块是开箱即用的,提供了一系列端点包括HealthEndpoint, EnvironmentEndpoint和BeansEndpoint等端点。
用一张思维导图解释模块关系
六、springboot启动原理介绍
在springboot升级时,遇到问题,需要debug Springboot启动过程原码。不熟悉springboot启动过程,会增大问题排查难度。
内置tomcat
我们开发任何一个Spring Boot项目,都会用到如下的启动类@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication背后的秘密
@SpringBootConfiguration 相当于@Configuration,意义在于能让@SpringBootApplication的配置类能被@ComponentScan扫描。
@Configuration的注解类标识这个类可以使用Spring IoC容器作为bean定义的来源。
提到@Configuration就要提到他的搭档@Bean。使用这两个注解就可以创建一个简单的spring配置类,可以用来替代相应的xml配置文件。
@Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册为在Spring应用程序上下文中的bean。
@ComponentScan功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。
我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
注:所以SpringBoot的启动类最好是放在root package下,因此默认不指定basePackages。
@EnableAutoConfiguration 用于启用自动装配,路径扫描、组件装配等通过此注解实现。
@EnableAutoConfiguration的理念和做事方式其实一脉相承,简单概括一下就是,借助@Import的支持,收集和注册特定场景相关的bean定义。
@EnableAutoConfiguration会根据类路径中的jar依赖为项目进行自动配置,如:添加了spring-boot-starter-web依赖,会自动添加Tomcat和Spring MVC的依赖,Spring Boot会对Tomcat和Spring MVC进行自动配置。
@EnableAutoConfiguration作为一个复合Annotation,其自身定义关键信息如下:
1.5.x
2.7.x
3.0.x
其中,最关键的要属@Import(AutoConfigurationImportSelector.class),借助AutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。就像一只“八爪鱼”一样,借助于Spring框架原有的一个工具类:SpringFactoriesLoader的支持,@EnableAutoConfiguration可以智能的自动配置功效才得以大功告成!
SpringFactoriesLoader属于Spring框架私有的一种扩展方案,其主要功能就是从指定的配置文件META-INF/spring.factories加载配置,并实例化,加入IOC容器中。
深入探索SpringApplication执行流程
main方法作为入口,SpringApplicationRunListener是一个更底层的接口,它可以监听Spring应用程序中的更多事件,包括应用程序的启动和关闭、上下文创建和刷新、以及各种错误和异常。SpringApplicationRunListener的实现可以在应用程序的生命周期的任何时候被调用。
ApplicationListener是一个更高层次的接口,它只能监听Spring应用程序中的特定事件,如上下文刷新事件、上下文关闭事件等。ApplicationListener的实现只能在应用程序上下文创建之后才能被调用。
外置tomcat: spring boot war启动是利用Servlet 3.0新增的ServletContainerInitializer接口结合SPI(Service Provider Interface)机制实现的。
1.设置当前项目的打包方式,启动类所在目录的pom文件
<packaging>war</packaging>
2.让tomcat相关的依赖不参与打包部署 ,因为外置tomcat服务器已经有这些jar包
<!--让它不参与打包部署,或者排除内嵌tomcat-->
<dependency>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
<scope>provided</scope>
</dependency>
3.支持启动springboot应用,需要在启动类
public class TomcatStartSpringBoot extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Application.class);
}
}
4.配置并启动tomcat
5.根据Servlet3.0规范,找到ServletContainerInitializer ,进行实例化。包路径:org\springframework\spring-web\4.3.14.RELEASE\spring-web-4.3.14.RELEASE.jar!\METAINF\services\javax.servlet.ServletContainerInitializer。
6.创建实例,SpringServletContainerInitializer将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都传入到onStartup方法的Set集合,为这些WebApplicationInitializer类型的类创建实例并遍历调用其onStartup方法。
7.SpringServletContainerInitializer方法中又调用每一个initializer的onStartup方法。即先调用SpringServletContainerInitializer实例的onStartup方法,在onStartup()方法内部又遍历每一个WebApplicationInitializer类型的实例,调用其onStartup()方法。
8.我们的SpringBootServletInitializer的实例(com.web.application)会被创建对象,并执行onStartup方法。onStartup主要是createRootApplicationContext和addListener。
9.createRootApplicationContext主要干了
- createSpringApplicationBuilder(),类似于内置tomcat初始化过程
- 注册一个新的ParentContextApplicationContextInitializer、ServletContextApplicationContextInitializer
- 添加listeners
- builder.build(),返回
- run(application),启动应用