Spring Boot 原理篇
Spring Boot 原理篇
Spring
系列框架是目前全世界最流行的java
开发框架,可以说**Spring
是现代java
开发的基石**,它划分了多个子框架,如:SpringData
,SpringBoot
,SpringCloud
,SpringAI
,SpringFramework
…其中SpringFramework
是所有模块的基础。
分析Spring Boot
原理可以使我们更好的熟练使用Spring Boot
,并汲取其优秀的设计思想。
若直接使用SpringFramework
,依赖配置将非常繁琐,每次引入一个依赖,都要找到其配套的所有依赖及其版本。基于此,Spring
官方基于SpringFramework
4.0之后版本推出一个全新的框架SpringBoot
,通过SpringBoot
来简化SpringFrmework
的开发(注意是简化,不是替代,也就是说SpringBoot
是SpringFramework
的上层框架),而SpringBoot
又是Spring
家族中最流行的框架,Spring
官方也推荐直接使用SpringBoot
构建项目。
使用SpringBoot
构建并开发项目会使项目开发更方便快捷,主要原因就是最核心的两个功能:起步依赖、自动配置。解析SpringBoot
的原理,就是来解析起步依赖
和自动配置
的原理
- 通过起步依赖,可以大大简化
pom.xml
文件中依赖的配置,解决SpringFramework
中依赖配置繁琐的问题。 - 通过自动配置,可以大大简化框架使用过程中
Bean
的声明及配置。
注:下文核心细节的知识点用图标表示重要程度
-
⭐:核心,作为使用Sping Boot的上层开发者必须要掌握的
-
🍀:了解,不需要深入学习,了解即可,学习它可更好了解Spring Boot的底层设计与实现
起步依赖
我们只需引入起步依赖,常见的基本依赖都会随之引入。
在SpringBoot
之前,光使用SpringFramework
,项目开始需要引入很多基本依赖,非常地繁琐。
使用SpringBoot
创建项目后,引入一个起步依赖(以Web
开发为例:spring-boot-start-web
)即可将许多基本依赖会随之引入:
原理也很简单,就是maven
的依赖传递。所谓依赖传递指:若A依赖B,B依赖C,引入A依赖,B、C依赖也会随之引入。
自动配置
自动配置:当spring
启动后,一些配置、bean对象(引入的依赖中存在)自动存入到IOC
容器中,不需要我们手动声明,从而简化了开发,省去了繁琐的配置操作。
研究SpringBoot
的自动配置,其实重点需要研究在Spring
启动后
SpringBoot
是如何将依赖jar
包中的Bean
对象注入到IOC
容器中的?- 它是如何识别依赖
jar
包中哪些对象是需要加载到IOC
容器的成为Bean
对象? - 加载
Bean
对象的规则是怎么样的?加载的流程又是怎么样的?
包扫描与导入
包扫描:Spring
中,通过@Commonent
注解及其衍生注解可以实现自定义Bean
对象的定义,Spring
启动时默认会扫描启动类所在包及其子包中的所有类来实现寻找bean
对象,并将其加载到IOC
容器中。也可使用@ComponentScan
添加参数实现自定义扫描范围,例如:
@ComponentScan({"com.example","com.gezishan","org.mybatis"})
@SpringBootApplication
public calss SpringbootWebApplication{}
如果将依赖jar
包中的Bean
对象加载到IOC
容器也使用这种方式,配置将非常繁琐,依赖多了,包扫描的范围也很大,但Bean
对象并没有多少,这样效率就很低。
Spring
也提供了另一种将Bean
对象加载到IOC
容器的方法,那就是导入。
导入:Spring
中,通过@Import
注解指定**Class
参数**实现导入,使用@Import
导入的类会被加载进IOC
容器,导入类的类型有:
- 导入普通类,指定的该普通类会被加载进
IOC
容器 - 导入配置类,指定的配置类所配置的所有
Bean
对象会被加载进IOC
容器 - 导入
ImportSlector
接口实现类(下文称自动配置选择器),该实现类的slectImporte()
方法返回的字符串类名数组对应的类会被加载进IOC
容器
//导入一个普通类和一个配置类以及ImportSlector接口实现类
@Import({TokenParser.class,HeaderConfig.class,MyImportSelector.class})
@SpringBootApplication
public calss SpringbootWebApplication{}
⭐ImportSlector接口
翻看源码:
该接口有一个必须实现的方法slectImporte()
,返回值是一个字符串数组,表示需要交给IOC
容器管理的类名数组,可以将Bean
对象的全类名封装成数组中返回。
配置管理:需要交出去的Bean
对象很多时,可以写一份文件列出要交出去的Bean
对象,再在slectImporte()
方法中读取文件中所有的Bean
对象全类名。这样配置管理就更加方便,代码也更清晰,不用在@Import
中罗列那么多类名。
对于第三方的jar
包中的Bean
对象,通常由第三方的开发者自己定义导入。
🍀 @EnableXxxx注解
@EnableXxxx
注解是一个泛称,它表示第三方提供的注解,通常封装了@Import
注解
启动流程
⭐@springBootApplication
@springBootApplication
注解是SpringBoot
项目的核心注解,它标识在SpringBoot
工程的启动类上,该注解由三部分组成:
@SpringBootConfiguration
:该注解与@Configuration
相同,用来声明当前类也是一个配置类。@ComponentScan
:组件扫描,默认扫描当前启动类所在包及其子包内的所有类文件,用于将自定义的Bean
对象加载到IOC
容器。@EnableAutoConfiguration
:SpringBoot
实现自动化配置的核心注解,用于将依赖jar
包中的Bean
对象加载到IOC
容器。
翻看源码:
⭐@EnableAutoConfiguration
@EnableAutoConfiguration
的作用是开启自动配置机制,是自动配置的核心,其实现依赖两个关键步骤
- @Import({AutoConfigurationImportSelector.class})
通过@import
导入AutoConfigurationImportSelector
类(自动配置选择器),它通过slectImporte()
方法返回待加载的Bean对象类名数组
- 加载自动配置类的候选名单
AutoConfigurationImportSelector
的slectImporte()
方法的内部会执行以下步骤
- 读取
spring.factories
文件:从所有类路径下的META-INF/spring.fectories
文件中加载要自动配置的类。
以MyBatisPlus的spring.factories
文件为例:
- 筛选有效的自动配置类:通过条件装配注解(@ConditionalXxxx系列注解)和AutoConfigurationMetadata
⭐类路径ClassPath
类路径(ClassPath) 指的是 Java 程序运行时,JVM 用于查找类(.class字节码文件)、资源(如配置文件、图片等)的路径。这些路径在编译、打包和运行时会被 JVM 识别并加载。
开发阶段(IDE 中)
在 Maven 或 Gradle 项目的标准目录结构中,类路径对应:
-
src/main/java
:存放 Java 源代码,编译后的.class
文件会被输出到target/classes
(Maven)或build/classes
(Gradle)。 -
src/main/resources
:存放资源文件(如application.properties
、META-INF/spring.factories
等),这些文件会被直接复制到target/classes
或build/classes
,成为类路径的一部分。例如,若在
src/main/resources/META-INF/
下创建spring.factories
,编译后会被放入target/classes/META-INF/spring.factories
,此时该文件处于类路径中,可被 Spring Boot 扫描到。
打包后(Jar/War 包中)
当项目打包为 Jar 或 War 包后,类路径对应包内的根目录:
- Jar 包中,
META-INF/spring.factories
通常位于 Jar 包的根目录下的META-INF
文件夹中(如xxx.jar!/META-INF/spring.factories
)。
例如,Spring Boot 内置的 spring-boot-autoconfigure.jar
中就包含 META-INF/spring.factories
,定义了默认的自动配置类列表。
依赖包中的类路径
jar包中依赖的jar包会通过依赖传递,也成为项目的类路径。
Spring Boot 在启动时会扫描所有依赖 jar 包及当前项目自身类路径下的spring.factories
文件,汇总其中的配置信息。
⭐ @ConditionalXxxx系列注解
自动配置类并非在候选名单中就无条件生效,它可通过注解实现动态装配,满足条件才会被注册为Bean
对象。
@Conditional
注解是条件装配注解,作用是按照一定的条件进行判断,在满足给定条件后才会注册对应的Bean
对象到IOC
容器中。
使用位置:方法、类
@Conditional本身是一个父注解,它派生出大量的子注解用于具体的条件判断,以下是常用的几个:
注解 | 作用 |
---|---|
@ConditionalOnClass | 类路径中存在指定类时生效 |
@ConditionalOnMissingClass | 类路径中不存在指定类时生效 |
@ConditionalOnBean | 容器中存在指定 Bean 时生效 |
@ConditionalOnMissingBean | 容器中不存在指定 Bean 时生效 |
@ConditionalOnProperty | 配置文件中存在指定属性且值匹配时生效 |
@ConditionalOnWebApplication | 应用是 Web 应用时生效 |
@ConditionalOnNotWebApplication | 应用不是 Web 应用时生效 |
@ConditionalOnClass
:判断环境中有对应字节码文件,才注册bean到IOC
容器。
@Configuration
public class HeaderConfig{@Bean@ConditionalOnClass(name = "io.jsonwebtoken.Jwts") //判断环境中存在Jwts类才会注册此Bean对象public HeaderParser headerParser(){return new HeaderParser();}
}
@ConditionalOnMissingBean
:判断环境中没有对应dean(可根据类型或名称判断),才注册bean
到IOC
容器。
@Configuration
public class HeaderConfig{@Bean@ConditionalOnMissingBean //判断环境中不存在此类型的Bean,才会注册此Bean对象。也可指定类型(使用注解的value属性)或名称(使用注解的name属性)判断public HeaderParser headerParser(){return new HeaderParser();}
}
@ConditionalOnProperty
:判断application
配置文件中有对应属性和值,才注册bean
到IOC
容器。
@Configuration
public class HeaderConfig{@Bean@ConditionalOnProperty(name="name",havingValue="gezishan") //判断配置文件中存在属性为"name",属性值为"gezishan"的配置项,才会注册此Bean对象public HeaderParser headerParser(){return new HeaderParser();}
}
除此之外还有许多派生注解:
🍀 AutoConfigurationMetadata
核心作用
AutoConfigurationMetadata
是 Spring Boot 自动配置过程中用于存储和提供自动配置类条件信息的元数据容器,它的核心作用是优化自动配置类的条件判断效率,避免在类路径扫描和条件校验时进行不必要的类加载或反射操作。
在 Spring Boot 自动配置中,大量自动配置类(如 WebMvcAutoConfiguration
、DataSourceAutoConfiguration
等)通过 @ConditionalXxxx
系列注解(如 @ConditionalOnClass
、@ConditionalOnMissingClass
)控制是否生效。这些条件判断通常需要检查类路径中是否存在某个类,或某个类是否有特定方法等。
如果每次直接通过反射或类加载器实时检查,会有两个问题:
- 性能开销:频繁的类加载和反射操作会降低启动速度。
- 潜在冲突:某些类可能在检查时被提前加载,导致后续逻辑异常。
数据来源
AutoConfigurationMetadata
的出现就是为了预先生成并缓存这些条件判断所需的元数据,在自动配置阶段直接读取元数据,避免实时检查,提升效率。
AutoConfigurationMetadata
的元数据来自于编译期生成的 META-INF/spring-autoconfigure-metadata.properties
文件。
这个文件由 Spring Boot 提供的 spring-boot-autoconfigure-processor
注解处理器在项目编译时自动生成,内容是对所有自动配置类的条件注解信息的预处理结果。
例如,对于注解了 @ConditionalOnClass({DataSource.class})
的 DataSourceAutoConfiguration
,编译后会在 spring-autoconfigure-metadata.properties
中生成类似如下的条目:
# 格式:自动配置类全类名.条件类型=值
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.ConditionalOnClass=javax.sql.DataSource
核心步骤
-
快速条件校验
在
AutoConfigurationImportSelector
筛选自动配置类时,会先通过AutoConfigurationMetadata
读取预存的条件信息(如需要检查的类名),而非直接加载类。只有元数据指示 “可能符合条件” 时,才会进一步通过类加载器验证,减少不必要的类加载。 -
缓存元数据
AutoConfigurationMetadata
会将spring-autoconfigure-metadata.properties
中的内容加载到内存中,形成键值对缓存,供自动配置过程中快速查询。 -
支持多种条件类型
除了
@ConditionalOnClass
,它还能处理@ConditionalOnMissingClass
、@ConditionalOnResource
等注解的元数据,例如:# 检查是否缺少某个类 org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.ConditionalOnMissingClass=org.springframework.web.reactive.DispatcherHandler # 检查是否存在某个资源 org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.ConditionalOnResource=classpath:org/hibernate/cache/ spi/RegionFactory.class
使用场景
作为开发者,我们通常不需要直接操作 AutoConfigurationMetadata
,但了解它的存在有助于理解自动配置的效率优化机制。
- 当自定义自动配置类时,只要在项目中引入
spring-boot-autoconfigure-processor
依赖(通常已包含在spring-boot-starter
中),编译时就会自动生成元数据文件,无需手动干预。 - 若需排查自动配置类未生效的原因,可查看
spring-autoconfigure-metadata.properties
中的元数据,确认条件判断的预期值是否与实际环境一致。
⭐个性化配置
配置绑定 @ConfigurationProperties
自动配置类通常会通过 @ConfigurationProperties
注解,将配置文件(如 application.yaml
)中的属性与 Java 类绑定,实现配置的动态注入。
示例:DataSourceProperties
绑定数据库配置:
@ConfigurationProperties(prefix = "spring.datasource") // 绑定前缀为spring.datasource的配置
public class DataSourceProperties {private String url;private String username;private String password;// getters/setters
}
配置文件 application.properties
中可直接设置:
spring:datasource:url: jdbc:mysql://localhost:3306/testusername: rootpassword: 123456
自动配置的优先级:用户配置 > 自动配置
SpringBoot 遵循用户配置优先于自动配置的原则(个性先于全局):
- 若用户手动定义了某个 Bean(如
@Bean
注解),则自动配置类中通过@ConditionalOnMissingBean
标注的同名 Bean 会被忽略。 - 例如:用户自定义了
DataSource
类型的 Bean,则DataSourceAutoConfiguration
中自动配置的数据源会失效。
Starter规范
Spring
项目中的Starter
就是前面讲得起步依赖。
SpringBoot
有自带的Starter
,如:spring-boot-starter-web
、spring-boot-starter-aop
等(一般规范:模块名在后)。- 很多第三方库也有对应的
SpringBoot
的Starter
,如:mybatis-spring-boot-starter
,pagehelper-spring-boot-starter
等(一般规范:模块名在前)。
Starter
一般用于实现自动装配的配置,通过Starter
可实现快速适配Spring
项目,当然,也有不少第三方库没有Starter
,我们可以通过自己写Starter
来实现对第三方库中的一些对象工具进行Bean
声明等配置,以实现快速适配Spring
项目,并实现组件化复用。在实际开发中,经常会定义一些公共组件,提供给各个项目团队使用。在SpringBoot
项目中,一般会将这些组件封装为Starter
。
一般Starter
中需要引入一份spring-boot-autoconfigure
依赖以实现对Bean
对象的自动配置:
我们先来分析一下mybatis-spring-boot-starter
依赖的jar
包:
目录结构如上图所示,mybatis
的starter
依赖jar
包中没有一行代码,其中有一些配置文件,其pow.xml
文件引入了很多有关mybatis
操作的依赖:
包括:
- 起步依赖
spring-boot-starter
,使用spring
提供的注解@Bean
、@Configurition
及其他基本功能实现对mybatis
的Bean
的配置等。 - 起步依赖
spring-boot-starter-jdbc
,使用spring-boot
封装的jdbc
功能,实现对数据库的操作 mybatis
依赖,实现mybatis
的核心功能mybatis-spring
依赖,基于spring
的mybatis
的功能mybatis-spring-boot-autoconfigure
,实现spring
自动装配的核心配置