SpringBoot底层原理详解
SpringBoot 底层原理详解
本章位置:第二阶段 Java 核心框架
前置知识:SpringBoot 请求响应、RESTful、MyBatis、事务、AOP、反射、注解
下一篇:Maven 高级
学习目标:从“会使用 SpringBoot”进入“理解 SpringBoot 为什么能自动工作”,掌握 IOC、Bean、依赖注入、组件扫描、自动配置、条件装配、Starter、Bean 生命周期、SpringApplication 启动流程、内嵌 Tomcat 和 DispatcherServlet 的底层关系。
一、为什么要学 SpringBoot 底层原理
前面我们已经会:
@SpringBootApplication
@RestController
@Service
@Mapper
@Autowired
@Transactional
@RequestMapping
但如果只会:
复制注解
遇到下面这些问题就很容易迷糊:
为什么 Controller 会自动被发现?
为什么 Service 不需要自己 new?
为什么 MyBatis Mapper 可以直接注入?
为什么配置一个 starter 就能使用某项功能?
为什么 SpringBoot 能自动启动 Tomcat?
为什么 application.yml 能改变配置?
为什么有些 Bean 有、有些 Bean 没有?
为什么 @ConditionalOnMissingBean 可以“你没有我才帮你创建”?
为什么改包结构以后 Controller 404?
为什么一个 Bean 可以被 AOP 代理?
SpringApplication.run() 到底做了什么?
所以这一章目标是:
把 SpringBoot 黑盒拆开
二、先区分 Spring、Spring MVC、SpringBoot
这三个不要混淆。
三、Spring Framework
Spring Framework 提供核心能力:
IOC
DI
AOP
事务
事件
资源管理
Web MVC
SpringBoot:
建立在 Spring Framework 之上
四、Spring MVC
Spring MVC 是 Spring 的:
Web MVC 框架
主要负责:
HTTP 请求
DispatcherServlet
Controller
参数绑定
响应转换
异常处理
五、SpringBoot
SpringBoot 的核心目标:
简化 Spring 项目配置和启动
它没有把 Spring 替换掉。
而是:
约定优于配置
自动配置
Starter
内嵌服务器
快速启动
六、三者关系
Spring Framework
│
├─ IOC
├─ AOP
├─ Transaction
└─ Spring MVC
└─ Web
↑
│
SpringBoot
负责快速装配、自动配置、启动
七、SpringBoot 最大的两个关键词
最重要:
自动配置
约定优于配置
八、没有 SpringBoot 的传统 Spring
以前可能需要配置:
XML Bean
DispatcherServlet
ViewResolver
DataSource
TransactionManager
JSON Converter
Tomcat Deployment
配置很多。
九、有 SpringBoot 后
很多东西:
自动创建
自动推断
自动装配
开发者更多关注:
业务代码
十、SpringBoot 不是“什么都自动”
它的自动配置有条件。
也就是:
符合条件
才配置
这就是后面的:
Conditional
条件装配
十一、IOC 是什么
IOC:
Inversion of Control
中文:
控制反转
十二、什么叫控制反转
以前:
UserService service =
new UserService();
对象由:
程序员自己创建
现在:
@Service
public class UserService {
}
Spring:
帮你创建对象
帮你保存对象
帮你管理对象
对象控制权:
从程序员
转交给 Spring 容器
这叫:
IOC
十三、IOC 容器
Spring 中管理对象的容器:
ApplicationContext
可以简单理解:
一个高级 Bean 工厂
里面保存:
Bean
十四、Bean 是什么
Spring Bean:
被 Spring 容器创建和管理的 Java 对象。
例如:
@Service
public class UserService {
}
UserService 实例:
就是一个 Bean
十五、哪些对象可以成为 Bean
常见:
@Controller
@RestController
@Service
@Repository
@Component
@Configuration
标记的类。
也可以:
@Bean
手工声明。
十六、@Component
最基础组件注解:
@Component
public class EmailUtil {
}
表示:
让 Spring 管理这个类
十七、@Service
语义上表示:
业务层 Bean
本质:
属于组件注解体系
十八、@Repository
语义上表示:
持久层组件
并且在 Spring 数据访问体系中还有:
异常转换
相关语义。
十九、@Controller
表示:
Spring MVC Controller
二十、@RestController
可以理解:
@Controller
+
@ResponseBody
二十一、@Configuration
表示:
配置类
通常用来:
声明 @Bean
添加框架配置
二十二、@Bean
如果某个类:
不能直接加 @Component
例如第三方库:
ObjectMapper
PasswordEncoder
某 SDK Client
可以:
@Configuration
public class AppConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
二十三、@Bean 方法返回值
Spring 会:
调用方法
取得返回对象
注册到 IOC 容器
Bean 名默认常见:
方法名
例如:
passwordEncoder
二十四、IOC 和 DI 的关系
IOC:
对象由 Spring 管理
DI:
Spring 把对象依赖注入给另一个对象
二十五、DI 是什么
DI:
Dependency Injection
中文:
依赖注入
二十六、传统写法
public class UserService {
private UserMapper userMapper =
new UserMapperImpl();
}
UserService:
自己创建依赖
耦合高。
二十七、Spring 写法
@Service
public class UserService {
private final UserMapper
userMapper;
public UserService(
UserMapper userMapper
) {
this.userMapper =
userMapper;
}
}
Spring:
从容器找到 UserMapper Bean
↓
传进构造器
二十八、构造器注入
推荐:
private final UserMapper userMapper;
public UserService(
UserMapper userMapper
) {
this.userMapper =
userMapper;
}
二十九、为什么推荐构造器注入
优点:
依赖明确
字段可以 final
方便测试
对象创建后依赖完整
避免隐藏依赖
三十、字段注入
以前常见:
@Autowired
private UserMapper userMapper;
虽然能用,
但现在更推荐:
构造器注入
三十一、只有一个构造器时
现代 Spring:
通常不需要写 @Autowired
Spring 会自动:
选择唯一构造器
三十二、DI 按什么找 Bean
常见:
按类型
例如:
UserMapper userMapper
Spring 找:
UserMapper 类型 Bean
三十三、如果同类型有两个 Bean
例如:
PaymentService
实现:
AliPayService
WechatPayService
注入:
PaymentService
Spring 不知道选谁。
可能:
NoUniqueBeanDefinitionException
三十四、@Primary
可以:
@Primary
@Service
public class AliPayService
implements PaymentService {
}
表示:
默认优先
三十五、@Qualifier
也可以:
public OrderService(
@Qualifier(
"wechatPayService"
)
PaymentService paymentService
) {
}
指定 Bean。
三十六、Bean 名
默认:
类名首字母小写
例如:
UserService
→ userService
三十七、ApplicationContext 获取 Bean
虽然业务中不推荐到处手工获取,
但为了理解:
ApplicationContext context =
SpringApplication.run(
DemoApplication.class,
args
);
UserService userService =
context.getBean(
UserService.class
);
三十八、说明什么
说明:
SpringApplication.run()
返回的是:
ApplicationContext
即:
Spring IOC 容器
三十九、组件扫描是什么
Spring 不可能:
扫描整个硬盘所有 Java 类
它只扫描:
指定包范围
找到:
组件注解
注册 Bean。
四十、@ComponentScan
用于:
组件扫描
例如:
@ComponentScan(
"com.example"
)
四十一、SpringBoot 为什么不用我们写 ComponentScan
因为:
@SpringBootApplication
本身包含:
@ComponentScan
相关功能。
四十二、启动类位置为什么重要
推荐:
启动类放根包
例如:
com.example.demo
├─ DemoApplication
├─ controller
├─ service
├─ mapper
└─ config
四十三、错误包结构
例如:
com.example.app
└─ DemoApplication
com.other.controller
└─ UserController
默认扫描:
可能扫不到 com.other
于是:
Controller 没 Bean
→ 请求 404
四十四、@SpringBootApplication
这是 SpringBoot 最核心注解之一。
@SpringBootApplication
public class DemoApplication {
}
四十五、@SpringBootApplication 大致包含
可以理解为组合:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
四十六、@SpringBootConfiguration
本质上属于:
@Configuration
语义:
当前类是 SpringBoot 主配置类
四十七、@EnableAutoConfiguration
表示:
开启自动配置
这是 SpringBoot 最核心的能力之一。
四十八、@ComponentScan
负责:
扫描用户自己写的组件
四十九、自动配置 vs 组件扫描
组件扫描:
找到你自己写的 Bean
自动配置:
根据依赖和条件
帮你创建框架 Bean
不要混淆。
五十、组件扫描例子
你写:
@Service
public class UserService {
}
通过:
Component Scan
注册。
五十一、自动配置例子
你引入:
spring-boot-starter-web
SpringBoot 检测到:
Spring MVC
Servlet
Jackson
Tomcat
然后自动配置:
DispatcherServlet
MessageConverter
Web MVC 基础设施
内嵌服务器
五十二、自动配置本质
可以理解:
SpringBoot 内部提前写了很多配置类
例如:
WebMvcAutoConfiguration
JacksonAutoConfiguration
DataSourceAutoConfiguration
满足条件时:
导入
创建 Bean
五十三、自动配置不是“魔法”
它本质仍然:
@Configuration
@Bean
@Conditional...
只是:
框架提前帮你写好
五十四、条件装配是什么
条件装配:
满足某个条件时,才注册 Bean。
常见注解:
@ConditionalOnClass
@ConditionalOnMissingBean
@ConditionalOnBean
@ConditionalOnProperty
@ConditionalOnWebApplication
五十五、@ConditionalOnClass
意思:
classpath 中存在某个类
才配置
例如:
有 Jackson
才做 Jackson 自动配置
五十六、classpath 是什么
运行时:
JVM 可以找到的类路径
Maven 依赖:
下载 jar
↓
加入 classpath
于是 SpringBoot 可以检测:
某个类是否存在
五十七、@ConditionalOnMissingBean
意思:
容器里没有这个 Bean
SpringBoot 才帮你创建
五十八、这是“用户配置优先”的关键
例如 SpringBoot:
默认提供 ObjectMapper
但如果你自己:
@Bean
public ObjectMapper objectMapper() {
...
}
自动配置通常:
退让
不再创建默认 Bean。
五十九、为什么说“约定优于配置”
默认情况:
你什么都不配置
也能运行
如果你需要自定义:
覆盖部分默认行为
六十、@ConditionalOnBean
意思:
容器已经存在某个 Bean
才启用当前配置
六十一、@ConditionalOnProperty
根据:
配置文件属性
决定是否启用。
例如:
feature.sms.enabled=true
才启用短信 Bean。
六十二、@ConditionalOnWebApplication
只有:
Web 应用
才装配。
六十三、Starter 是什么
Starter:
一组依赖的“启动依赖包”
核心作用:
帮你一次性引入一组相关依赖
六十四、spring-boot-starter-web
引入后常包含:
Spring MVC
Tomcat
Jackson
Validation/Logging 的相关基础依赖
具体传递依赖随版本可能变化。
六十五、Starter 本身不等于自动配置
Starter 主要:
解决依赖组合
AutoConfiguration:
解决 Bean 配置
二者一起:
构成 SpringBoot 快速开发体验
六十六、Starter + AutoConfiguration
流程:
添加 Starter
↓
相关类进入 classpath
↓
自动配置类判断条件
↓
条件满足
↓
创建 Bean
六十七、为什么加 starter 就能用 Web
因为:
Web 相关类进入 classpath
自动配置检测到:
Web 环境
于是:
装配 Web 基础设施
六十八、SpringBoot 3 自动配置文件
现代 SpringBoot 中自动配置元数据通常使用:
META-INF/spring/
org.springframework.boot.autoconfigure.AutoConfiguration.imports
列出:
自动配置类
六十九、为什么要知道这个
当以后自己写:
Starter
就会用到。
七十、以前的 spring.factories
较老 SpringBoot 版本常通过:
META-INF/spring.factories
发现自动配置。
现代版本:
自动配置导入机制已经演进
所以学习时不要只背老版本写法。
七十一、AutoConfiguration 类
现代写法常见:
@AutoConfiguration
public class XxxAutoConfiguration {
}
内部再:
@Bean
@Conditional...
七十二、自动配置简化示例
假设我们写短信功能:
@AutoConfiguration
@ConditionalOnClass(
SmsClient.class
)
public class SmsAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SmsService smsService() {
return new SmsService();
}
}
七十三、逻辑
有 SmsClient 类
↓
用户没自己定义 SmsService
↓
SpringBoot 自动创建 SmsService
七十四、application.yml 为什么能配置 Bean
因为 SpringBoot 建立:
Environment
统一读取:
application.properties
application.yml
环境变量
命令行参数
七十五、Environment
Spring 中可以:
private final Environment
environment;
读取:
environment.getProperty(
"server.port"
);
七十六、配置优先级
不同来源:
配置文件
环境变量
命令行参数
有:
优先级规则
SpringBoot 会合并成:
Environment
七十七、@Value
例如:
@Value(
"${app.name}"
)
private String appName;
七十八、@ConfigurationProperties
大量配置更推荐:
@ConfigurationProperties(
prefix = "app"
)
public class AppProperties {
private String name;
private Integer timeout;
}
七十九、为什么 ConfigurationProperties 更适合复杂配置
相比多个:
@Value
它:
类型安全
结构清晰
支持嵌套配置
便于复用
八十、配置绑定本质
SpringBoot:
读取 Environment
↓
属性名匹配
↓
类型转换
↓
绑定 JavaBean
这又用到了:
反射
JavaBean
类型转换
八十一、BeanDefinition 是什么
Spring 创建 Bean 前:
先保存“Bean 的定义信息”
这个元数据可以理解为:
BeanDefinition
八十二、BeanDefinition 保存什么
例如:
Bean 类型
Bean 名
作用域
构造方式
初始化方法
是否懒加载
依赖关系
八十三、Spring 启动并不是先把所有对象直接 new 出来
大致:
扫描类
↓
注册 BeanDefinition
↓
执行 BeanFactory 后处理
↓
创建 Bean
↓
依赖注入
↓
初始化
八十四、BeanFactory
Spring 最核心的 Bean 容器接口之一:
BeanFactory
主要:
创建 Bean
获取 Bean
管理 Bean
八十五、ApplicationContext 和 BeanFactory
ApplicationContext:
建立在 BeanFactory 之上
并扩展:
事件
国际化
资源
环境
Web 上下文
所以业务项目常接触:
ApplicationContext
八十六、Bean 的作用域
常见:
singleton
prototype
request
session
八十七、singleton
Spring 默认:
单例作用域
意思:
一个 ApplicationContext 中
通常一个 Bean 实例
八十八、这就是为什么 Controller 不能保存当前用户
Controller 默认:
singleton
多个用户:
共享同一个 Controller 对象
所以请求数据不能:
放成员变量
八十九、prototype
每次获取 Bean:
创建新实例
九十、request scope
一个 HTTP 请求:
一个 Bean 实例
九十一、session scope
一个 Session:
一个 Bean 实例
九十二、普通 Service 为什么适合 singleton
因为 Service 应该设计为:
无状态
请求数据:
只放局部变量
九十三、Bean 生命周期
这是 Spring 面试高频。
大致:
实例化
属性填充 / 依赖注入
Aware 回调
BeanPostProcessor 前置
初始化
BeanPostProcessor 后置
使用 Bean
销毁
九十四、第一步:实例化
Spring:
选择构造器
↓
创建对象
九十五、第二步:依赖注入
例如:
UserService
需要 UserMapper
Spring:
把 UserMapper 注入
九十六、Aware
某些 Bean 可以通过:
BeanNameAware
ApplicationContextAware
获得容器相关信息。
九十七、BeanPostProcessor
非常重要。
BeanPostProcessor:
Bean 初始化前后
可以统一处理 Bean
九十八、AOP 为什么和 BeanPostProcessor 有关系
Spring AOP 代理对象的创建:
和 Bean 后置处理机制密切相关
可以理解:
Spring 创建原始 Bean
↓
后处理器判断是否需要 AOP
↓
如果需要
创建 Proxy
↓
容器最终暴露 Proxy
九十九、这就解释了为什么注入的 Service 可能是代理对象
你写:
UserService
容器实际给 Controller 的可能是:
UserService Proxy
于是:
@Transactional
@Around
才能生效。
一百、@PostConstruct
Bean 依赖注入完成后,
可以:
@PostConstruct
public void init() {
}
用于初始化。
SpringBoot 3 使用:
jakarta.annotation.PostConstruct
一百零一、@PreDestroy
Bean 销毁前:
@PreDestroy
public void destroy() {
}
用于:
释放资源
一百零二、@Bean initMethod
也可以:
@Bean(
initMethod = "init",
destroyMethod = "close"
)
一百零三、InitializingBean
还可以实现:
InitializingBean
但业务代码更常用:
@PostConstruct
或:
@Bean initMethod
一百零四、Bean 生命周期为什么重要
因为很多框架功能依赖:
Bean 创建过程
例如:
AOP
事务代理
ConfigurationProperties
@Autowired
事件监听
一百零五、循环依赖是什么
例如:
A 依赖 B
B 又依赖 A
一百零六、构造器循环依赖
A(
B b
)
B(
A a
)
创建 A:
需要 B
创建 B:
又需要 A
无法完成。
一百零七、为什么不要依赖 Spring “自动解决”循环依赖
循环依赖往往意味着:
设计职责不清
最佳方案:
重构
一百零八、SpringBoot 2.6+ 对循环依赖更严格
现代 SpringBoot 默认:
更倾向不允许循环引用
所以不要依赖:
旧项目里某些 setter 循环依赖还能跑
一百零九、解决循环依赖
优先:
重新拆 Service
提取公共 Service
调整职责
而不是:
到处 @Lazy
一百一十、@Lazy
可以:
@Lazy
延迟创建 Bean。
但它不是:
循环依赖设计问题的万能解
一百一十一、Bean 什么时候创建
默认 singleton:
ApplicationContext refresh 时
通常提前创建
除非:
lazy
一百一十二、SpringBoot 启动入口
SpringApplication.run(
DemoApplication.class,
args
);
这是:
整个 SpringBoot 启动核心入口
一百一十三、SpringApplication.run() 大致做什么
可以简化成:
1. 创建 SpringApplication
2. 判断应用类型
3. 准备 Environment
4. 创建 ApplicationContext
5. 准备 Context
6. 加载 BeanDefinition
7. refresh Context
8. 创建 Bean
9. 启动内嵌 Web Server
10. 执行 Runner
11. 应用启动完成
一百一十四、创建 SpringApplication
内部会读取:
主启动类
例如:
DemoApplication.class
一百一十五、判断 WebApplicationType
SpringBoot 会判断:
Servlet Web
Reactive Web
Non-Web
例如引入:
spring-boot-starter-web
通常识别为:
SERVLET
一百一十六、准备 Environment
读取:
系统属性
环境变量
application.yml
application.properties
命令行参数
合并配置。
一百一十七、创建 ApplicationContext
Servlet Web 应用:
会使用适合 Web 的 ApplicationContext
一百一十八、加载主配置类
也就是:
@SpringBootApplication
public class DemoApplication {
}
一百一十九、refresh()
这是 Spring 容器非常核心的方法。
大量工作都在:
ApplicationContext.refresh()
期间发生。
一百二十、refresh 做什么
高度简化:
准备 BeanFactory
执行 BeanFactoryPostProcessor
注册 BeanPostProcessor
初始化事件机制
创建 singleton Bean
启动相关生命周期组件
一百二十一、BeanFactoryPostProcessor
它处理:
BeanDefinition
发生在:
Bean 真正创建之前
一百二十二、ConfigurationClassPostProcessor
非常重要。
负责处理:
@Configuration
@ComponentScan
@Import
@Bean
等配置类机制。
一百二十三、这就是注解配置能工作的关键之一
启动类:
@SpringBootApplication
会被:
ConfigurationClassPostProcessor
解析。
一百二十四、@Import
可以:
把其他配置类 / Bean 定义导入容器
自动配置也大量使用:
Import
思想。
一百二十五、Enable 注解模式
Spring 中很多:
@EnableXxx
本质常见:
@Import
导入某些配置。
一百二十六、@EnableAutoConfiguration
也会触发:
自动配置导入机制
一百二十七、自动配置选择过程
简化:
读取自动配置候选类
↓
应用条件过滤
↓
留下匹配配置
↓
注册 BeanDefinition
↓
创建 Bean
一百二十八、为什么 SpringBoot 启动日志里自动配置很多
因为内部:
检查大量条件
但真正创建的:
只是匹配当前项目的部分
一百二十九、Condition Evaluation Report
SpringBoot 可以输出:
条件评估报告
帮助查看:
哪些 AutoConfiguration 生效
哪些没生效
为什么
一百三十、debug 模式
可以配置:
debug=true
启动时可能显示:
CONDITIONS EVALUATION REPORT
一百三十一、Positive matches
表示:
满足条件
生效的自动配置
一百三十二、Negative matches
表示:
条件不满足
没有启用
一百三十三、排查自动配置问题
如果:
Bean 为什么没有?
可以检查:
有没有 starter
类是否在 classpath
属性是否正确
是否存在用户自定义 Bean
条件评估报告
一百三十四、内嵌 Tomcat 为什么自动启动
因为引入:
spring-boot-starter-web
后:
Tomcat 相关类在 classpath
自动配置发现:
Servlet Web Application
于是创建:
Servlet Web Server Factory
一百三十五、TomcatServletWebServerFactory
可以理解:
负责创建内嵌 Tomcat
一百三十六、SpringBoot 传统项目不需要外部 Tomcat
因为:
Tomcat 作为依赖
包含在应用中
执行:
main()
即可启动。
一百三十七、传统 WAR vs SpringBoot JAR
传统:
项目
↓
打 WAR
↓
外部 Tomcat
SpringBoot 常见:
项目
↓
可执行 JAR
↓
java -jar
↓
内嵌 Tomcat
一百三十八、可执行 JAR
SpringBoot Maven 插件会:
重新打包
让 JAR:
包含应用代码
依赖
启动信息
可以:
java -jar app.jar
一百三十九、SpringBoot Loader
SpringBoot 可执行 Jar 需要:
特殊加载机制
让嵌套依赖:
能够被加载
一百四十、为什么普通 JAR 不会直接包含依赖
普通 Maven JAR:
通常只包含项目 class/resources
依赖还在:
外部 classpath
SpringBoot repackage:
生成可执行结构
一百四十一、Spring Boot Maven Plugin
pom 常见:
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
</plugin>
用于:
SpringBoot 打包
一百四十二、DispatcherServlet 怎么来的
你没有手工:
new DispatcherServlet()
但 SpringBoot 自动配置:
帮你创建和注册
一百四十三、DispatcherServlet 核心作用
所有 Spring MVC 请求统一入口
流程:
Tomcat
↓
DispatcherServlet
↓
HandlerMapping
↓
HandlerAdapter
↓
Controller
↓
HttpMessageConverter
↓
Response
一百四十四、HandlerMapping
负责:
根据 URL + Method
找到哪个 Controller 方法
一百四十五、RequestMappingHandlerMapping
会扫描:
@Controller
@RestController
中的:
@RequestMapping
@GetMapping
@PostMapping
建立映射。
一百四十六、映射表
例如:
@GetMapping(
"/users/{id}"
)
Spring 启动时:
注册映射信息
请求到来:
快速找到对应 HandlerMethod
一百四十七、HandlerMethod
表示:
Controller Bean
+
具体 Method
这也是前面 Interceptor 中:
handler instanceof HandlerMethod
的来源。
一百四十八、HandlerAdapter
找到 Handler 后:
负责调用它
并处理:
参数解析
返回值处理
一百四十九、参数解析
例如:
@PathVariable
@RequestParam
@RequestBody
@RequestHeader
都是由不同:
HandlerMethodArgumentResolver
处理。
一百五十、HandlerMethodArgumentResolver
可以理解:
“这个参数我会解析”
例如:
@PathVariable
有对应 resolver。
一百五十一、这就是 Spring MVC 参数绑定为什么这么灵活
Controller 方法:
public Result<?> detail(
@PathVariable Long id,
@RequestHeader String token
)
Spring:
逐个找适合的参数解析器
一百五十二、@RequestBody 怎么解析
会使用:
HttpMessageConverter
例如:
MappingJackson2HttpMessageConverter
把:
JSON
→ Java Object
一百五十三、返回对象怎么变 JSON
Controller:
return userVO;
Spring:
返回值处理器
↓
HttpMessageConverter
↓
Jackson
↓
JSON
一百五十四、Jackson 为什么自动存在
因为:
Web Starter 带相关依赖
并且:
JacksonAutoConfiguration
等自动配置满足条件。
一百五十五、DataSource 为什么自动创建
当项目:
有 JDBC / MySQL Driver
并配置:
spring.datasource.url
username
password
SpringBoot 自动配置:
DataSource
一百五十六、DataSourceAutoConfiguration
就是典型:
自动配置类
根据条件:
创建 DataSource
一百五十七、连接池
SpringBoot 默认常见使用:
HikariCP
前提:
对应依赖和自动配置条件满足
一百五十八、为什么 MyBatis Mapper 能自动注入
MyBatis Spring Starter 会:
扫描 Mapper
创建 Mapper Proxy
注册为 Spring Bean
于是 Service:
public UserService(
UserMapper userMapper
)
可以直接注入。
一百五十九、Mapper 不是普通实现类
接口:
public interface UserMapper {
}
真正注入的是:
MyBatis 创建的代理对象
一百六十、Spring 和 MyBatis 怎么结合
大致:
Spring IOC
管理 Mapper Proxy
↓
Mapper Proxy
使用 SqlSessionTemplate
↓
SqlSessionTemplate
参与 Spring Transaction
↓
JDBC Connection
一百六十一、SqlSessionTemplate
Spring-MyBatis 集成中的重要类。
它:
线程安全地适配 SqlSession
并和:
Spring 事务
集成。
一百六十二、为什么 Spring 项目不自己保存 SqlSession
因为:
SqlSessionTemplate
+
Spring Transaction
已经负责上下文管理。
一百六十三、事务 Bean 怎么自动出现
SpringBoot / Spring Framework 根据:
DataSource
事务管理器
@EnableTransactionManagement 等机制
建立:
事务 AOP
于是:
@Transactional
能够工作。
一百六十四、AOP 代理什么时候创建
Bean 生命周期中:
BeanPostProcessor
会判断某个 Bean:
是否需要代理
例如发现:
@Transactional
可能创建:
Proxy
一百六十五、最终注入的是谁
可能不是:
原始 UserService
而是:
UserService Proxy
一百六十六、这解释 self-invocation
外部:
Controller
↓
Proxy
↓
Transactional
↓
Target
内部:
Target.this.method()
直接:
Target
绕过:
Proxy
一百六十七、Spring 事件机制
ApplicationContext 还支持:
ApplicationEvent
和:
@EventListener
一百六十八、事件适合什么
例如:
用户注册成功
↓
发布 UserRegisteredEvent
监听器:
发欢迎邮件
记录统计
一百六十九、事件的价值
让主业务:
减少直接依赖
一百七十、同步事件
默认很多事件监听:
同步执行
也就是说:
监听器慢
会拖慢主线程
一百七十一、异步事件
可以结合:
@Async
但要注意:
线程切换
事务边界
一百七十二、ApplicationRunner
SpringBoot 启动完成后可以:
@Component
public class InitRunner
implements ApplicationRunner {
@Override
public void run(
ApplicationArguments args
) {
System.out.println(
"应用启动完成"
);
}
}
一百七十三、CommandLineRunner
类似:
@Component
public class InitRunner
implements CommandLineRunner {
@Override
public void run(
String... args
) {
}
}
一百七十四、Runner 适合什么
例如:
启动初始化
预热缓存
检查配置
但不要:
执行很慢导致应用迟迟不 ready
一百七十五、SpringBoot 启动事件
启动过程中有:
多种 ApplicationEvent
例如:
starting
environment prepared
context prepared
started
ready
具体事件类随版本体系存在。
当前:
理解生命周期思想
即可。
一百七十六、为什么启动失败会打印 Failure Analysis
SpringBoot 对部分常见启动异常:
做了 FailureAnalyzer
帮助输出:
Description
Action
比纯堆栈更友好。
一百七十七、配置元数据和 IDE 提示
Starter / ConfigurationProperties 可以提供:
配置元数据
IDEA 就能:
自动补全 application.yml 属性
一百七十八、SpringBoot DevTools
开发阶段可以使用:
spring-boot-devtools
提供:
自动重启
开发便利功能
生产环境:
通常不使用
一百七十九、Actuator 简单预览
SpringBoot Actuator:
应用监控和管理端点
例如:
health
metrics
info
企业部署很常见。
一百八十、为什么底层原理和 Actuator 有关系
SpringBoot 自动配置不仅负责:
业务基础设施
还可以按 Starter:
自动装配管理能力
一百八十一、自己写一个最小 Starter 的思路
以后可以:
my-sms-spring-boot-starter
里面:
1. SmsProperties
2. SmsService
3. SmsAutoConfiguration
4. AutoConfiguration.imports
使用者只需:
<dependency>
...
</dependency>
和:
sms:
app-key: xxx
一百八十二、自定义 Starter 的价值
把公司内部公共功能:
短信
OSS
日志
鉴权 SDK
第三方支付
封装成:
一键依赖
一百八十三、SpringBoot 的“开箱即用”到底来自哪里
不是一个点。
而是组合:
Starter
+
AutoConfiguration
+
Conditional
+
ConfigurationProperties
+
Embedded Server
+
Spring IOC
一百八十四、底层原理总流程
pom.xml Starter
↓
依赖进入 classpath
↓
main()
↓
SpringApplication.run()
↓
Environment
↓
ApplicationContext
↓
解析 @SpringBootApplication
↓
ComponentScan
↓
EnableAutoConfiguration
↓
条件装配
↓
注册 BeanDefinition
↓
BeanFactoryPostProcessor
↓
BeanPostProcessor
↓
创建 Bean
↓
依赖注入
↓
AOP Proxy
↓
启动内嵌 Tomcat
↓
注册 DispatcherServlet
↓
映射 Controller
↓
Application Ready
一百八十五、SpringBoot 启动失败排查
遇到:
Application failed to start
按顺序看:
最底部 Caused by
Description
Action
端口
Bean 冲突
循环依赖
配置绑定
DataSource
Mapper
一百八十六、常见错误 1:Bean 找不到
例如:
NoSuchBeanDefinitionException
检查:
类有没有组件注解
包有没有被扫描
Bean 条件是否满足
配置类有没有加载
一百八十七、常见错误 2:同类型多个 Bean
NoUniqueBeanDefinitionException
解决:
@Primary
@Qualifier
重构 Bean
一百八十八、常见错误 3:Bean 名冲突
两个 Bean:
相同名字
可能导致:
冲突
现代 SpringBoot 默认通常:
不允许随意覆盖
一百八十九、常见错误 4:Controller 404
如果应用正常启动但:
接口 404
检查:
Controller 是否被扫描
@RequestMapping
context path
HTTP Method
启动类包位置
一百九十、常见错误 5:循环依赖
错误信息可能:
The dependencies of some of the beans
form a cycle
不要第一反应:
开启循环依赖
优先:
重构依赖
一百九十一、常见错误 6:端口占用
Web server failed to start.
Port 8080 was already in use.
Windows:
netstat -ano | findstr :8080
一百九十二、常见错误 7:DataSource 配置失败
例如:
Failed to configure a DataSource
检查:
JDBC Driver
url
username
password
数据库是否启动
一百九十三、常见错误 8:配置属性绑定失败
例如:
timeout: abc
Java:
Integer timeout;
无法转换:
启动失败
一百九十四、常见错误 9:自动配置没生效
例如加了配置:
却没有 Bean
检查:
starter
classpath
conditional
property
用户自定义 Bean
一百九十五、常见错误 10:事务没生效
现在从 Bean 底层可以解释:
Service 是否 Spring Bean
是否创建 Proxy
调用是否经过 Proxy
self-invocation
一百九十六、常见错误 11:AOP 不生效
同样检查:
目标对象是否 Bean
切点是否匹配
调用是否经过 Proxy
private/final/self-invocation
一百九十七、SpringBoot 底层和反射的关系
Spring 大量依赖:
反射
完成:
扫描类
读取注解
调用构造器
访问方法
参数绑定
Bean 创建
一百九十八、SpringBoot 底层和注解的关系
例如:
@Component
@Service
@Configuration
@Bean
@RequestMapping
@Transactional
本质:
元数据
框架运行时:
读取这些元数据
决定行为。
一百九十九、SpringBoot 底层和动态代理的关系
@Transactional
@Async
@Cacheable
AOP
大量依赖:
Proxy
二百、SpringBoot 底层和 JavaBean 的关系
配置绑定:
ConfigurationProperties
请求参数绑定:
DTO
都离不开:
JavaBean 属性模型
二百零一、SpringBoot 底层和 Maven 的关系
Starter:
本质是 Maven 依赖
依赖进入:
classpath
自动配置:
再根据 classpath 判断
所以:
Maven → classpath → AutoConfiguration
是连起来的。
二百零二、这就是下一章为什么是 Maven 高级
下一篇 Maven 高级会进一步解释:
依赖冲突
依赖仲裁
dependencyManagement
多模块
插件
生命周期
父子工程
私服
这些都直接影响:
SpringBoot 依赖和自动配置
二百零三、练习 1:获取 Bean
启动后:
ApplicationContext context =
SpringApplication.run(
DemoApplication.class,
args
);
System.out.println(
context.getBean(
UserService.class
)
);
观察:
Bean 是否存在
二百零四、练习 2:自己定义 Bean
@Configuration
public class AppConfig {
@Bean
public Random random() {
return new Random();
}
}
注入:
private final Random random;
二百零五、练习 3:包扫描
故意把 Controller:
移动到启动类兄弟根包之外
观察:
404
再移动回来。
二百零六、练习 4:两个同类型 Bean
定义:
AliPayService
WechatPayService
都实现:
PaymentService
注入接口,
观察:
NoUniqueBeanDefinitionException
再用:
@Primary / @Qualifier
解决。
二百零七、练习 5:Bean 生命周期
@PostConstruct
public void init() {
System.out.println(
"init"
);
}
@PreDestroy
public void destroy() {
System.out.println(
"destroy"
);
}
观察:
启动和关闭日志
二百零八、练习 6:ConditionalOnProperty 思想
自己写:
feature.demo.enabled
值为:
true
才创建 Bean。
二百零九、练习 7:ConfigurationProperties
application.yml:
app:
name: rbac-system
timeout: 5000
绑定:
AppProperties
二百一十、练习 8:debug 自动配置
配置:
debug=true
启动项目。
找到:
CONDITIONS EVALUATION REPORT
观察:
Positive matches
Negative matches
二百一十一、练习 9:查看 AOP 代理类
在 Controller 打印:
System.out.println(
userService.getClass()
);
如果 UserService 有:
@Transactional / AOP
观察实际 class。
二百一十二、练习 10:self-invocation
再次做:
this.transactionMethod()
结合:
Proxy
解释为什么事务不生效。
二百一十三、练习 11:内嵌 Tomcat
运行 SpringBoot:
不启动外部 Tomcat
观察日志:
Tomcat started on port ...
理解:
Tomcat 已嵌入项目
二百一十四、练习 12:修改端口
application.yml:
server:
port: 8081
观察:
Environment 配置
如何影响自动配置 Bean
二百一十五、练习 13:Runner
创建:
ApplicationRunner
启动时打印:
初始化完成
二百一十六、必须掌握 IOC
IOC
ApplicationContext
Bean
BeanDefinition
BeanFactory
二百一十七、必须掌握 DI
Constructor Injection
@Autowired
@Primary
@Qualifier
二百一十八、必须掌握组件扫描
@Component
@Service
@Repository
@Controller
@RestController
@Configuration
@ComponentScan
二百一十九、必须掌握 SpringBoot 核心
@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
二百二十、必须掌握自动配置
AutoConfiguration
Conditional
ConditionalOnClass
ConditionalOnMissingBean
ConditionalOnProperty
二百二十一、必须掌握 Starter
Starter
负责依赖组合
AutoConfiguration
负责配置 Bean
二百二十二、必须掌握 Bean 生命周期
实例化
依赖注入
Aware
BeanPostProcessor
初始化
AOP Proxy
销毁
二百二十三、必须掌握启动流程
SpringApplication.run
Environment
ApplicationContext
BeanDefinition
refresh
BeanPostProcessor
Embedded Tomcat
DispatcherServlet
二百二十四、必须掌握 Spring MVC 底层链路
Tomcat
DispatcherServlet
HandlerMapping
HandlerMethod
HandlerAdapter
ArgumentResolver
HttpMessageConverter
Controller
二百二十五、面试题 1
问:
@SpringBootApplication 是什么?
答:
它是 SpringBoot 主启动类常用的组合注解。
核心可以理解为组合了:
@SpringBootConfiguration、
@EnableAutoConfiguration、
@ComponentScan。
分别负责配置类语义、自动配置和组件扫描。
二百二十六、面试题 2
问:
Spring IOC 是什么?
答:
IOC 是控制反转。
传统代码由程序员自己 new 对象,
Spring 中对象的创建、依赖关系和生命周期
交给 IOC 容器管理。
Spring 管理的对象称为 Bean。
二百二十七、面试题 3
问:
IOC 和 DI 有什么区别?
答:
IOC 是整体思想:
对象控制权交给容器。
DI 是实现 IOC 的核心方式:
容器把一个 Bean 所需要的依赖注入进去。
二百二十八、面试题 4
问:
为什么推荐构造器注入?
答:
构造器注入可以让依赖更加明确,
字段可以声明为 final,
方便单元测试,
并保证对象创建完成后依赖已经完整。
二百二十九、面试题 5
问:
SpringBoot 自动配置原理是什么?
答:
SpringBoot 会加载一批自动配置候选类,
然后根据 classpath、已有 Bean、
配置属性、应用类型等条件进行判断。
满足条件的自动配置才会注册 Bean。
因此自动配置本质仍然是
@Configuration/@Bean + 条件装配。
二百三十、面试题 6
问:
Starter 和 AutoConfiguration 有什么区别?
答:
Starter 主要解决依赖组合问题,
把一组相关 jar 一次性引入。
AutoConfiguration 主要解决 Bean 配置问题,
根据条件自动创建框架需要的 Bean。
两者共同构成 SpringBoot 的开箱即用体验。
二百三十一、面试题 7
问:
@ConditionalOnMissingBean 有什么作用?
答:
只有当 Spring 容器中不存在指定 Bean 时,
自动配置才创建默认 Bean。
这使得 SpringBoot 可以提供默认配置,
同时允许用户自己定义 Bean 覆盖默认行为。
二百三十二、面试题 8
问:
为什么启动类推荐放根包?
答:
@SpringBootApplication 默认会从启动类所在包
向下扫描子包。
如果 Controller 或 Service 放在扫描范围外,
它们不会注册为 Bean,
可能导致注入失败或接口 404。
二百三十三、面试题 9
问:
Spring Bean 默认是单例吗?
答:
默认是 singleton。
同一个 ApplicationContext 中,
通常只有一个对应 Bean 实例。
因此 Controller 和 Service 不应该用普通成员变量
保存某个请求的临时状态。
二百三十四、面试题 10
问:
BeanPostProcessor 是什么?
答:
BeanPostProcessor 是 Bean 后置处理扩展点。
Spring 可以在 Bean 初始化前后统一处理对象。
Spring AOP 代理的创建就和 Bean 后置处理机制
有非常密切的关系。
二百三十五、面试题 11
问:
SpringApplication.run() 做了什么?
答:
它会准备 Environment,
创建 ApplicationContext,
加载和解析配置类,
注册 BeanDefinition,
刷新 Spring 容器,
创建 Bean,
执行自动配置,
启动内嵌 Web Server,
并最终完成应用启动。
二百三十六、面试题 12
问:
SpringBoot 为什么能直接启动 Tomcat?
答:
spring-boot-starter-web 会引入 Web 和内嵌 Tomcat
所需的相关依赖。
SpringBoot 自动配置检测到 Servlet Web 环境后,
会创建内嵌 WebServerFactory,
进而启动 Tomcat。
因此可以直接运行 main 方法启动 Web 应用。
二百三十七、面试题 13
问:
DispatcherServlet 是什么?
答:
DispatcherServlet 是 Spring MVC 的前端控制器。
大部分 MVC 请求先进入 DispatcherServlet,
再通过 HandlerMapping 找到 Controller 方法,
通过 HandlerAdapter 调用,
最后完成参数解析和响应转换。
二百三十八、面试题 14
问:
@RequestBody 为什么能自动把 JSON 转成对象?
答:
Spring MVC 会通过参数解析器识别 @RequestBody,
然后选择合适的 HttpMessageConverter。
在常见 SpringBoot Web 项目中,
Jackson MessageConverter 会把 JSON
反序列化成 Java 对象。
二百三十九、面试题 15
问:
为什么 @Transactional 自调用会失效?
答:
因为事务通常依赖 Spring AOP 代理。
外部调用经过 Proxy,
事务拦截器才有机会执行。
this.method() 是目标对象内部直接调用,
绕过 Proxy,
因此事务增强可能不会执行。
二百四十、SpringBoot 底层知识树
SpringBoot
│
├─ Spring Core
│ ├─ IOC
│ ├─ DI
│ ├─ BeanFactory
│ ├─ ApplicationContext
│ ├─ BeanDefinition
│ └─ Bean Lifecycle
│
├─ Component
│ ├─ Component
│ ├─ Service
│ ├─ Repository
│ ├─ Controller
│ ├─ Configuration
│ └─ Bean
│
├─ Boot
│ ├─ SpringBootApplication
│ ├─ EnableAutoConfiguration
│ ├─ ComponentScan
│ ├─ Starter
│ ├─ AutoConfiguration
│ └─ Conditional
│
├─ Config
│ ├─ Environment
│ ├─ application.yml
│ ├─ Value
│ └─ ConfigurationProperties
│
├─ Extension
│ ├─ BeanFactoryPostProcessor
│ ├─ BeanPostProcessor
│ ├─ AOP Proxy
│ └─ Events
│
├─ Startup
│ ├─ SpringApplication
│ ├─ Environment
│ ├─ Context
│ ├─ refresh
│ ├─ Bean Init
│ └─ Runner
│
└─ Web
├─ Embedded Tomcat
├─ DispatcherServlet
├─ HandlerMapping
├─ HandlerAdapter
├─ ArgumentResolver
└─ HttpMessageConverter
二百四十一、最重要的启动总图
main()
↓
SpringApplication.run()
↓
准备 Environment
↓
创建 ApplicationContext
↓
加载 @SpringBootApplication
↓
ComponentScan
↓
EnableAutoConfiguration
↓
读取 AutoConfiguration
↓
Conditional 判断
↓
注册 BeanDefinition
↓
BeanFactoryPostProcessor
↓
BeanPostProcessor
↓
创建 Bean
↓
依赖注入
↓
初始化
↓
AOP Proxy
↓
启动内嵌 Tomcat
↓
创建/注册 DispatcherServlet
↓
扫描 RequestMapping
↓
运行 Runner
↓
Application Ready
二百四十二、本章总结
SpringBoot 底层最核心不是:
背源码类名
而是理解:
SpringBoot 如何把大量配置自动化
IOC:
对象交给容器管理
DI:
容器自动注入依赖
Bean:
Spring 管理的对象
@SpringBootApplication:
主配置
+
组件扫描
+
自动配置
自动配置:
AutoConfiguration
+
Conditional
Starter:
负责快速引入一组依赖
Bean 生命周期:
BeanDefinition
↓
实例化
↓
依赖注入
↓
初始化
↓
BeanPostProcessor
↓
AOP Proxy
↓
使用
↓
销毁
SpringBoot 启动:
SpringApplication.run()
↓
Environment
↓
ApplicationContext
↓
refresh
↓
Bean
↓
Embedded Tomcat
↓
DispatcherServlet
Spring MVC 请求:
Tomcat
↓
DispatcherServlet
↓
HandlerMapping
↓
HandlerAdapter
↓
ArgumentResolver
↓
Controller
↓
HttpMessageConverter
↓
JSON
现在再回头看这些注解:
@Service
@RestController
@Transactional
@RequestBody
就不再是:
“SpringBoot 很神奇,所以自动能用”
而是能够解释:
组件扫描注册 Bean
IOC 管理 Bean
DI 注入 Bean
BeanPostProcessor 创建 Proxy
自动配置提供框架 Bean
DispatcherServlet 负责 Web 请求
到这里,你对 SpringBoot 已经从:
会用
进入:
能解释基本底层机制
按照课程表,下一篇继续:
Maven 高级
会进一步系统学习:
依赖传递
依赖冲突
依赖仲裁
dependencyManagement
父子工程
多模块
生命周期
插件
profile
私服
SpringBoot Maven 构建