目录
- Spring Boot 原理
- 配置优先级
- Bean 管理
- 获取 Bean
- Bean 的作用域
- 第三方 Bean
- Spring Boot 底层原理
- 起步依赖
- 自动配置
- 核心原理
- 实例说明
- 例 1:自定义一个 “日志 starter”
- 例 2:SpringBoot 自带的 spring-boot-starter-web
- 关键总结
Spring Boot 原理
配置优先级
Spring Boot 中支持三种格式的配置文件:
-
application.properties
server.port=8081
-
application.yml
server:port: 8082
-
application.yaml
server:port: 8083
在代码中配置这三个文件,运行程序的结果如下;
Tomcat 服务器在 8081 端口运行
如果只配置 application.yml 和 application.yaml,程序运行结果如下:
Tomcat 服务器在 8082 端口运行
通过测试同一属性在三个文件中的配置,得出优先级:properties 最高,yml 次之,yaml 最低。
Spring Boot 除了支持配置文件属性配置,还支持 Java 系统属性和命令行参数的方式进行属性配置
- Java系统属性:
-Dserver.port=9000
- 命令行参数:
--server.port=9001
命令行参数优先级高于 Java 系统属性。
在 Spring Boot 项目打包后,若需配置属性(如端口号等),可通过 Java 系统属性和命令行参数两种方式进行,具体操作如下:
-
项目打包前提
-
打包需执行 Maven 的
package
生命周期,生成可运行的 jar 包。 -
注意:Spring Boot 项目打包必须依赖
spring-boot-maven-plugin
插件,基于官方骨架创建的项目会自动引入该插件,无需手动添加。
-
-
运行打包后的 jar 包
-
基本命令:
java -jar 项目名.jar
-
(例如:
java -jar spring-boot-web-config.jar
,可通过tab
键自动补全 jar 包名称)
-
-
配置 Java 系统属性
-
格式:在
java
命令后、-jar
之前,使用-Dkey=value
格式配置。 -
示例:配置 Tomcat 端口号为 9000
java -Dserver.port=9000 -jar spring-boot-web-config.jar
-
-
配置命令行参数
-
格式:在 jar 包名称之后,使用
--key=value
格式配置。 -
示例:配置 Tomcat 端口号为 10010
java -jar spring-boot-web-config.jar --server.port=10010
-
五种配置方式的优先级:从高到低依次为命令行参数、Java 系统属性、properties 配置文件、yml 配置文件、yaml 配置文件。
Bean 管理
获取 Bean
默认情况下,Spring 项目启动时,会把 Bean 都创建好放在 IOC 容器中,如果想要主动获取这些 Bean,可以通过以下方式:
- 根据 name 获取 Bean:
Object getBean(String name)
- 根据类型获取 Bean:
<T> T getBean(Class<T> requiredType)
- 根据 name 获取 Bean(带类型转换):
<T> T getBean(String name, Class<T> requiredType)
在测试类中加入以下代码进行获取 Bean 的测试:
@Autowired
// 获取ApplicationContext对象
private ApplicationContext applicationContext;
@Test
public void testGetBean(){// 根据Bean的名称获取DeptController bean1 = (DeptController) applicationContext.getBean("deptController");System.out.println(bean1);// 根据Bean的类型获取DeptController bean2 = applicationContext.getBean(DeptController.class);System.out.println(bean2);// 根据Bean的名称和类型获取DeptController bean3 = applicationContext.getBean("deptController", DeptController.class);System.out.println(bean3);
}
测试运行结果如下:
三次调用 getBean 方法获取同一 bean 对象,其地址值相同,说明默认情况下 bean 是单例的。
Spring 项目启动时创建所有 bean 对象并放在 IOC 容器中,这仅针对默认情况下单例且非延迟加载的 bean,bean 的创建时间还受作用域和延迟初始化影响
Bean 的作用域
bean 是单例还是多例取决于其作用域,Spring 中 bean 支持五种作用域,重点关注前两种,后三种在 web 环境生效:
作用域 | 说明 |
---|---|
singleton | 在整个 Spring 容器中,同名称的 bean 对象只有一个实例,即单例,是作用域的默认值 |
prototype | 代表非单例,每一次使用该 bean 对象时,都会创建一个新的实例对象 |
request | 代表每一次请求对应一个实例对象 |
session | 代表每一次会话对应一个新的实例对象 |
application | 代表每一个应用对应一个实例对象 |
默认情况下,未设置作用域的 bean 是单例的,在 Spring 项目启动、IOC 容器创建时就会实例化并放到容器中,多次获取的是同一个对象。
设置 bean 作用域的方式:可以借助 Spring 中的 @Scope 注解来配置 bean 的作用域。
在要获取 Bean 的类上添加 @Scope("prototype")
,运行前面的测试代码,结果如下:
延迟初始化注解 @lazy
:在类上添加 @lazy
注解后,bean 会延迟初始化,延迟到第一次使用的时候实例化。
注意事项:
- 默认作用域为 singleton,默认单例 bean 在容器启动时创建,可通过
@lazy
延迟到第一次使用时创建 - prototype 非单例 bean 每次使用都会创建新实例
第三方 Bean
第三方 Bean 配置的必要性:项目中引入的第三方依赖提供的类(如 dom4j 中的 SAXReader),若每次使用都新建对象会耗费资源,需交给 Spring 的 IOC 容器管理,通过依赖注入使用。
第三方 Bean 配置的特殊之处:第三方类是只读的,无法直接在类上添加 @Component 及其衍生注解声明为 bean,需使用 @Bean 注解。
@Bean 注解的使用方法:在方法上添加 @Bean 注解,方法返回值为要管理的第三方 Bean 对象,Spring 会将方法返回值交给 IOC 容器管理,后续可通过 @Autowired 注入使用。
第三方 Bean 配置的位置:建议单独定义配置类(用 @Configuration 标识),在配置类中集中配置第三方 bean。
第三方 Bean 的名称规则:可通过 @Bean 的 name 或 value 属性指定名称,二者互为别名;未指定时,默认名称为方法名。
第三方 Bean 声明时的依赖注入:在定义第三方 bean 的方法中声明对应类型的形参,Spring 容器会根据类型自动装配 IOC 容器中的对应 Bean 对象。
Spring Boot 底层原理
Spring Boot 简化开发的原因:底层提供起步依赖和自动配置两个重要功能。起步依赖简化 pom 文件依赖配置,解决 Spring 框架依赖配置繁琐问题;自动配置简化框架使用时 Bean 的声明和配置,引入起步依赖后常见配置已存在,可直接使用。
起步依赖
以 Web 程序开发为例,使用 Spring 框架需引入多个依赖且版本需匹配,而使用 Spring Boot 只需引入对应的起步依赖(如 web 开发的 spring-boot-starter-web,aop 开发的 spring-boot-starter-aop)。其原理是 Maven 的依赖传递,起步依赖集成了开发所需的常见依赖,引入一个起步依赖后,其他依赖会通过依赖传递自动引入(若 A 依赖 B,B 依赖 C,C 依赖 D,引入 A 则 B、C、D 也会被引入)。
自动配置
自动配置的定义:指 Spring Boot 项目启动时,除了用户自己定义的 Bean 对象外,Spring Boot 会自动创建一些内置的配置类及 Bean 对象并放入 IOC 容器,使用户在开发时无需手动声明即可直接使用,简化开发,省去繁琐配置。
通过启动 Spring Boot 工程,在控制台的 Bean 中可查看所有 Bean 对象及配置类,包括用户自己定义的和 Spring Boot 自动加载的配置类及其生成的 Bean 对象
核心原理
自动配置的实现依赖 3 个关键机制:@EnableAutoConfiguration 注解、SPI 机制(META-INF/spring.factories)、条件注解(@Conditional)。三者协同工作,流程如下:
- 触发点:@SpringBootApplication 注解
SpringBoot 项目的启动类通常标注@SpringBootApplication
,它是一个组合注解,包含 3 个核心注解:
@SpringBootConfiguration
:标记当前类为配置类(类似@Configuration
)。@ComponentScan
:默认扫描启动类所在包及其子包(但自动配置不依赖它,而是通过其他机制加载外部包的 Bean)。@EnableAutoConfiguration
:自动配置的 “开关”,正是这个注解触发了后续的自动配置流程。
- 核心:@EnableAutoConfiguration 的作用
@EnableAutoConfiguration
通过@Import(AutoConfigurationImportSelector.class)
导入了AutoConfigurationImportSelector
类,这个类是自动配置的 “核心执行者”,主要做两件事:
- 加载候选配置类:扫描类路径下所有
META-INF/spring.factories
文件,读取其中org.springframework.boot.autoconfigure.EnableAutoConfiguration
对应的配置类全类名(这些是 “候选自动配置类”)。 - 筛选有效配置类:通过
spring.factories
加载的候选配置类会经过条件注解(如@ConditionalOnClass
)的筛选,只有满足条件的配置类才会被真正加载到 IOC 容器。
- SPI 机制:META-INF/spring.factories 的作用
META-INF/spring.factories
是 Java 的 SPI(Service Provider Interface)机制在 SpringBoot 中的应用,用于声明 “自动配置类” 的位置。
格式如下(key 固定为org.springframework.boot.autoconfigure.EnableAutoConfiguration
,value 为自动配置类的全类名列表):
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyAutoConfiguration,\
com.example.OtherAutoConfiguration
当项目启动时,AutoConfigurationImportSelector
会读取所有依赖中的spring.factories
,收集所有声明的自动配置类,作为候选者。
- 条件注解:筛选有效的配置类
候选配置类不会全部生效,需要通过条件注解判断是否满足生效条件。常见的条件注解有:
@ConditionalOnClass
:类路径下存在指定类时,配置类才生效(如引入spring-web
依赖后,DispatcherServlet.class
存在,Web 相关配置才生效)。@ConditionalOnMissingBean
:容器中不存在指定 Bean 时,配置类才生效(允许开发者自定义 Bean 覆盖默认配置)。@ConditionalOnProperty
:配置文件中存在指定属性时生效(如server.port
配置触发端口绑定)。
只有满足所有条件的配置类,才会被 Spring 实例化,其内部定义的 Bean 才会被注册到 IOC 容器。
总结流程:
- 启动类标注
@SpringBootApplication
,触发@EnableAutoConfiguration
。 AutoConfigurationImportSelector
扫描所有META-INF/spring.factories
,收集候选自动配置类。- 候选配置类通过条件注解筛选,保留有效配置类。
- 有效配置类被加载,其内部的 Bean(如
DataSource
、DispatcherServlet
)被注册到 IOC 容器。
实例说明
下面通过两个例子(自定义 starter 和 SpringBoot 自带 starter)直观理解自动配置。
例 1:自定义一个 “日志 starter”
假设我们要开发一个my-log-starter
,功能是:引入后自动配置一个LogService
Bean,用于打印日志。
步骤 1:定义核心 Bean 和配置类
-
LogService:需要被自动配置的 Bean。
public class LogService {public void log(String message) {System.out.println("[MyLog] " + message);} }
-
LogAutoConfiguration:自动配置类,负责注册
LogService
。@Configuration // 标记为配置类 @ConditionalOnClass(LogService.class) // 类路径存在LogService时生效 public class LogAutoConfiguration {// 注册LogService到IOC容器@Bean@ConditionalOnMissingBean // 若用户自定义了LogService,则不使用默认的public LogService logService() {return new LogService();} }
步骤 2:通过 spring.factories 声明自动配置类
在src/main/resources
下创建META-INF/spring.factories
,声明LogAutoConfiguration
为候选配置类:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.log.LogAutoConfiguration
步骤 3:使用 starter
-
其他项目引入
my-log-starter
依赖后,启动项目时:
AutoConfigurationImportSelector
读取spring.factories
,发现LogAutoConfiguration
。- 检查到类路径存在
LogService.class
(依赖已引入),且容器中没有自定义的LogService
,满足条件。 LogAutoConfiguration
生效,logService()
方法被执行,LogService
Bean 被注册到 IOC 容器。
-
开发者可直接注入使用:
@RestController public class TestController {@Autowiredprivate LogService logService; // 直接使用自动配置的Bean@GetMapping("/test")public String test() {logService.log("测试日志"); // 输出:[MyLog] 测试日志return "ok";} }
例 2:SpringBoot 自带的 spring-boot-starter-web
引入spring-boot-starter-web
后,SpringBoot 会自动配置 Web 开发所需的核心组件(如 Tomcat、DispatcherServlet),原理如下:
- 依赖引入:
starter-web
包含spring-web
、spring-webmvc
、tomcat-embed-core
等依赖。 - 自动配置类:
spring-boot-autoconfigure
包的META-INF/spring.factories
中声明了DispatcherServletAutoConfiguration
、TomcatAutoConfiguration
等配置类。 - 条件判断:
TomcatAutoConfiguration
通过@ConditionalOnClass(Tomcat.class)
判断:因引入了tomcat-embed-core
,Tomcat 类存在,配置生效,自动启动内嵌 Tomcat。DispatcherServletAutoConfiguration
通过@ConditionalOnClass(DispatcherServlet.class)
判断:因引入spring-webmvc
,DispatcherServlet 类存在,配置生效,注册DispatcherServlet
到容器。
- 最终效果:开发者无需手动配置 Tomcat 和 DispatcherServlet,引入依赖即可开发 Web 接口。
关键总结
- 自动配置的核心是:通过 @EnableAutoConfiguration 触发,SPI 机制加载候选配置类,条件注解筛选有效配置类。
- 开发者可通过自定义 starter(含
spring.factories
和自动配置类)实现自动配置,也可通过@ConditionalOnMissingBean
等注解覆盖默认配置。 - SpringBoot 的 starter(如
web
、data-jpa
)都是基于此原理实现,极大简化了配置流程。