SpringBoot3 基础教程之九核心原理
大纲
- SpringBoot3 基础教程之一快速入门
- SpringBoot3 基础教程之二常规配置
- SpringBoot3 基础教程之三 Web 开发
- SpringBoot3 基础教程之四 Web 开发
- SpringBoot3 基础教程之五基础特性
- SpringBoot3 基础教程之六场景整合
- SpringBoot3 基础教程之七场景整合
- SpringBoot3 基础教程之九核心原理
依赖管理机制

为什么导入 spring-boot-starter-web 后,所有相关的依赖都导入进来了?
- 开发什么场景,导入什么场景启动器
- Maven 依赖的传递原则。A -> B -> C,那么 A 就拥有 B 和 C
- 导入场景启动器后,会自动把这个场景的所有核心依赖全部导入进来
为什么版本号都不用写?
- 每个 SpringBoot 项目都有一个父项目
spring-boot-starter-parent parent的父项目是spring-boot-dependencies- 父项目是版本仲裁中心,会将所有常见 Jar 包的依赖版本都声明好了,比如
mysql-connector-j
如何自定义依赖的版本号?
- 第一种方式:直接在当前 Maven 配置文件的
<properties></properties>标签中声明父项目用的版本属性的 Key - 第二种方式:直接在导入依赖的时候声明版本
- 上述两种方式都是利用 Maven 的就近原则特性
如何导入第三方的 Jar 包
- 对于
spring-boot-starter-parent没有管理的 Jar 包依赖,直接在 Maven 的配置文件中自行声明就可以
自动配置原理
SpringBoot 应用关注的三大核心:场景、配置、组件。
自动配置流程
初步理解
- 自动配置 SpringMVC、Tomcat 等
- 导入场景,容器中就会自动配置好这个场景的核心组件
- 以前:DispatcherServlet、ViewResolver、CharacterEncodingFilter ….
- 现在:自动配置好的这些组件
- 验证:容器中有了什么组件,就具有什么功能
1 |
|
默认的包扫描规则
@SpringBootApplication注解标注的类就是主程序类- SpringBoot 只会扫描主程序类所在的包及其下面的子包,即自动的
component-scan功能 - 自定义包的扫描路径
- 第一种方式:
@SpringBootApplication(scanBasePackages = "com.clay") - 第二种方式:
@ComponentScan("com.clay")直接指定扫描的包路径
- 第一种方式:
配置默认值
- 配置文件的所有配置项是和某个类的对象值进行一一绑定的
- 绑定了配置文件中每一项值的类,称为配置属性类
- 比如:
ServerProperties绑定了所有 Tomcat 服务器有关的配置MultipartProperties绑定了所有文件上传相关的配置- ….. 参照 官方文档 或者参照绑定的配置属性类
按需加载自动配置
- 导入场景启动器
spring-boot-starter-web - 场景启动器除了会导入相关功能的依赖,还会导入
spring-boot-starter,它是所有starter的starter,是基础核心starter spring-boot-starter导入了一个包spring-boot-autoconfigure,里面都是各种场景的AutoConfiguration自动配置类- 虽然全场景的自动配置都在
spring-boot-autoconfigure这个包中,但并不是全部都默认开启的,导入哪个场景启动器才会开启哪个场景的自动配置
- 导入场景启动器
总结
导入场景启动器会触发 spring-boot-autoconfigure 这个包的自动配置生效,Spring 的 IOC 容器中就会具有相关场景的功能。
进阶理解
思考以下问题
- 1、SpringBoot 是怎么实现导一个
starter,写一些简单配置,开发者无需关心整合,应用就能跑起来的? - 2、为什么 Tomcat 的端口号可以配置在
application.properties中,并且 Tomcat 能启动成功? - 3、导入场景启动器后,哪些自动配置能生效?

导入 Web 开发场景
spring-boot-starter-web- 1、场景启动器导入了相关场景的所有依赖,如
spring-boot-starter-json、spring-boot-starter-tomcat、spring-webmvc。 - 2、每个场景启动器都引入了一个
spring-boot-starter,即核心场景启动器。 - 3、核心场景启动器引入了
spring-boot-autoconfigure包。 - 4、
spring-boot-autoconfigure里面囊括了所有场景的自动配置。 - 5、只要
spring-boot-autoconfigure这个包下的所有类都能生效,那么相当于 SpringBoot 官方写好的整合功能就生效了。 - 6、SpringBoot 默认是扫描不到
spring-boot-autoconfigure下写好的所有配置类(这些配置类给我们做了整合操作),默认只扫描主程序类所在的包及其下面的子包。
- 1、场景启动器导入了相关场景的所有依赖,如
主程序注解
@SpringBootApplication- 1、
@SpringBootApplication由三个注解组成,分别是@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan。 - 2、SpringBoot 默认只能扫描自己主程序所在的包及其下面的子包,扫描不到
spring-boot-autoconfigure包中官方写好的配置类。 - 3、
@EnableAutoConfiguration是 SpringBoot 开启自动配置的核心。- 1、是由
@Import(AutoConfigurationImportSelector.class)提供核心功能,批量往容器中导入组件。 - 2、SpringBoot 启动时会默认加载 142 个配置类,这 142 个配置类是由
spring-boot-autoconfigure包下的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件指定。 - 3、项目启动的时候利用
@Import批量导入组件的机制,将spring-boot-autoconfigure包下的 142 个xxxxAutoConfiguration类导入进来(自动配置类)。 - 4、虽然导入了 142 个自动配置类,但并不是这 142 个自动配置类都能生效;每一个自动配置类,都有条件注解
@ConditionalOnxxx,只有条件成立才能生效。
- 1、是由
- 1、
xxxxAutoConfiguration自动配置类- 1、往容器中使用
@Bean注册一堆组件 - 2、每个自动配置类都可能有这个注解
@EnableConfigurationProperties(ServerProperties.class),用来把配置文件中配的指定前缀的属性值封装到xxxProperties属性类中。 - 3、以 Tomcat 为例,把服务器的所有配置都是以
server开头的配置都封装到了属性类中。 - 4、往容器中放的所有组件的一些核心参数,都来自于
xxxProperties属性类,它都是和配置文件绑定的。 - 5、只需要更改配置文件的值,核心组件的底层参数就能修改。
- 6、将精力都用于写业务,全程无需关心各种框架的整合(底层这些整合都写好了,而且也按需生效了)
- 1、往容器中使用
自动配置流程的总结
- 1、导入
spring-boot-starter-xxxx,会导入spring-boot-starter,也就会导入spring-boot-autoconfigure包。 - 2、
spring-boot-autoconfigure包里面有一个文件META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,里面指定了应用启动时所有要加载的自动配置类。 - 3、
@EnableAutoConfiguration会自动地将上面文件里写的所有自动配置类都导入进来,同时xxxAutoConfiguration是有声明条件注解的,目的是按需加载。 - 4、
xxxAutoConfiguration往容器中导入一堆组件,这些组件都是从xxxProperties中获取属性值。 - 5、
xxxProperties又是和配置文件进行了绑定。
最终效果:导入
starter,修改配置文件,就能修改框架的底层行为。
深入理解
@SpringBootApplication 注解包含了三个核心注解,分别是 @ComponentScan、@EnableAutoConfiguration、@SpringBootConfiguration,它们的作用如下:
@ComponentScan- 组件扫描,排除一些组件(哪些不需要的)
- 排除前面已经扫描进来的配置类和自动配置类
@EnableAutoConfiguration:开启自动配置功能@AutoConfigurationPackage:扫描主程序所在的包以及其子包,加载自己的组件- 利用
@Import(AutoConfigurationPackages.Registrar.class),将主程序所在的包以及其子包的所有组件导入进来
- 利用
@Import(AutoConfigurationImportSelector.class)加载所有自动配置类,会加载starter导入的组件- 扫描 SPI 文件
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中的所有自动配置类
- 扫描 SPI 文件
@SpringBootConfiguration:本质就是@Configuration注解,标注在组件类、配置类上面,Spring IOC 容器启动时就会创建加载这些类对象
SpringBoot 完整的生命周期启动加载流程如下:

SPI 机制介绍
- Java 中的 SPI(Service Provider Interface)是一种软件设计模式,用于在应用程序中动态地发现和加载组件。SPI 的思想是,定义一个接口或抽象类,然后通过在
classpath中定义实现该接口的类来实现对组件的动态发现和加载。 - SPI 的主要目的是解决在应用程序中使用可插拔组件的问题。例如,一个应用程序可能需要使用不同的日志框架或数据库连接池,但是这些组件的选择可能取决于运行时的条件。通过使用 SPI,应用程序可以在运行时发现并加载适当的组件,而无需在代码中硬编码这些组件的实现类。
- 在 Java 中,SPI 的实现方式是通过在
META-INF/services目录下创建一个以服务接口全限定名为名字的文件,文件中包含实现该服务接口的类的全限定名。当应用程序启动时,Java 的 SPI 机制会自动扫描classpath中的这些文件,并根据文件中指定的类名来加载实现类。 - 通过使用 SPI,应用程序可以实现更灵活、可扩展的架构,同时也可以避免硬编码依赖关系和增加代码的可维护性。
SpringBoot 的自动配置使用了 SPI 机制,但实现方式不是在
META-INF/services目录下创建一个以服务接口全限定名为名字的文件,而是使用META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件进行替代。
功能开关介绍
自动配置:自动批量导入,全部都配置好,什么都不用管- 项目一启动,SPI 文件中指定的所有组件都会被加载
@EnableXxxx:手动导入,手动控制哪些功能的开启- 开启
xxx功能 - 都是利用
@Import注解把此功能要用的组件导入进去
- 开启
嵌入式容器
Servlet 容器指的是管理、运行 Servlet 组件(Servlet、Filter、Listener)的环境,一般指 Web 服务器。
自动配置原理
- SpringBoot 默认使用嵌入的 Tomcat 作为 Servlet 容器
- 嵌入式容器的自动配置类是
ServletWebServerFactoryAutoConfiguration,EmbeddedWebServerFactoryCustomizerAutoConfiguration ServletWebServerFactoryAutoConfiguration自动配置了嵌入式容器场景- 绑定了
ServerProperties配置类,所有和服务器相关的配置都使用server作为开始前缀 ServletWebServerFactoryAutoConfiguration默认导入了嵌入式的三大服务器,包括Tomcat、Jetty、Undertow- 导入
Tomcat、Jetty、Undertow时都有条件注解,系统中有对应的类才会生效(也就是导了包) - 在默认情况下,Tomcat 的配置会生效,SpringBoot 往容器中放了
TomcatServletWebServerFactory组件 - 往容器中放一个 Web 服务器工厂
ServletWebServerFactory后,可以创建 Web 服务器 - Web 服务器工厂都有一个功能,可以调用
getWebServer()获取 Web 服务器 TomcatServletWebServerFactory创建了 Tomcat Web 服务器
- 导入
ServletWebServerApplicationContextIOC 容器在启动的时候,会调用onRefresh()刷新子容器- 在调用了
onRefresh()之后,会调用ServletWebServerFactory创建 Web 服务器
1 |
|
1 |
|
总结
- Web 场景的 Spring 容器启动,在调用
onRefresh()的时候,会调用创建 Web 服务器的方法。 - Web 服务器的创建是通过
WebServerFactory实现的,容器中又会根据条件注解,启动相关的服务器配置,默认EmbeddedTomcat会往容器中放一个TomcatServletWebServerFactory组件,导致项目启动后,自动创建出 Tomcat 服务器。
内容协商
底层原理分析
1、
@ResponseBody的底层由HttpMessageConverter处理数据,即标注了@ResponseBody的返回值,将会由支持它的HttpMessageConverter将数据返回给浏览器。
如果
Controller方法的返回值标注了@ResponseBody注解- 请求进来先来到
DispatcherServlet的doDispatch()进行处理 - 找到一个
HandlerAdapter适配器,利用适配器执行目标方法 RequestMappingHandlerAdapter会执行,调用invokeHandlerMethod()来执行目标方法- 在目标方法执行之前,需要准备好两样东西
HandlerMethodArgumentResolver:参数解析器,确定目标方法的每个参数值HandlerMethodReturnValueHandler:返回值处理器,确定目标方法的返回值该怎么处理
RequestMappingHandlerAdapter里面的invokeAndHandle()真正执行目标方法- 目标方法执行完成,会返回返回值的对象
- 去找一个合适的返回值处理器
HandlerMethodReturnValueHandler - 最终找到
RequestResponseBodyMethodProcessor,它能处理标注了@ResponseBody注解的方法 RequestResponseBodyMethodProcessor调用writeWithMessageConverters(),利用MessageConverter把返回值输出给浏览器
- 请求进来先来到
HttpMessageConverter会先进行内容协商- 遍历所有的
MessageConverter,看哪个支持这种内容类型的数据 - 默认的
MessageConverter有这些 - 最终因为需要返回 JSON 数据,所以通过
MappingJackson2HttpMessageConverter输出 JSON 数据 - Jackson 利用
ObjectMapper把返回值对象写出去
- 遍历所有的
2、
WebMvcAutoConfiguration提供了 6 种 默认的HttpMessageConverters
EnableWebMvcConfiguration通过addDefaultHttpMessageConverters添加了默认的MessageConverter,如下:ByteArrayHttpMessageConverter:支持字节数据读写StringHttpMessageConverter:支持字符串读写ResourceHttpMessageConverter:支持资源读写ResourceRegionHttpMessageConverter:支持分区资源写出AllEncompassingFormHttpMessageConverter:支持表单 XML/JSON 读写MappingJackson2HttpMessageConverter:支持请求响应体 JSON 读写
提示
SpringBoot 提供默认的 MessageConverter 功能有限,仅用于 JSON 或者普通的返回数据。如果需要增加新的内容协商功能,必须添加新的 HttpMessageConverter。
事件和监听器
生命周期监听
自定义监听器
SpringApplicationRunListener 负责监听应用的生命周期。
- 自定义
SpringApplicationRunListener来监听应用生命周期的步骤- 1、编写
SpringApplicationRunListener接口的实现类 - 2、在项目的
/META-INF/spring.factories中配置org.springframework.boot.SpringApplicationRunListener=自定义的Listener,还可以指定一个有参构造方法,接受两个参数(SpringApplication application,String[] args) - 3、值得一提的是,SpringBoot 在
spring-boot.jar中配置了默认的 Listener,即org.springframework.boot.SpringApplicationRunListener=org.springframework.boot.context.event.EventPublishingRunListener
- 1、编写
生命周期流程

- SpringBoot 应用的生命周期流程
- 1、首先从
/META-INF/spring.factories读取到Listener - 2、引导:利用
BootstrapContext引导整个项目启动starting:应用开始,SpringApplication 的run()方法一调用,只要有了BootstrapContext就执行environmentPrepared:环境准备好(把启动参数等绑定到环境变量中),但是 IOC 容器还没有创建,此步骤只会执行一次
- 3、启动
contextPrepared:IOC 容器创建并准备好,但是 Sources(主配置类)还没加载,并关闭引导上下文,组件都没创建,此步骤只会执行一次contextLoaded:IOC 容器加载。主配置类加载进去了,但是 IOC 容器还没刷新(所有 Bean 还没创建),此步骤只会执行一次started:IOC 容器刷新了(所有 Bean 创建好了),但是runner没调用ready:IOC 容器刷新了(所有 Bean 创建好了),所有runner调用完了
- 4、运行
- 以上步骤都正确执行,代表容器成功运行
- 1、首先从
提示
关于 SpringBoot 应用的生命周期流程,更详细的说明请阅读 SpringApplication 和 SpringApplicationRunListener 的底层源码。
事件触发时机
各种事件监听器
BootstrapRegistryInitializer:感知特定阶段(感知引导初始化)META-INF/spring.factories- 创建引导上下文
BootstrapContext的时候触发 application.addBootstrapRegistryInitializer()- 使用场景:进行密钥校对授权
ApplicationContextInitializer:感知特定阶段(感知 IOC 容器初始化)META-INF/spring.factoriesapplication.addInitializers()
ApplicationListener:感知全阶段,基于事件机制感知事件,一旦到了哪个阶段可以做别的事@Bean或@EventListener:事件驱动SpringApplication.addListeners(…)或SpringApplicationBuilder.listeners(…)META-INF/spring.factories
SpringApplicationRunListener:感知全阶段生命周期,各种阶段都能自定义操作,功能更完善META-INF/spring.factories
ApplicationRunner: 感知特定阶段(感知应用就绪 - Ready)。如果应用卡死,就不会就绪@Bean
CommandLineRunner:感知特定阶段(感知应用就绪 - Ready)。如果应用卡死,就不会就绪@Bean

最佳实战
- 如果想要在项目启动前做事:
BootstrapRegistryInitializer和ApplicationContextInitializer - 如果想要在项目启动完成后做事:
ApplicationRunner和CommandLineRunner - 如果要干涉整个生命周期做事:
SpringApplicationRunListener - 如果想要利用事件机制做事:
ApplicationListener
完整事件触发流程
- SpringBoot 9 大事件触发顺序与触发时机
ApplicationStartingEvent:应用启动但未做任何事情,除了注册listeners与initializersApplicationEnvironmentPreparedEvent: Environment 准备好了,但ApplicationContext未创建ApplicationContextInitializedEvent:ApplicationContext准备好了,ApplicationContextInitializers被调用,但是任何 Bean 未加载ApplicationPreparedEvent:容器刷新之前,Bean 定义信息加载ApplicationStartedEvent:容器刷新完成,runner未被调用AvailabilityChangeEvent:LivenessState.CORRECT应用存活探针ApplicationReadyEvent: 任何runner被调用AvailabilityChangeEvent:ReadinessState.ACCEPTING_TRAFFIC应用就绪探针,可以接收请求了ApplicationFailedEvent:应用启动出错

探针使用说明
感知应用是否存活了:应用可能处于植物状态,虽然活着,但是不能处理请求。应用是否就绪了:应用可以处理外部请求,说明确实活的比较好。探针的使用场景:若应用将来部署到 Kubernetes 等平台,则会大量被使用到。
更详细的 SpringBoot 事件触发顺序,请看这里的图解。
SpringBoot 事件驱动开发
SpringBoot 事件驱动开发,依赖应用启动过程生命周期事件感知(9 大事件)、应用运行中事件感知(无数种)。
- 事件发布:实现
ApplicationEventPublisherAware接口,或者注入ApplicationEventMulticaster - 事件监听:实现
ApplicationListener接口,或者使用@EventListener注解


SpringBoot 事件驱动开发的使用案例可以阅读 这里 的教程。
Redis 整合
自动配置原理
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中导入了RedisAutoConfiguration、RedisReactiveAutoConfiguration和RedisRepositoriesAutoConfiguration,所有属性都绑定在RedisProperties中RedisReactiveAutoConfiguration适用于响应式编程,RedisRepositoriesAutoConfiguration适用于 JPA 操作RedisAutoConfiguration配置了以下组件LettuceConnectionConfiguration: 往容器中注入了连接工厂LettuceConnectionFactory和操作 Redis 的客户端DefaultClientResourcesRedisTemplate<Object, Object>: 可以往 Redis 存储任意对象,默认会使用 JDK 的序列化机制StringRedisTemplate: 可以往 Redis 存储字符串,如果需要存储对象,需要开发人员自己执行序列化操作,Key-Value 都是以字符串的形式进行操作
MyBatis 整合
自动配置原理
JDBC 场景的自动配置
- JDBC 场景的自动配置
mybatis-spring-boot-starter导入了spring-boot-starter-jdbc,JDBC 用于操作数据库的场景- JDBC 场景的几个自动配置
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration- 支持数据源的自动配置
- 所有和数据源相关的配置都绑定在
DataSourceProperties配置类 - 默认使用
HikariDataSource数据源
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration- 往容器注册
JdbcTemplate,操作数据库
- 往容器注册
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfigurationorg.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration- 基于 XA 二阶提交协议的分布式事务数据源
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration- 支持事务的自动配置
- 拥有的底层能力:数据源、JdbcTemplate、事务处理
MyBatisAutoConfiguration 分析
MyBatisAutoConfiguration配置了 MyBatis 的整合流程mybatis-spring-boot-starter导入mybatis-spring-boot-autoconfigure(MyBatis 的自动配置包)- 默认加载两个自动配置类
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfigurationorg.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration- 必须在数据源配置好之后才配置
- 往容器注册
SqlSessionFactory组件,用于创建访问数据库的一次会话 - 往容器注册
SqlSessionTemplate组件,用于操作数据库
- MyBatis 的所有配置绑定在
MybatisProperties - 每个
Mapper接口的代理对象是怎么创建并放到容器中,详见@MapperScan的底层源码- 利用
@Import(MapperScannerRegistrar.class)批量往容器中注册组件。解析指定的包路径下的每一个类,为每一个Mapper接口类创建代理对象,并注册到容器中
- 利用
如何分析哪个场景导入以后,开启了哪些自动配置类?
- 在
spring.boot.autoconfigure包里面找classpath:/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中配置的所有值,就是要开启的自动配置类;但是每个类可能有条件注解,基于条件注解判断哪个自动配置类会生效。 - 快速定位生效的自动配置,方法如下:
1 | # 是否开启调试模式,可以详细打印开启了哪些自动配置,Positive(生效的自动配置),Negative(不生效的自动配置) |
