Spring Boot 完整入门教程:从入门到精通
写给读者的话:
这不是一篇"Hello World"式的浅尝辄止的教程。
这篇教程会带你从最基础的概念开始,一路深入到 Spring Boot 的核心原理、各种技术选型对比、前沿技术趋势,以及企业级开发中的硬核实践。
你会看到大量的对比分析(技术选型怎么选)、原理剖析(为什么是这样)、实用技巧(工作中直接能用)和前沿趋势(未来的方向)。
适合谁读:
- 有 Java 基础,想系统学习 Spring Boot 的同学
- 已经在用 Spring Boot,但想深入理解原理的开发者
- 想了解 Spring 生态全貌、做技术选型的架构师
- 准备面试,需要系统性梳理知识的求职者
怎么读:
- 新手:按顺序读,先建立整体认知
- 有基础:挑感兴趣的章节深入看
- 面试前:重点看对比、原理、最佳实践部分
准备好了吗?我们开始。
目录
- 第 1 章:为什么是 Spring Boot
- 第 2 章:快速入门
- 第 3 章:核心原理深度剖析
- 第 4 章:Web 开发全景
- 第 5 章:数据访问技术选型与实战
- 第 6 章:配置体系详解
- 第 7 章:安全框架对比与实战
- 第 8 章:缓存技术深度对比
- 第 9 章:消息队列选型与实战
- 第 10 章:任务调度方案对比
- 第 11 章:日志体系与最佳实践
- 第 12 章:监控与可观测性
- 第 13 章:测试体系
- 第 14 章:部署与运维
- 第 15 章:前沿技术与未来趋势
- 第 16 章:性能优化实战
- 第 17 章:架构设计与最佳实践
- 第 18 章:学习路线与资源推荐
第 1 章:为什么是 Spring Boot
1.1 Java Web 开发的进化史
要理解 Spring Boot 为什么火,得先看看 Java Web 开发是怎么一步步进化的。
第一阶段:Servlet + JSP(远古时代)
- 写 Servlet,继承 HttpServlet,重写 doGet/doPost
- 配置 web.xml,每个 Servlet 配一个
- JSP 写页面,里面嵌 Java 代码
- 部署到 Tomcat,启动慢,配置多
- 问题:配置繁琐、开发效率低、项目结构混乱
第二阶段:SSH 框架时代(Struts + Spring + Hibernate)
- Struts 处理 Web 层
- Spring 管理 Bean,IOC/AOP
- Hibernate 操作数据库
- 问题:配置还是很多,Struts 漏洞多,Hibernate 不够灵活
第三阶段:SSM 时代(Spring + SpringMVC + MyBatis)
- SpringMVC 替代 Struts
- MyBatis 替代 Hibernate,SQL 更灵活
- Spring 贯穿始终
- 问题:还是要写一堆 XML 配置,依赖管理麻烦,整合各种框架费时费力
第四阶段:Spring Boot 时代(现在)
- 约定优于配置,开箱即用
- 起步依赖,不用自己找版本
- 内嵌容器,不用装 Tomcat
- 自动配置,不用写一堆 XML
- starter 生态,整合各种技术就加个依赖
- 问题:入门容易精通难,出了问题不好排查
第五阶段:Spring Cloud + Spring Boot(微服务时代)
- 基于 Spring Boot 的微服务全家桶
- 服务注册发现、配置中心、网关、熔断...
- 问题:复杂度高,运维成本高
简单说:Spring Boot 就是把以前 Spring 开发需要的各种配置、整合、部署工作都给你简化了。 以前搭一个 Spring 项目要一天,现在 5 分钟搞定。
1.2 Spring Boot 到底是什么
官方定义:Spring Boot 是 Spring 家族的一个子项目,目的是简化 Spring 应用的初始搭建和开发过程。
人话翻译:
- 不是新框架,是 Spring 的"加速器"
- 把 Spring 全家桶整合好了,拿来就能用
- 自动配置了很多东西,不用你手动配
- 内嵌 Tomcat/Jetty/Undertow,直接 JAR 包运行
- 提供 starter 依赖,加一个依赖就整合一种技术
核心特性:
| 特性 | 说明 |
|---|---|
| 起步依赖 | Starter 依赖,自动管理版本 |
| 自动配置 | 自动配置 Spring 应用 |
| 内嵌容器 | 内嵌 Tomcat,直接 java -jar 运行 |
| Actuator | 生产级监控和管理 |
| 无代码生成 | 不用生成代码,纯配置 |
| 无 XML 配置 | 不用写 XML,全注解 |
1.3 Spring Boot vs 传统 Spring 开发
| 维度 | 传统 Spring 开发 | Spring Boot |
|---|---|---|
| 依赖管理 | 手动找依赖,版本冲突常踩坑 | Starter 起步依赖,自动管理版本 |
| 配置方式 | 大量 XML 配置 | 自动配置 + 少量注解 |
| 部署方式 | 打 WAR 包,部署到外部 Tomcat | 打 JAR 包,内嵌容器直接运行 |
| 开发效率 | 搭项目半天起步 | 5 分钟搞定一个项目 |
| 学习曲线 | 陡峭,要学各种配置 | 平缓,开箱即用 |
| 灵活性 | 高,什么都能自己配 | 中,默认配置够用,要改也能改 |
| 问题排查 | 配置都在明面上 | 自动配置是黑盒,出问题不好找 |
Spring Boot 不是替代 Spring,而是在 Spring 之上做了一层封装。 你可以理解为:Spring 是零件,Spring Boot 是组装好的整机。
1.4 Spring Boot 能做什么
Spring Boot 几乎能做所有 Java 后端开发的事情:
| 领域 | 说明 |
|---|---|
| Web 应用 | 网站、后台管理系统、RESTful API |
| 微服务 | 配合 Spring Cloud 做微服务 |
| 企业应用 | OA、CRM、ERP 等管理系统 |
| 数据处理 | 批处理、ETL、数据清洗 |
| 移动端后端 | App、小程序的 API 服务 |
| 物联网 | IoT 设备管理、数据采集 |
| 游戏后端 | 游戏服务器、匹配系统 |
简单说:只要是 Java 后端,Spring Boot 基本都能做。 而且是现在 Java 后端的绝对主流。
1.5 版本选择指南
Spring Boot 版本很多,怎么选?
| 版本 | JDK 要求 | Spring 版本 | 状态 | 推荐 |
|---|---|---|---|---|
| 2.7.x | 8 ~ 18 | 5.3.x | 开源维护结束 | 老项目维护用 |
| 3.0.x | 17 ~ 19 | 6.0.x | 已停止维护 | 不推荐 |
| 3.1.x | 17 ~ 20 | 6.0.x | 已停止维护 | 不推荐 |
| 3.2.x | 17 ~ 21 | 6.1.x | 开源维护中 | 推荐学习用 |
| 3.3.x | 17 ~ 22 | 6.1.x | 最新稳定版 | 新项目首选 |
选择建议:
- 学习/新项目:选最新稳定版(3.3.x),用 JDK 17+
- 老项目维护:看项目用的什么版本,保持一致
- 企业选型:选 LTS(长期支持)版本,更稳定
注意:Spring Boot 3.x 要求 JDK 17+,而且用的是 Jakarta EE 命名空间(javax → jakarta)。 从 2.x 升级到 3.x 不是无缝的,有一些迁移工作。
第 2 章:快速入门
2.1 环境准备
必备工具:
| 工具 | 推荐版本 | 说明 |
|---|---|---|
| JDK | 17+ | Spring Boot 3.x 要求 17+ |
| Maven | 3.8+ | 构建工具 |
| IDE | IDEA 旗舰版 | 开发效率神器 |
| 数据库 | MySQL 8.0+ | 最常用的关系型数据库 |
| Redis | 7.0+ | 缓存 |
| Postman / Apifox | 最新版 | 接口测试 |
可选工具:
- Docker:容器化部署
- Git:版本控制
- Navicat / DBeaver:数据库可视化
- JMeter:压测
2.2 创建项目的三种方式
方式一:Spring Initializr(官方推荐)
- 打开 https://start.spring.io/
- 选择项目信息(Group、Artifact、Java 版本)
- 选择依赖(Web、MyBatis、Redis...)
- 下载 ZIP,解压,用 IDEA 打开
方式二:IDEA 内置(最方便)
- IDEA → New Project → Spring Initializr
- 填信息,选依赖
- 一键创建,自动下载依赖
方式三:Maven 手动创建(了解原理)
- 创建 Maven 项目
- pom.xml 加 parent 和 starter 依赖
- 写启动类
- 写配置文件
新手推荐用 IDEA 内置的方式,最方便。 手动创建的方式了解一下就行,知道原理。
2.3 第一个 Spring Boot 项目
pom.xml 核心依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.2</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>启动类:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}第一个 Controller:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello Spring Boot!";
}
}启动,访问:http://localhost:8080/hello
就这么简单!一个 Web 项目就跑起来了。 以前要配 Tomcat、配 Spring MVC、写 web.xml,现在什么都不用配。
2.4 项目结构解读
src/main/java
└── com/example/demo
├── DemoApplication.java # 启动类(项目入口)
├── controller/ # 控制层(接收请求)
├── service/ # 业务层(业务逻辑)
├── mapper/ # 数据访问层(操作数据库)
├── entity/ # 实体类(数据库表对应)
├── dto/ # 数据传输对象(前后端传参)
├── config/ # 配置类
├── common/ # 公共类
├── exception/ # 异常处理
└── utils/ # 工具类
src/main/resources
├── application.yml # 配置文件
├── static/ # 静态资源(js、css、图片)
└── templates/ # 模板页面(Thymeleaf 等)
src/test/java # 测试代码三层架构:
- Controller 层:接收请求,参数校验,返回结果
- Service 层:业务逻辑,事务控制
- Mapper/DAO 层:数据访问,操作数据库
这是企业项目最经典的分层结构,一定要掌握。 分层的目的是解耦,各层各司其职,好维护。
2.5 开发必备插件
IDEA 插件推荐:
| 插件 | 作用 |
|---|---|
| Lombok | 简化实体类,不用写 getter/setter |
| MyBatisX | MyBatis 代码生成,Mapper 跳转 |
| Rainbow Brackets | 彩虹括号,代码更清晰 |
| Translation | 翻译插件,看英文文档方便 |
| Json Parser | JSON 格式化 |
| RestfulToolkit | 接口搜索、测试 |
| Maven Helper | Maven 依赖分析、冲突解决 |
好的工具能让开发效率翻倍。 不要觉得用插件是投机取巧,工具就是用来提高效率的。
第 3 章:核心原理深度剖析
3.1 @SpringBootApplication 注解揭秘
很多人以为 @SpringBootApplication 是一个普通注解,其实它是一个组合注解。
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
// ...
}三个核心注解:
| 注解 | 作用 |
|---|---|
@SpringBootConfiguration | 标记这是一个配置类,其实就是 @Configuration |
@EnableAutoConfiguration | 开启自动配置,Spring Boot 的核心 |
@ComponentScan | 组件扫描,自动扫描并注册 Bean |
就这一个注解,干了三件大事。 这就是 Spring Boot 开箱即用的秘密之一。
3.2 自动配置原理(硬核)
自动配置是 Spring Boot 最核心的功能,也是最神秘的。
自动配置到底是怎么工作的?
第一步:@EnableAutoConfiguration
这个注解导入了一个选择器:AutoConfigurationImportSelector
第二步:加载自动配置类
AutoConfigurationImportSelector 会去 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件里读取所有自动配置类的全限定名。
Spring Boot 内置了 100+ 个自动配置类,比如:
DataSourceAutoConfiguration(数据源自动配置)WebMvcAutoConfiguration(Web MVC 自动配置)RedisAutoConfiguration(Redis 自动配置)- ...
第三步:条件装配
不是所有自动配置类都会生效,每个配置类上都有条件注解:
@Configuration
@ConditionalOnClass(DataSource.class) // 类路径下有 DataSource 才生效
@ConditionalOnMissingBean(DataSource.class) // 容器中没有 DataSource 才生效
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
// ...
}常用条件注解:
| 注解 | 作用 |
|---|---|
@ConditionalOnClass | 类路径下有指定类才生效 |
@ConditionalOnMissingClass | 类路径下没有指定类才生效 |
@ConditionalOnBean | 容器中有指定 Bean 才生效 |
@ConditionalOnMissingBean | 容器中没有指定 Bean 才生效 |
@ConditionalOnProperty | 配置文件中有指定属性才生效 |
@ConditionalOnWebApplication | Web 应用才生效 |
@ConditionalOnNotWebApplication | 不是 Web 应用才生效 |
第四步:绑定配置属性
自动配置类会绑定配置文件中的属性,通过 @ConfigurationProperties。
比如数据源配置:
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver这些属性会绑定到 DataSourceProperties 类上,然后自动配置类用这些属性来创建 DataSource Bean。
总结一下自动配置流程:
启动 → @EnableAutoConfiguration → 加载所有自动配置类
→ 条件装配(满足条件的才生效)→ 绑定配置属性 → 创建 Bean → 放入容器自动配置的本质:约定 + 条件装配 + 属性绑定。 约定好了默认配置,你不配置就用默认的,你配置了就用你的。
怎么看哪些自动配置生效了?
启动时加参数:--debug,或者配置:
debug: true启动日志里会打印:
- Positive matches:生效的自动配置
- Negative matches:没生效的自动配置
- Exclusions:排除的自动配置
遇到自动配置的问题,先开 debug 看看哪些生效了,哪些没生效,为什么没生效。 这是排查自动配置问题的利器。
3.3 起步依赖原理
起步依赖(Starter)是什么?
简单说:就是一个 pom.xml,把某个场景需要的依赖都打包好了。
比如 spring-boot-starter-web 里面包含了:
- spring-web
- spring-webmvc
- jackson-databind
- spring-boot-starter-tomcat
- ...
你只需要加一个 starter-web 依赖,这些就都有了,不用自己一个个加,也不用担心版本冲突。
版本管理:
版本是怎么管理的?靠 parent。
spring-boot-starter-parent 里面定义了几百个依赖的版本号,你加依赖不用写版本号,直接用 parent 里的。
这样做的好处:
- 不用自己找版本
- 不会有版本冲突
- 升级 Spring Boot 版本,所有依赖版本一起升
Starter 不是什么黑科技,就是 Maven 的依赖传递 + 版本管理。 但这个设计真的很巧妙,大大降低了依赖管理的复杂度。
3.4 内嵌容器原理
Spring Boot 为什么能直接 java -jar 运行?
因为它把 Tomcat 内嵌进去了。
原理:
- 启动时,Spring Boot 检查是不是 Web 应用
- 如果是,就创建一个内嵌的 Tomcat/Jetty/Undertow
- 把 DispatcherServlet、Filter 等注册到内嵌容器
- 启动容器
怎么切换容器:
<!-- 排除 Tomcat,换成 Jetty -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>三种容器对比:
| 容器 | 特点 | 适用场景 |
|---|---|---|
| Tomcat | 最常用,稳定,生态好 | 大多数场景 |
| Jetty | 轻量,灵活,内存占用小 | 内存敏感、嵌入式场景 |
| Undertow | 高性能,非阻塞 IO,轻量 | 高并发、性能要求高 |
一般项目用 Tomcat 就行,稳定可靠。 追求性能或者内存敏感的场景可以考虑 Undertow 或 Jetty。
3.5 Spring Boot 启动流程
Spring Boot 启动过程是怎样的?简单梳理一下:
1. 执行 main 方法,调用 SpringApplication.run()
2. 创建 SpringApplication 对象
3. 准备环境(加载配置文件、profile 等)
4. 打印 Banner
5. 创建 ApplicationContext(容器)
6. 注册 Bean 定义
7. 执行各种初始化器
8. 刷新容器(Bean 实例化、自动配置等)
9. 执行 CommandLineRunner、ApplicationRunner
10. 启动完成,打印启动时间启动过程中的扩展点:
| 扩展点 | 作用 |
|---|---|
ApplicationContextInitializer | 容器刷新前执行 |
BeanFactoryPostProcessor | Bean 定义加载后,实例化前 |
BeanPostProcessor | Bean 实例化前后 |
CommandLineRunner | 启动完成后执行 |
ApplicationRunner | 启动完成后执行(参数更丰富) |
@PostConstruct | Bean 初始化后执行 |
InitializingBean | Bean 属性设置完后执行 |
了解启动流程和扩展点,能帮你更好地定制 Spring Boot 的行为。 比如启动时做一些初始化工作,就可以用 CommandLineRunner。
第 4 章:Web 开发全景
4.1 Spring MVC 自动配置
Spring Boot 自动配置了 Spring MVC,开箱即用。
自动配置的内容:
DispatcherServlet:前端控制器HandlerMapping:处理器映射HandlerAdapter:处理器适配器ViewResolver:视图解析器Converter:类型转换器Validator:数据校验- 静态资源映射
- 文件上传支持
- 异常处理
- ...
什么都不用配,直接就能用。
4.2 RESTful API 设计
RESTful 是现在最流行的 API 设计风格。
核心原则:
- 资源用 URI 表示
- 用 HTTP 方法表示操作
- 用 HTTP 状态码表示结果
- 无状态
HTTP 方法对应 CRUD:
| 方法 | 操作 | 幂等 | 示例 |
|---|---|---|---|
| GET | 查询 | 是 | GET /users(查列表)、GET /users/1(查单个) |
| POST | 新增 | 否 | POST /users(新增用户) |
| PUT | 修改(全量) | 是 | PUT /users/1(修改用户 1) |
| PATCH | 修改(部分) | 否 | PATCH /users/1(部分修改用户 1) |
| DELETE | 删除 | 是 | DELETE /users/1(删除用户 1) |
HTTP 状态码:
| 状态码 | 含义 | 场景 |
|---|---|---|
| 200 OK | 成功 | 查询、修改成功 |
| 201 Created | 创建成功 | 新增成功 |
| 204 No Content | 无内容 | 删除成功 |
| 400 Bad Request | 请求参数错误 | 参数校验失败 |
| 401 Unauthorized | 未认证 | 没登录 |
| 403 Forbidden | 无权限 | 登录了但没权限 |
| 404 Not Found | 资源不存在 | 路径错了或资源不存在 |
| 409 Conflict | 冲突 | 重复创建 |
| 500 Internal Server Error | 服务器内部错误 | 代码异常 |
RESTful 是一种风格,不是强制标准。 实际项目中不用教条,怎么方便怎么来。 但基本的规范还是要遵守的,前后端协作更顺畅。
4.3 参数绑定大全
Spring MVC 支持多种参数绑定方式:
1. 路径参数
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
// ...
}2. 请求参数(Query String)
@GetMapping("/users")
public List<User> listUsers(@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
// ...
}3. 请求体(JSON)
@PostMapping("/users")
public User createUser(@RequestBody @Valid UserDTO userDTO) {
// ...
}4. 表单参数
@PostMapping("/login")
public String login(@RequestParam String username,
@RequestParam String password) {
// ...
}5. 请求头
@GetMapping("/test")
public String test(@RequestHeader("Authorization") String token) {
// ...
}6. Cookie
@GetMapping("/test")
public String test(@CookieValue("JSESSIONID") String sessionId) {
// ...
}7. Model/Map
@GetMapping("/test")
public String test(Model model) {
model.addAttribute("name", "张三");
return "view";
}8. 原生 API
@GetMapping("/test")
public void test(HttpServletRequest request,
HttpServletResponse response) {
// ...
}参数绑定方式很多,根据场景选择。 最常用的是 @PathVariable、@RequestParam、@RequestBody。
4.4 数据校验(JSR-303)
参数校验是必须的,不能相信前端传过来的任何数据。
常用校验注解:
| 注解 | 作用 |
|---|---|
@NotNull | 不能为 null |
@NotEmpty | 不能为 null 且不能为空(字符串、集合) |
@NotBlank | 不能为 null 且不能全是空格 |
@Min | 最小值 |
@Max | 最大值 |
@Size | 大小范围 |
@Email | 邮箱格式 |
@Pattern | 正则匹配 |
@Valid | 级联校验 |
使用示例:
@Data
public class UserDTO {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度 3-20")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 20, message = "密码长度 6-20")
private String password;
@Email(message = "邮箱格式不正确")
private String email;
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
}Controller 中使用:
@PostMapping("/users")
public R<User> createUser(@RequestBody @Valid UserDTO userDTO,
BindingResult result) {
if (result.hasErrors()) {
// 校验失败,返回错误信息
return R.error(result.getFieldError().getDefaultMessage());
}
// ...
}全局异常处理校验异常:
不用每个方法都写 BindingResult,全局统一处理:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public R<String> handleValidationException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldError().getDefaultMessage();
return R.error(message);
}
}校验一定要做,而且后端校验不能省。 前端校验是为了用户体验,后端校验是为了安全。 永远不要相信前端传过来的数据。
4.5 统一返回结果
所有接口返回统一格式,前端好处理。
@Data
public class R<T> {
private int code; // 状态码
private String message; // 提示信息
private T data; // 数据
private long timestamp; // 时间戳
public static <T> R<T> success(T data) {
R<T> r = new R<>();
r.setCode(200);
r.setMessage("success");
r.setData(data);
r.setTimestamp(System.currentTimeMillis());
return r;
}
public static <T> R<T> error(int code, String message) {
R<T> r = new R<>();
r.setCode(code);
r.setMessage(message);
r.setTimestamp(System.currentTimeMillis());
return r;
}
}统一返回结果是企业项目的标配。 前端不用每个接口写一套解析逻辑,统一处理就行。
4.6 全局异常处理
所有异常统一处理,不用每个方法都 try-catch。
@RestControllerAdvice
public class GlobalExceptionHandler {
// 业务异常
@ExceptionHandler(BusinessException.class)
public R<Void> handleBusinessException(BusinessException e) {
return R.error(e.getCode(), e.getMessage());
}
// 参数校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public R<Void> handleValidationException(MethodArgumentNotValidException e) {
String msg = e.getBindingResult().getFieldError().getDefaultMessage();
return R.error(400, msg);
}
// 其他异常
@ExceptionHandler(Exception.class)
public R<Void> handleException(Exception e) {
log.error("系统异常", e);
return R.error(500, "系统异常,请联系管理员");
}
}全局异常处理 + 自定义业务异常 = 优雅的错误处理。 代码里遇到业务错误直接抛异常,不用层层返回。
4.7 静态资源
Spring Boot 自动配置了静态资源映射。
默认静态资源位置(优先级从高到低):
classpath:/META-INF/resources/classpath:/resources/classpath:/static/classpath:/public/
访问方式:直接访问文件名,比如 classpath:/static/img/logo.png → http://localhost:8080/img/logo.png
自定义静态资源路径:
spring:
web:
resources:
static-locations: classpath:/static/,file:/data/static/4.8 文件上传下载
文件上传:
@PostMapping("/upload")
public R<String> upload(MultipartFile file) throws IOException {
String originalFilename = file.getOriginalFilename();
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
String filename = UUID.randomUUID() + suffix;
file.transferTo(new File("/data/upload/" + filename));
return R.success(filename);
}文件下载:
@GetMapping("/download/{filename}")
public void download(@PathVariable String filename,
HttpServletResponse response) throws IOException {
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition",
"attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
Files.copy(Paths.get("/data/upload/" + filename),
response.getOutputStream());
}配置上传大小:
spring:
servlet:
multipart:
max-file-size: 10MB # 单个文件最大
max-request-size: 100MB # 整个请求最大文件上传下载是 Web 项目的常见功能。 注意:生产环境文件不要存在应用服务器上,要用对象存储(OSS、COS 等)。
4.9 跨域问题(CORS)
前后端分离项目一定会遇到跨域问题。
什么是跨域:
- 协议不同(http vs https)
- 域名不同
- 端口不同
只要有一个不同,就是跨域。
解决方案:
方式一:全局配置(推荐)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}方式二:注解方式
@CrossOrigin(origins = "*")
@RestController
public class UserController {
// ...
}方式三:过滤器方式
@Bean
public CorsFilter corsFilter() {
// ...
}推荐用全局配置的方式,一次配置所有接口都生效。 注意:生产环境不要允许所有来源,要配置具体的域名。
4.10 拦截器
拦截器可以在请求处理前后做一些事情,比如登录校验、权限检查、日志记录。
定义拦截器:
@Component
public class LoginInterceptor implements HandlerInterceptor {
// 请求处理前
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 检查登录状态
Object user = request.getSession().getAttribute("user");
if (user == null) {
response.setStatus(401);
return false; // 拦截
}
return true; // 放行
}
// 请求处理后,视图渲染前
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
// ...
}
// 整个请求完成后
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
// ...
}
}注册拦截器:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") // 拦截所有
.excludePathPatterns( // 排除这些
"/login",
"/register",
"/public/**"
);
}
}拦截器 vs 过滤器:
| 维度 | 过滤器 Filter | 拦截器 Interceptor |
|---|---|---|
| 规范 | Servlet 规范 | Spring MVC 规范 |
| 执行时机 | 请求到达 Servlet 之前 | Controller 前后 |
| 能拿到的东西 | 只有 request/response | 能拿到 handler、ModelAndView |
| 依赖 Spring 容器 | 不依赖 | 依赖 |
| 执行顺序 | 先执行 | 后执行 |
| 数量 | 多个 | 多个 |
简单的请求过滤用 Filter,跟 Spring MVC 相关的用 Interceptor。 登录校验两种都可以,看习惯。
第 5 章:数据访问技术选型与实战
数据访问是后端开发的核心,技术方案也很多。
5.1 数据访问技术全景
Java 生态里数据访问技术很多,我们来梳理一下:
| 技术 | 类型 | 特点 | 适用场景 |
|---|---|---|---|
| JDBC | 底层 | 最原始,最灵活,代码多 | 极致性能、特殊需求 |
| JdbcTemplate | 轻量封装 | Spring 封装的 JDBC,比原生好用 | 简单项目、SQL 少 |
| MyBatis | 半 ORM | SQL 灵活,容易上手 | 大多数业务系统 |
| MyBatis-Plus | MyBatis 增强 | 单表 CRUD 不用写 SQL | 快速开发、单表多 |
| JPA / Hibernate | 全 ORM | 不用写 SQL,面向对象 | 简单 CRUD、快速原型 |
| Spring Data JPA | JPA 增强 | 方法名查询、更方便 | 同上 |
| jOOQ | 类型安全 SQL | 用 Java 写 SQL,类型安全 | 追求类型安全 |
| QueryDSL | 类型安全查询 | 跟 JPA/MyBatis 配合 | 动态查询 |
怎么选?
- 中小项目、快速开发:MyBatis-Plus(单表多,SQL 简单)
- 复杂业务、SQL 多:MyBatis(SQL 灵活可控)
- 简单 CRUD、原型开发:Spring Data JPA(开发快)
- 极致性能、特殊需求:JDBC / JdbcTemplate
- 类型安全、编译期检查:jOOQ / QueryDSL
目前国内最主流的是 MyBatis + MyBatis-Plus 组合。 国外 JPA 用的多一些。 没有最好的,只有最合适的,根据项目情况选。
5.2 MyBatis-Plus 实战(重点)
MyBatis-Plus(简称 MP)是 MyBatis 的增强工具,单表 CRUD 不用写 SQL,非常方便。
核心特性:
- 单表 CRUD 零 SQL
- 条件构造器(Wrapper)
- 分页插件
- 代码生成器
- 自动填充
- 逻辑删除
- 乐观锁
- 多租户
- ...
快速入门:
- 加依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version>
</dependency>- 实体类:
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
}- Mapper:
public interface UserMapper extends BaseMapper<User> {
}- Service:
public interface UserService extends IService<User> {
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements UserService {
}就这么简单!单表 CRUD 都有了。
常用方法:
// 新增
userMapper.insert(user);
// 根据 ID 查询
User user = userMapper.selectById(id);
// 查询列表
List<User> list = userMapper.selectList(null);
// 条件查询
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "张三")
.gt(User::getAge, 18)
.orderByDesc(User::getCreateTime);
List<User> list = userMapper.selectList(wrapper);
// 分页
Page<User> page = new Page<>(1, 10);
userMapper.selectPage(page, wrapper);
// 更新
userMapper.updateById(user);
// 删除
userMapper.deleteById(id);MyBatis-Plus 是提升开发效率的神器。 单表操作基本不用写 SQL,复杂查询再写 XML。 国内现在新项目基本都用 MP。
5.3 数据库连接池对比
连接池是数据库访问的必备组件。
| 连接池 | 特点 | 性能 | 推荐 |
|---|---|---|---|
| HikariCP | Spring Boot 默认,轻量,高性能 | 最高 | 首选 |
| Druid | 阿里开源,功能丰富,监控强 | 高 | 需要监控、SQL 防火墙用 |
| Tomcat JDBC | Tomcat 自带 | 中 | 用 Tomcat 连接池时 |
| C3P0 | 老牌,历史悠久 | 低 | 不推荐,太老了 |
| DBCP | Apache 出品 | 低 | 不推荐 |
HikariCP vs Druid:
| 维度 | HikariCP | Druid |
|---|---|---|
| 性能 | 更高 | 高 |
| 功能 | 少而精 | 丰富(监控、SQL 防火墙、加密等) |
| 配置 | 简单 | 稍复杂 |
| 社区 | Spring Boot 默认,活跃 | 国内活跃 |
| 推荐场景 | 追求性能、简单够用 | 需要监控、SQL 审计 |
一般项目用默认的 HikariCP 就行,性能最好。 如果需要 SQL 监控、慢查询分析、SQL 防火墙等功能,用 Druid。
5.4 Redis 实战
Redis 是现在后端开发的必备技能,用途太广了。
Redis 能做什么:
- 缓存(最常用)
- 分布式锁
- 计数器
- 排行榜
- 消息队列(简单场景)
- 限流
- 会话存储
- 发布订阅
- 地理位置
- 布隆过滤器
- ...
五种基本数据结构:
| 类型 | 特点 | 应用场景 |
|---|---|---|
| String | 字符串,最基础 | 缓存、计数器、分布式锁 |
| Hash | 键值对集合 | 对象缓存、购物车 |
| List | 列表,有序可重复 | 消息队列、最新列表 |
| Set | 集合,无序不重复 | 去重、交集并集差集、标签 |
| ZSet | 有序集合,不重复 | 排行榜、延时队列 |
Spring Boot 整合 Redis:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>spring:
data:
redis:
host: localhost
port: 6379
database: 0
timeout: 3000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0RedisTemplate 配置(JSON 序列化):
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(
RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// JSON 序列化器
Jackson2JsonRedisSerializer<Object> jsonSerializer =
new Jackson2JsonRedisSerializer<>(Object.class);
// key 用字符串序列化
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
// value 用 JSON 序列化
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
}Redis 内容很多,这里只是入门。 缓存三大问题(穿透、击穿、雪崩)、分布式锁、持久化、集群... 都要深入学。
5.5 缓存三大问题
缓存用不好会有很多问题,经典的三大问题:
1. 缓存穿透
问题:查询一个不存在的数据,缓存没命中,每次都查数据库。 如果有人恶意用不存在的 key 疯狂请求,数据库压力会很大。
解决方案:
- 缓存空值:查不到也缓存,值为 null,设个短过期时间
- 布隆过滤器:把所有存在的 key 放到布隆过滤器,请求先过布隆过滤器
2. 缓存击穿
问题:某个热点 key 过期了,瞬间大量请求过来,都打到数据库。 就像大坝决堤一样。
解决方案:
- 互斥锁:缓存失效时,只有一个请求去查数据库并更新缓存,其他等待
- 永不过期:热点 key 不设过期时间,后台异步更新
3. 缓存雪崩
问题:大量 key 同时过期,或者 Redis 挂了,所有请求都打到数据库。 比击穿更严重,是大面积的。
解决方案:
- 过期时间加随机值:避免大量 key 同时过期
- 多级缓存:本地缓存 + Redis 缓存
- 高可用:Redis 集群、哨兵,避免单点故障
- 限流降级:缓存挂了,限制数据库请求量,保证不挂
缓存三大问题是面试高频考点,也是实际项目中一定会遇到的。 一定要理解原理和解决方案。
5.6 缓存与数据库一致性
缓存和数据库怎么保证一致性?这是个经典问题。
常见方案:
方案一:Cache Aside Pattern(最常用)
- 读:先读缓存,有就返回,没有就读数据库,然后写缓存
- 写:先更新数据库,再删除缓存
为什么是删除缓存,不是更新缓存?
- 更新缓存可能会更新很多次,但很多可能根本没被读,浪费
- 删除缓存更简单,下次读的时候再加载
- 并发场景下更新缓存容易出问题
方案二:延时双删
- 先删缓存
- 再更新数据库
- 延时一会再删缓存(防止并发读导致缓存旧数据)
方案三:订阅 binlog(Canal)
- 更新数据库
- 监听 binlog,异步更新缓存
- 一致性更好,但复杂度高
最终一致性 vs 强一致性:
- 大多数场景接受最终一致性,用 Cache Aside 就行
- 要求强一致性的场景,直接读数据库,不用缓存
没有完美的方案,都是在一致性和性能之间做权衡。 大多数业务场景,Cache Aside + 删除缓存 就够用了。
第 6 章:配置体系详解
6.1 配置文件格式
Spring Boot 支持多种配置文件格式:
| 格式 | 后缀 | 特点 | 推荐 |
|---|---|---|---|
| Properties | .properties | 传统,层级用点,简单 | 简单配置 |
| YAML | .yml / .yaml | 层级清晰,支持列表,更简洁 | 推荐 |
| JSON | .json | 不常用 | 一般不用 |
YAML vs Properties:
# YAML
server:
port: 8080
servlet:
context-path: /demo
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: root# Properties
server.port=8080
server.servlet.context-path=/demo
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=rootYAML 更清晰,尤其是层级深的时候。 但 YAML 对缩进要求严格,注意不要用 Tab,要用空格。
6.2 配置优先级
Spring Boot 的配置有很多来源,优先级从高到低:
- 命令行参数(
--server.port=8080) - 系统环境变量
- 操作系统环境变量
application-{profile}.yml(jar 包外)application-{profile}.yml(jar 包内)application.yml(jar 包外)application.yml(jar 包内)@PropertySource注解- 默认值
优先级高的会覆盖优先级低的。 这个设计很灵活,打包后不用重新打包,改外部配置就行。
6.3 配置绑定的三种方式
方式一:@Value
最简单,单个属性注入。
@RestController
public class TestController {
@Value("${server.port}")
private int port;
@Value("${app.name:default}") // 有默认值
private String appName;
}方式二:Environment
通过 Environment 对象获取。
@Autowired
private Environment env;
public void test() {
String port = env.getProperty("server.port");
String name = env.getProperty("app.name", "default");
}方式三:@ConfigurationProperties(推荐)
批量绑定,类型安全。
@Data
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name = "default";
private String version = "1.0";
private boolean enable = true;
private List<String> hosts;
private Map<String, String> config;
}app:
name: myapp
version: 2.0
enable: true
hosts:
- host1
- host2
config:
key1: value1
key2: value2三种方式对比:
| 维度 | @Value | Environment | @ConfigurationProperties |
|---|---|---|---|
| 适用场景 | 单个属性 | 动态获取 | 批量属性绑定 |
| 类型安全 | 一般 | 弱 | 强 |
| 松散绑定 | 不支持 | 不支持 | 支持 |
| 复杂类型 | 不支持 | 不支持 | 支持(List、Map、对象) |
| JSR-303 校验 | 不支持 | 不支持 | 支持 |
| 推荐度 | ⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ |
批量配置用 @ConfigurationProperties,零散的用 @Value。 企业项目一般都用 @ConfigurationProperties,更规范。
6.4 多环境配置
项目一般有多个环境:开发、测试、生产。
多 Profile 配置:
application.yml # 公共配置
application-dev.yml # 开发环境
application-test.yml # 测试环境
application-prod.yml # 生产环境激活方式:
# application.yml
spring:
profiles:
active: dev # 激活 dev 环境或者命令行:
java -jar app.jar --spring.profiles.active=prod或者环境变量:
SPRING_PROFILES_ACTIVE=prod java -jar app.jar多环境配置是项目必备的。 不同环境的配置分开,代码一套,配置多套。
6.5 配置中心
微服务架构下,服务多了,配置文件分散在各个服务里,不好管理。
这时候需要配置中心:统一管理配置,动态刷新。
常见配置中心:
| 配置中心 | 来源 | 特点 | 推荐 |
|---|---|---|---|
| Nacos | 阿里 | 功能全,注册中心+配置中心,国内主流 | 国内项目首选 |
| Apollo | 携程 | 功能强大,治理完善,灰度发布 | 大公司、复杂需求 |
| Spring Cloud Config | Spring 官方 | 配合 Git,轻量 | Spring Cloud 技术栈 |
| Consul | HashiCorp | 服务发现+配置,Go 写的 | Go 技术栈 |
国内现在 Nacos 用的最多,既可以做注册中心,又可以做配置中心,一站式。 单体项目一般不用配置中心,多服务了再考虑。
第 7 章:安全框架对比与实战
7.1 安全框架对比
Java 生态主流的安全框架:
| 框架 | 特点 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|---|
| Spring Security | Spring 家族,功能强大 | 功能全,Spring 生态好,社区活跃 | 学习曲线陡,配置复杂 | Spring 技术栈、复杂权限 |
| Apache Shiro | 轻量,简单 | 简单易用,不依赖 Spring,灵活 | 功能不如 Security 全,更新慢 | 简单项目、快速开发 |
| Sa-Token | 国产,轻量 | 极其简单,API 友好,功能全 | 生态不如前两个 | 国内项目、快速开发 |
| JWT | 不是框架,是规范 | 无状态,适合前后端分离 | 无法主动失效,续签麻烦 | 前后端分离、微服务 |
怎么选?
- 项目简单、快速开发:Sa-Token / Shiro
- Spring 技术栈、权限复杂:Spring Security
- 前后端分离、无状态:JWT(配合上面任一框架)
以前 Spring Security 配置很复杂,现在 Spring Boot 整合后简单多了。 Sa-Token 是国产的,设计理念很现代,用起来非常顺手,推荐试试。
7.2 Spring Security 核心概念
Spring Security 虽然复杂,但核心概念就几个:
核心组件:
| 组件 | 作用 |
|---|---|
| Authentication | 认证信息(用户、密码、权限) |
| SecurityContext | 安全上下文,存 Authentication |
| UserDetailsService | 加载用户信息 |
| PasswordEncoder | 密码加密 |
| Filter Chain | 过滤器链,认证授权都在这 |
| AuthenticationManager | 认证管理器 |
| AccessDecisionManager | 授权决策管理器 |
认证流程:
请求 → 过滤器链 → UsernamePasswordAuthenticationFilter
→ AuthenticationManager 认证
→ 认证成功 → 存 SecurityContext → 继续执行
→ 认证失败 → 跳登录页 / 返回错误授权流程:
请求 → FilterSecurityInterceptor
→ 获取用户权限和资源所需权限
→ AccessDecisionManager 投票决策
→ 有权限 → 放行
→ 没权限 → 抛异常 → 403Spring Security 的核心就是一堆过滤器。 理解了过滤器链,就理解了一半。
7.3 JWT 实战
前后端分离项目,JWT 是现在的主流方案。
什么是 JWT:
- JSON Web Token
- 由三部分组成:Header.Payload.Signature
- 服务端不用存,客户端存,无状态
JWT 工作流程:
1. 用户登录 → 验证成功 → 生成 JWT → 返回给前端
2. 前端把 JWT 存起来(localStorage / cookie)
3. 后续请求 header 里带上 JWT
4. 后端验证 JWT → 有效就放行Spring Boot 整合 JWT:
// JWT 工具类
public class JwtUtil {
private static final String SECRET = "your-secret-key";
private static final long EXPIRE = 7 * 24 * 3600 * 1000; // 7 天
// 生成 token
public static String generateToken(Long userId, String username) {
return Jwts.builder()
.setSubject(userId.toString())
.claim("username", username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
}
// 解析 token
public static Claims parseToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
}
}JWT 的问题:
- 无法主动失效(用户改密码了,旧 token 还能用)
- 续签麻烦(快过期了怎么续)
- token 泄露了怎么办
解决方案:
- 黑名单:失效的 token 存 Redis,校验时查
- 短有效期 + refresh token:access token 有效期短,用 refresh token 换新的
- 存 Redis:还是服务端存,那就不是无状态了,但可控
JWT 不是银弹,有它的适用场景。 简单的、对安全性要求不高的场景,用 JWT 很方便。 要求高的,还是用 Session + Redis,或者配合黑名单。
第 8 章:缓存技术深度对比
8.1 缓存技术全景
缓存是提升性能的第一利器。
缓存的层次:
浏览器缓存 → CDN → 反向代理缓存 → 应用本地缓存 → 分布式缓存 → 数据库越靠前,速度越快,容量越小。
Java 常用缓存技术:
| 类型 | 技术 | 特点 | 适用场景 |
|---|---|---|---|
| 本地缓存 | HashMap / ConcurrentHashMap | 最简单 | 极简单场景 |
| Guava Cache | Google 出品,功能丰富 | 本地缓存首选 | |
| Caffeine | 高性能,Java 8+,Spring Boot 默认 | 推荐,性能最好 | |
| Ehcache | 老牌,功能全 | 传统项目 | |
| 分布式缓存 | Redis | 最主流,功能丰富 | 绝大多数场景 |
| Memcached | 简单,纯内存 | 简单缓存 | |
| Hazelcast | 分布式,支持 SQL | 分布式计算 |
8.2 本地缓存对比
Caffeine vs Guava Cache vs Ehcache:
| 维度 | Caffeine | Guava Cache | Ehcache |
|---|---|---|---|
| 性能 | 最高 | 高 | 中 |
| 功能 | 丰富 | 丰富 | 很丰富 |
| 持久化 | 不支持 | 不支持 | 支持 |
| 分布式 | 不支持 | 不支持 | 支持(Terracotta) |
| Spring 集成 | 支持(Spring Boot 2.x+ 默认) | 支持 | 支持 |
| 学习成本 | 低 | 低 | 中 |
| 推荐度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
Caffeine 是目前 Java 本地缓存的性能王者,Spring Boot 2.x+ 默认就是它。 新项目优先选 Caffeine。
8.3 Spring Cache 抽象
Spring Cache 是 Spring 的缓存抽象,统一了各种缓存的使用方式。
常用注解:
| 注解 | 作用 |
|---|---|
@EnableCaching | 开启缓存 |
@Cacheable | 查询缓存,有就返回,没有就执行方法并缓存 |
@CachePut | 更新缓存(方法一定会执行) |
@CacheEvict | 删除缓存 |
@CacheConfig | 类级别配置 |
@Caching | 组合多个缓存操作 |
使用示例:
@Service
@CacheConfig(cacheNames = "user")
public class UserService {
@Cacheable(key = "#id")
public User getUserById(Long id) {
// 查数据库
return userMapper.selectById(id);
}
@CachePut(key = "#user.id")
public User updateUser(User user) {
userMapper.updateById(user);
return user;
}
@CacheEvict(key = "#id")
public void deleteUser(Long id) {
userMapper.deleteById(id);
}
}Spring Cache 的好处是:代码里不用写缓存逻辑,加注解就行。 切换缓存实现(Caffeine → Redis)也不用改代码,改配置就行。
8.4 多级缓存
为了更高的性能,可以用多级缓存:
- L1:本地缓存(Caffeine),速度最快,容量小
- L2:分布式缓存(Redis),速度快,容量大
- L3:数据库,最慢,容量最大
请求流程:
请求 → 本地缓存 → 有 → 返回
↓ 没有
Redis 缓存 → 有 → 回填本地缓存 → 返回
↓ 没有
数据库 → 回填 Redis → 回填本地缓存 → 返回多级缓存的问题:
- 一致性:本地缓存怎么跟 Redis 保持一致?
- 失效:数据更新了,怎么通知所有节点清本地缓存?
解决方案:
- 短过期时间:本地缓存过期时间设短一点(比如 1 分钟)
- 消息通知:数据更新了,发 MQ 通知所有节点清缓存
- 主动失效:用 Redis 发布订阅,通知清缓存
多级缓存性能更好,但复杂度也更高。 不是所有项目都需要,根据性能需求来。
第 9 章:消息队列选型与实战
9.1 消息队列能做什么
消息队列(MQ)是分布式系统的重要组件。
三大核心作用:
| 作用 | 说明 | 例子 |
|---|---|---|
| 异步 | 不用等,后台慢慢做 | 注册后发邮件、发短信 |
| 解耦 | 系统之间不直接依赖 | 下单后通知库存、物流、积分 |
| 削峰 | 高峰期请求先存起来,慢慢消费 | 秒杀、抢票 |
其他作用:
- 最终一致性
- 广播
- 流量控制
- 日志收集
- ...
9.2 主流消息队列对比
| 维度 | RabbitMQ | RocketMQ | Kafka | Pulsar |
|---|---|---|---|---|
| 开发语言 | Erlang | Java | Scala/Java | Java |
| 性能 | 万级 | 十万级 | 十万级~百万级 | 百万级 |
| 延迟 | 微秒级 | 毫秒级 | 毫秒级 | 毫秒级 |
| 可靠性 | 高 | 很高 | 很高 | 很高 |
| 功能丰富度 | 很丰富 | 丰富 | 少(专注流处理) | 丰富 |
| 消息顺序 | 不保证 | 支持 | 支持(分区内) | 支持 |
| 定时消息 | 支持(插件) | 支持(原生) | 不支持 | 支持 |
| 事务消息 | 不支持 | 支持 | 支持 | 支持 |
| 死信队列 | 支持 | 支持 | 支持 | 支持 |
| 运维复杂度 | 中 | 中 | 高 | 高 |
| 生态 | 丰富 | 国内丰富 | 很丰富(大数据) | 新兴 |
| 适用场景 | 中小项目、功能需求多 | 电商、金融、国内项目 | 大数据、日志、流处理 | 云原生、下一代 |
怎么选?
- 中小项目、功能多:RabbitMQ
- 电商、金融、国内:RocketMQ
- 大数据、日志、流处理:Kafka
- 云原生、新一代:Pulsar
国内现在互联网公司用 RocketMQ 和 Kafka 比较多。 业务系统用 RocketMQ,大数据用 Kafka。 RabbitMQ 适合中小项目,功能丰富,上手快。
9.3 消息可靠性
消息队列怎么保证消息不丢?这是面试高频问题。
消息丢失的三个环节:
生产者 → Broker → 消费者1. 生产者丢失
问题:生产者发消息,网络问题,Broker 没收到。
解决方案:
- 确认机制(publisher confirm):发完等 Broker 确认
- 重试机制:失败了重试
- 本地消息表:消息存本地,定时重发,保证最终送达
2. Broker 丢失
问题:Broker 收到消息,还没持久化,挂了。
解决方案:
- 持久化:消息持久化到磁盘
- 集群:多副本,一个挂了还有其他的
- 同步刷盘:消息刷盘了才确认
3. 消费者丢失
问题:消费者收到消息,还没处理,程序崩了。
解决方案:
- 手动 ACK:处理完了才确认,没处理完不确认
- 重试机制:处理失败重试
- 死信队列:重试多次都失败,进死信队列,人工处理
消息可靠性是 MQ 的核心问题。 要保证消息不丢,三个环节都要做处理。 但可靠性越高,性能越低,根据业务需求权衡。
9.4 消息幂等性
消息可能重复消费,怎么保证消费多次结果一样?
幂等性:同一个消息消费一次和消费多次,结果一样。
实现方案:
| 方案 | 原理 | 适用场景 |
|---|---|---|
| 唯一 ID + 去重表 | 每个消息有唯一 ID,消费前查去重表 | 通用 |
| 数据库唯一键 | 利用数据库唯一约束 | 插入操作 |
| 乐观锁 | 版本号,更新时带版本 | 更新操作 |
| Redis set | 消息 ID 存 Redis set,消费前检查 | 通用,性能好 |
| 业务状态判断 | 根据业务状态判断是否处理过 | 有状态的业务 |
幂等性是 MQ 的必考题,也是实际项目必须考虑的。 消息队列一般都有重试机制,重复消息是常态,必须做幂等。
第 10 章:任务调度方案对比
10.1 任务调度方案全景
定时任务、异步任务,项目里经常用到。
常见方案:
| 方案 | 特点 | 适用场景 |
|---|---|---|
| Thread + sleep | 最简单 | 测试用,生产不用 |
| Timer | JDK 自带 | 简单任务,单线程 |
| ScheduledExecutorService | JDK 线程池版 | 简单定时任务 |
| Spring @Scheduled | Spring 注解,简单 | 单体应用,简单任务 |
| Quartz | 功能强大,持久化,集群 | 复杂调度、企业级 |
| XXL-Job | 分布式任务调度,可视化 | 分布式、微服务 |
| ElasticJob | 当当开源,分布式 | 分布式、弹性伸缩 |
| PowerJob | 新一代,功能丰富 | 新一代调度框架 |
10.2 方案对比
| 维度 | Spring Schedule | Quartz | XXL-Job | ElasticJob | PowerJob |
|---|---|---|---|---|---|
| 上手难度 | 简单 | 中等 | 简单 | 中等 | 中等 |
| 功能 | 基础 | 强大 | 强大 | 强大 | 很强大 |
| 可视化 | 无 | 无 | 有(管理后台) | 有 | 有 |
| 持久化 | 无 | 支持 | 支持 | 支持 | 支持 |
| 集群 | 不支持 | 支持 | 支持 | 支持 | 支持 |
| 动态管理 | 不支持 | 支持(代码) | 支持(页面) | 支持 | 支持 |
| 任务依赖 | 不支持 | 不支持 | 支持 | 不支持 | 支持 |
| 分片 | 不支持 | 不支持 | 支持 | 支持 | 支持 |
| 失败重试 | 不支持 | 不支持 | 支持 | 支持 | 支持 |
| 适用场景 | 单体简单任务 | 单体复杂任务 | 分布式任务调度 | 分布式弹性任务 | 新一代调度 |
怎么选?
- 单体应用、简单任务:Spring @Scheduled
- 单体应用、复杂调度:Quartz
- 分布式、微服务:XXL-Job / ElasticJob / PowerJob
- 国内项目:XXL-Job 用的最多,文档全,社区活跃
分布式任务调度框架,国内 XXL-Job 是绝对主流。 界面友好,功能齐全,文档详细,非常推荐。
10.3 异步任务 vs 消息队列
很多人搞不清:什么时候用异步任务,什么时候用消息队列?
| 维度 | 异步任务(@Async) | 消息队列(MQ) |
|---|---|---|
| 复杂度 | 简单 | 复杂 |
| 可靠性 | 低(进程挂了就丢了) | 高(持久化) |
| 解耦 | 弱(同一个应用内) | 强(跨系统) |
| 削峰 | 不能 | 能 |
| 重试 | 不支持 | 支持 |
| 死信 | 没有 | 有 |
| 监控 | 弱 | 强 |
| 适用场景 | 同一个应用内的简单异步 | 跨系统、需要可靠、需要削峰 |
简单判断:
- 同一个应用里,简单的异步,用 @Async
- 跨系统、需要可靠投递、需要削峰,用 MQ
不要为了用 MQ 而用 MQ,简单异步用 @Async 就够了。 但重要的业务、不能丢的任务,一定要用 MQ。
第 11 章:日志体系与最佳实践
11.1 日志框架全景
Java 日志框架很多,有点乱,梳理一下:
日志门面(抽象层):
- SLF4J:最主流的日志门面
- Commons Logging (JCL):Spring 用的,老了
日志实现:
- Logback:SLF4J 原生实现,Spring Boot 默认
- Log4j2:Apache 出品,性能好
- Log4j:老牌,已停止更新
- JUL (java.util.logging):JDK 自带,不好用
关系:
应用代码 → SLF4J(门面) → Logback / Log4j2 / Log4j(实现)最佳实践:代码里用 SLF4J,实现用 Logback 或 Log4j2。 这样以后想换实现,不用改代码。
11.2 Spring Boot 默认日志
Spring Boot 默认用的是:SLF4J + Logback
配置文件:logback-spring.xml
简单配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 文件输出 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory> <!-- 保留 30 天 -->
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 根日志级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
<!-- 特定包日志级别 -->
<logger name="com.example" level="DEBUG"/>
<logger name="org.springframework" level="INFO"/>
</configuration>11.3 日志级别
| 级别 | 说明 | 使用场景 |
|---|---|---|
| TRACE | 最细粒度 | 追踪代码执行路径,很少用 |
| DEBUG | 调试信息 | 开发调试用 |
| INFO | 一般信息 | 正常运行信息,默认级别 |
| WARN | 警告 | 可能有问题,但不影响运行 |
| ERROR | 错误 | 出问题了,需要处理 |
| FATAL | 致命错误 | 系统崩溃,基本不用 |
使用原则:
- 开发环境:DEBUG 级别,看详细信息
- 生产环境:INFO 级别,只看重要信息
- 出问题了:临时调到 DEBUG,排查完改回去
11.4 日志最佳实践
1. 用占位符,不要字符串拼接
// 好
log.info("用户登录成功,userId: {}", userId);
// 不好
log.info("用户登录成功,userId: " + userId);占位符的好处:如果日志级别不够,不会执行拼接,性能好。
2. 关键路径打日志
- 接口入口:打印请求参数
- 接口出口:打印返回结果
- 异常:打印异常堆栈
- 关键业务节点:下单、支付、发货等
3. 日志要有用
- 不要打一堆没用的日志
- 日志里要有上下文(用户 ID、订单号等),方便排查
- 异常日志要打堆栈,不要只打 message
4. 敏感信息脱敏
- 手机号、身份证、密码、银行卡号等敏感信息要脱敏
- 不要明文打在日志里
5. 异步日志
- 高并发场景用异步日志,减少 IO 对业务的影响
- Logback、Log4j2 都支持异步
6. 日志收集
- 微服务架构下,日志分散在各个服务
- 用 ELK(Elasticsearch + Logstash + Kibana)统一收集
- 或者 Loki + Grafana
日志是排查问题的第一手段,非常重要。 日志打得好,排查问题事半功倍。 日志打得烂,出了问题一头雾水。
第 12 章:监控与可观测性
12.1 可观测性三支柱
现代系统可观测性有三大支柱:
| 支柱 | 作用 | 工具 |
|---|---|---|
| Metrics(指标) | 数值型数据,看趋势、告警 | Prometheus + Grafana |
| Logging(日志) | 事件记录,排查问题 | ELK / Loki |
| Tracing(链路追踪) | 请求链路,看耗时、找瓶颈 | SkyWalking / Jaeger / Zipkin |
三者关系:
- Metrics 告诉你"哪里出问题了"
- Logging 告诉你"出了什么问题"
- Tracing 告诉你"问题在哪一步"
12.2 Spring Boot Actuator
Spring Boot 自带的监控工具:Actuator。
常用端点:
| 端点 | 作用 |
|---|---|
/actuator/health | 健康检查 |
/actuator/info | 应用信息 |
/actuator/metrics | 指标 |
/actuator/env | 环境变量 |
/actuator/beans | Bean 列表 |
/actuator/mappings | 接口映射 |
/actuator/loggers | 日志级别 |
/actuator/heapdump | 堆转储 |
配置:
management:
endpoints:
web:
exposure:
include: "*" # 暴露所有端点(生产环境不要这么干)
endpoint:
health:
show-details: always # 显示详细健康信息Actuator 是 Spring Boot 自带的监控基础。 生产环境注意安全,不要暴露所有端点,要加权限控制。
12.3 Prometheus + Grafana
现在最主流的监控方案。
架构:
- Prometheus:时序数据库,采集和存储指标
- Grafana:可视化仪表盘,展示图表
- Alertmanager:告警
Spring Boot 整合:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>management:
endpoints:
web:
exposure:
include: health,info,prometheus
metrics:
tags:
application: ${spring.application.name}常用监控指标:
- JVM:堆内存、GC、线程数
- 系统:CPU、内存、磁盘
- 应用:QPS、响应时间、错误率
- 业务:订单量、用户数、支付成功率
Prometheus + Grafana 是现在监控的标配。 不管什么项目,监控一定要做。 没有监控的系统,就是裸奔。
12.4 链路追踪
微服务架构下,一个请求经过很多服务,出了问题不知道在哪。
链路追踪就是用来追踪请求在各个服务中的流转。
主流方案:
| 工具 | 来源 | 特点 | 推荐 |
|---|---|---|---|
| SkyWalking | 国产(华为) | 功能全,无侵入,UI 友好 | 国内首选 |
| Jaeger | Uber | 轻量,CNCF 项目 | 云原生 |
| Zipkin | 老牌,简单 | 简单场景 | |
| Pinpoint | 韩国 | 功能强,侵入性低 | 功能全面 |
国内 SkyWalking 用的最多,国产的,文档全,UI 友好。 微服务项目一定要上链路追踪,排查问题太方便了。
第 13 章:测试体系
13.1 测试金字塔
测试的金字塔模型:
/\
/ \ E2E 测试(端到端)
/ \
/------\ 集成测试
/ \
/----------\ 单元测试
/ \- 单元测试:最多,最快,测单个方法/类
- 集成测试:中等,测模块间协作
- E2E 测试:最少,最慢,测整个系统
13.2 单元测试
单元测试是测试的基础。
技术栈:
- JUnit 5:测试框架(Spring Boot 3.x 默认)
- Mockito:Mock 框架,模拟依赖
- AssertJ:断言库,流式断言
示例:
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserMapper userMapper;
@InjectMocks
private UserServiceImpl userService;
@Test
void testGetUserById() {
// 准备数据
User user = new User();
user.setId(1L);
user.setName("张三");
when(userMapper.selectById(1L)).thenReturn(user);
// 执行
User result = userService.getUserById(1L);
// 断言
assertThat(result).isNotNull();
assertThat(result.getName()).isEqualTo("张三");
verify(userMapper).selectById(1L);
}
}13.3 Spring Boot 集成测试
Spring Boot 提供了 @SpringBootTest 注解,可以启动整个 Spring 容器来测试。
示例:
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void testGetUser() throws Exception {
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(200))
.andExpect(jsonPath("$.data.name").value("张三"));
}
}测试切片:
不需要启动整个容器,只启动需要的部分:
| 注解 | 作用 |
|---|---|
@WebMvcTest | 只测 Web 层 |
@DataJpaTest | 只测 JPA 层 |
@JsonTest | 只测 JSON 序列化 |
单元测试要快,不要依赖外部资源。 依赖数据库、Redis 的,用 H2 内存数据库或者 Mock。 测试要独立,不能互相依赖。
13.4 测试最佳实践
- 测试命名清晰:方法名说明测什么、预期结果
- Given-When-Then 模式:准备 → 执行 → 断言
- 一个测试一个断言:每个测试只测一个点
- 测试要独立:测试之间不能有依赖
- 测试要快:单元测试秒级完成
- 覆盖率不是目标:不要为了覆盖率写无用的测试
- 边界值要测:null、空、最大值、异常情况
测试是质量的保障,也是重构的信心来源。 有完善的测试,改代码心里有底。 没有测试,改代码跟拆炸弹一样。
第 14 章:部署与运维
14.1 打包方式
JAR 包(推荐):
- 内嵌容器,直接
java -jar运行 - 部署简单,一个文件搞定
- Spring Boot 默认方式
WAR 包:
- 需要外部 Tomcat
- 老项目、需要统一部署的时候用
JAR vs WAR:
| 维度 | JAR | WAR |
|---|---|---|
| 部署方式 | java -jar | 放到外部 Tomcat |
| 容器 | 内嵌 | 外部 |
| 复杂度 | 低 | 高 |
| 灵活性 | 高(每个应用独立容器) | 低(共享容器) |
| 推荐 | ✅ | 老项目用 |
新项目一律用 JAR 包,简单方便。 WAR 包是历史遗留了。
14.2 运行参数
常用启动参数:
# 基本运行
java -jar app.jar
# 指定端口
java -jar app.jar --server.port=8080
# 指定环境
java -jar app.jar --spring.profiles.active=prod
# JVM 参数
java -Xms512m -Xmx1024m -XX:+UseG1GC -jar app.jar
# 远程调试
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar app.jar常用 JVM 参数:
| 参数 | 作用 |
|---|---|
-Xms | 初始堆内存 |
-Xmx | 最大堆内存 |
-Xmn | 新生代大小 |
-XX:+UseG1GC | 使用 G1 垃圾收集器 |
-XX:+HeapDumpOnOutOfMemoryError | OOM 时自动 dump 堆 |
-XX:HeapDumpPath | dump 文件路径 |
-XX:+PrintGCDetails | 打印 GC 详情 |
14.3 Docker 部署
现在最流行的部署方式。
Dockerfile:
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY target/app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]构建镜像:
docker build -t myapp:1.0 .运行容器:
docker run -d -p 8080:8080 --name myapp myapp:1.0docker-compose:
version: '3'
services:
app:
image: myapp:1.0
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on:
- mysql
- redis
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=root
redis:
image: redis:7Docker 部署是现在的标配。 环境一致,部署方便,迁移容易。
14.4 K8s 部署
大规模微服务用 Kubernetes(K8s)。
Deployment + Service:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:1.0
ports:
- containerPort: 8080
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 1000m
memory: 1Gi
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
---
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
type: ClusterIPK8s 是容器编排的事实标准。 微服务、大规模部署一定要学 K8s。 小项目 Docker + docker-compose 就够了。
第 15 章:前沿技术与未来趋势
15.1 Spring Boot 3.x 新特性
Spring Boot 3.0 是一个大版本,变化很大。
核心变化:
| 变化 | 说明 |
|---|---|
| JDK 17+ | 最低要求 JDK 17 |
| Jakarta EE 9+ | javax.* → jakarta.* 命名空间 |
| Spring Framework 6 | 基于 Spring 6 |
| GraalVM 原生镜像 | 官方支持 AOT 编译 |
| 虚拟线程预览 | 配合 JDK 21 的虚拟线程 |
| Observability | 增强可观测性 |
| 移除老东西 | 移除了很多过时的支持 |
从 2.x 升级到 3.x 注意事项:
- JDK 要升到 17+
- 所有 javax.* 改成 jakarta.*
- 第三方依赖要确认支持 3.x
- 移除的功能要找替代
Spring Boot 3.x 是未来的方向,新项目建议直接上 3.x。 老项目如果稳定,不着急升,等生态成熟了再说。
15.2 GraalVM 与原生镜像
什么是 GraalVM:
- Oracle 开发的高性能虚拟机
- 支持 AOT(Ahead-Of-Time)编译
- 可以把 Java 程序编译成原生可执行文件
原生镜像的好处:
- 启动极快(毫秒级)
- 内存占用极低
- 不需要 JVM
缺点:
- 编译慢
- 动态特性受限(反射、动态代理等需要配置)
- 调试困难
Spring Boot 3.x 支持:
# 构建原生镜像
mvn native:compile
# 或者用 Buildpacks
mvn spring-boot:build-image -Pnative原生镜像特别适合 Serverless、CLI 工具、函数计算等场景。 启动快、内存小,按需启动,用完就销毁。 传统 Web 应用暂时不建议,生态还在完善中。
15.3 虚拟线程(Project Loom)
JDK 21 正式引入了虚拟线程(Virtual Thread)。
什么是虚拟线程:
- 轻量级线程,由 JVM 管理,不是 OS 线程
- 一个 OS 线程可以跑很多虚拟线程
- 创建成本极低,可以开几十上百万个
- 阻塞是免费的(阻塞虚拟线程不会阻塞 OS 线程)
对 Spring Boot 的影响:
- 以前 Tomcat 线程池最多几百个线程
- 现在可以开几万、几十万个虚拟线程
- 吞吐量大幅提升
- 不用再写异步代码了,同步代码就能有异步的性能
Spring Boot 开启虚拟线程:
spring:
threads:
virtual:
enabled: true虚拟线程是 Java 的革命性特性。 可能会改变 Java 后端的编程模型,不用再搞各种异步了。 但还比较新,需要时间验证,生产环境谨慎使用。
15.4 响应式编程
响应式编程(Reactive)是另一种编程范式。
Spring WebFlux:
- Spring 的响应式 Web 框架
- 基于 Reactor
- 非阻塞,高并发
- 跟 Spring MVC 并列
响应式 vs 传统 MVC:
| 维度 | Spring MVC | Spring WebFlux |
|---|---|---|
| 编程模型 | 命令式 | 响应式 |
| 阻塞 | 阻塞 | 非阻塞 |
| 并发模型 | 线程池 | 事件循环 |
| 学习曲线 | 平缓 | 陡峭 |
| 适用场景 | 大多数业务 | 高并发、流处理 |
| 生态 | 成熟 | 还在发展 |
响应式编程性能更好,但学习成本高,调试困难。 大多数业务场景用 MVC 就够了。 高并发、IO 密集型的场景可以考虑 WebFlux。
15.5 其他前沿方向
- Serverless:函数即服务,按需付费
- Service Mesh:服务网格(Istio、Linkerd)
- eBPF:内核级可观测性、网络
- AI 集成:Spring AI,大模型集成
- 低代码 / 无代码:快速开发平台
- WebAssembly:跨语言、高性能
技术发展很快,要保持学习。 但也不用什么火就学什么,先把基础打牢,再根据兴趣和工作需要深入。
第 16 章:性能优化实战
16.1 性能优化方法论
优化原则:
- 不要过早优化:先让它跑起来,再优化
- 先测量再优化:用数据说话,不要猜
- 找瓶颈:80% 的性能问题在 20% 的地方
- 权衡取舍:性能、复杂度、成本之间找平衡
优化步骤:
发现问题 → 定位瓶颈 → 分析原因 → 优化 → 验证 → 持续监控16.2 常见性能瓶颈
| 层级 | 常见瓶颈 | 优化手段 |
|---|---|---|
| 前端 | 资源大、请求多 | 压缩、缓存、CDN、懒加载 |
| 网关 | 并发不够 | 扩容、调优 |
| 应用 | 代码慢、锁竞争、GC | 代码优化、缓存、异步 |
| 缓存 | 穿透、击穿、雪崩 | 见第 5 章 |
| 数据库 | 慢 SQL、锁、索引 | 索引优化、SQL 优化、分库分表 |
| 网络 | 延迟高、带宽不够 | 就近接入、扩容 |
16.3 应用层优化
1. 加缓存
- 热点数据加缓存
- 本地缓存 + 分布式缓存多级缓存
2. 异步化
- 耗时操作异步处理
- 用 MQ 解耦和削峰
3. 池化技术
- 线程池、连接池、对象池
- 合理设置池大小
4. 代码优化
- 避免在循环里查数据库
- 批量操作代替单次操作
- 减少对象创建
- 用合适的数据结构
5. JVM 调优
- 堆大小设置
- 垃圾收集器选择
- GC 调优
16.4 数据库优化
数据库是最常见的瓶颈。
1. 索引优化
- 经常查询的字段加索引
- 联合索引注意顺序
- 避免索引失效
- 不要建太多索引(影响写入)
2. SQL 优化
- 避免 select *
- 避免大事务
- 分页优化(深分页)
- 用 explain 分析执行计划
3. 读写分离
- 主库写,从库读
- 一主多从,提升读性能
4. 分库分表
- 数据量大了,单表扛不住
- 水平分表、垂直分库
- 用 ShardingSphere 等中间件
数据库优化是个大话题,够写一本书了。 记住:先优化索引和 SQL,不行再读写分离,最后才考虑分库分表。 分库分表复杂度很高,不要上来就搞。
16.5 性能监控工具
| 工具 | 作用 |
|---|---|
| JProfiler / Arthas | JVM 性能分析 |
| JMeter / wrk | 压测 |
| Prometheus + Grafana | 监控指标 |
| SkyWalking | 链路追踪,找慢接口 |
| Explain | SQL 执行计划分析 |
| Flame Graph | 火焰图,看 CPU 热点 |
性能优化的第一步是测量。 用工具找到瓶颈,再针对性优化。 不要凭感觉优化,很多时候你猜的瓶颈根本不是瓶颈。
第 17 章:架构设计与最佳实践
17.1 项目结构最佳实践
标准分层结构:
com.example.project
├── ProjectApplication.java # 启动类
├── common/ # 公共模块
│ ├── result/ # 统一返回结果
│ ├── exception/ # 异常定义
│ ├── constant/ # 常量
│ └── utils/ # 工具类
├── config/ # 配置类
│ ├── WebConfig.java
│ ├── RedisConfig.java
│ └── MybatisPlusConfig.java
├── controller/ # 控制层
│ ├── UserController.java
│ └── OrderController.java
├── service/ # 业务层
│ ├── UserService.java
│ ├── impl/
│ │ └── UserServiceImpl.java
│ └── ...
├── mapper/ # 数据访问层
│ ├── UserMapper.java
│ └── ...
├── entity/ # 数据库实体
│ ├── User.java
│ └── ...
├── dto/ # 数据传输对象
│ ├── req/ # 请求 DTO
│ └── resp/ # 响应 DTO
├── vo/ # 视图对象
├── convert/ # 对象转换器
├── interceptor/ # 拦截器
├── filter/ # 过滤器
└── aspect/ # 切面分层原则:
- 上层依赖下层,不能反向依赖
- 每层职责单一
- 跨层调用通过接口
项目结构没有标准答案,但要清晰、一致。 团队约定好,大家都遵守,代码就好维护。
17.2 常用设计模式
Spring 里到处都是设计模式,了解设计模式能帮你更好地理解 Spring。
Spring 中常用的设计模式:
| 模式 | 应用场景 |
|---|---|
| 工厂模式 | BeanFactory、ApplicationContext |
| 单例模式 | Spring Bean 默认单例 |
| 代理模式 | AOP、事务 |
| 模板方法模式 | JdbcTemplate、RestTemplate |
| 观察者模式 | 事件机制、ApplicationEvent |
| 策略模式 | HandlerMapping、HandlerAdapter |
| 装饰器模式 | BeanWrapper |
| 责任链模式 | 过滤器链、拦截器链 |
设计模式是前人总结的经验。 不用为了用模式而用模式,合适的场景用合适的模式。 看懂 Spring 源码里的设计模式,对你自己写代码也很有帮助。
17.3 代码规范
为什么要有规范:
- 统一风格,可读性好
- 减少低级错误
- 降低维护成本
- 新人上手快
规范包括:
- 命名规范(类名、方法名、变量名)
- 代码格式(缩进、空行、括号)
- 注释规范
- 异常处理规范
- 日志规范
- 提交规范
工具:
- Checkstyle:代码风格检查
- SpotBugs / FindBugs:Bug 检查
- PMD:代码质量检查
- Alibaba Java Coding Guidelines:阿里 Java 开发规约插件
规范很重要,尤其是团队开发。 代码是写给人看的,顺便给机器执行。 写代码的时候想想:别人能不能看懂?三个月后自己能不能看懂?
17.4 安全最佳实践
OWASP Top 10(最常见的安全风险):
- 注入(SQL 注入、命令注入)
- 失效的身份认证
- 敏感数据泄露
- XML 外部实体(XXE)
- 失效的访问控制
- 安全配置错误
- 跨站脚本(XSS)
- 不安全的反序列化
- 使用含有已知漏洞的组件
- 不足的日志和监控
后端安全清单:
- [ ] 密码加密存储(BCrypt)
- [ ] SQL 注入防护(参数化查询)
- [ ] XSS 防护(输出转义)
- [ ] CSRF 防护
- [ ] 越权检查(水平越权、垂直越权)
- [ ] 敏感数据脱敏
- [ ] 接口限流
- [ ] 输入校验
- [ ] 依赖漏洞扫描
- [ ] 日志审计
安全很重要,出了事就是大事。 不要觉得"我们是小公司,没人攻击我们"。 很多攻击是脚本自动扫的,不管你大小。
17.5 微服务架构演进
架构演进路径:
单体应用 → 垂直拆分 → SOA → 微服务 → 服务网格什么时候考虑微服务:
- 团队大了,单体开发效率低
- 系统复杂了,单体不好维护
- 需要独立部署、独立扩展
- 不同模块技术栈不一样
微服务不是银弹:
- 复杂度大大增加
- 分布式问题(一致性、容错、监控...)
- 运维成本高
- 调试困难
建议:
- 小团队、小项目,单体就好
- 随着业务发展,逐步拆分
- 不要上来就微服务,很多项目根本不需要
架构是演化出来的,不是设计出来的。 合适的架构就是最好的架构。 不要为了微服务而微服务。
第 18 章:学习路线与资源推荐
18.1 Spring Boot 学习路线
第一阶段:入门
- Java 基础
- Spring 基础(IOC、AOP)
- Spring Boot 快速入门
- Web 开发
- MyBatis / MyBatis-Plus
- 做个小项目
第二阶段:进阶
- 自动配置原理
- 各种 starter 整合
- 缓存、消息队列、安全
- 性能优化
- 监控和运维
- 做个完整项目
第三阶段:深入
- Spring 源码
- Spring Boot 源码
- 设计模式
- 架构设计
- 微服务
- 中间件原理
第四阶段:专家
- 深入 JVM
- 操作系统、网络
- 分布式系统
- 技术选型和架构设计
- 团队管理和技术规划
学习是个循序渐进的过程。 不要急于求成,一步一步来。 多动手,多实践,光看是学不会的。
18.2 推荐资源
官方文档:
- Spring Boot 官方文档:https://docs.spring.io/spring-boot/docs/current/reference/html/
- Spring Framework 官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/
书籍:
- 《Spring Boot 实战》
- 《Spring 实战》
- 《深入理解 Java 虚拟机》
- 《设计模式之禅》
- 《高性能 MySQL》
- 《Redis 设计与实现》
网站:
- 菜鸟教程:https://www.runoob.com/
- 廖雪峰的官方网站:https://www.liaoxuefeng.com/
- Baeldung:https://www.baeldung.com/(英文,质量高)
视频:
- B站搜"Spring Boot 教程",很多免费的
- 黑马程序员、尚硅谷、动力节点等
社区:
- GitHub:https://github.com/
- Stack Overflow:https://stackoverflow.com/
- 掘金:https://juejin.cn/
- 思否:https://segmentfault.com/
- V2EX:https://www.v2ex.com/
18.3 给初学者的建议
多动手:看十遍不如敲一遍,代码是敲出来的,不是看出来的。
多思考:不要只抄代码,要想为什么这么写,有没有更好的方式。
多调试:遇到问题先自己 Debug,一步步跟,比问别人印象深。
打好基础:Java 基础、数据结构、算法、计算机网络、操作系统,这些基础很重要。
不要贪多:技术学不完,先把一个方向学深,再扩展。
写博客:学了东西写下来,输出倒逼输入,也方便以后复习。
做项目:找个完整的项目做一遍,把学到的知识串起来。
看源码:有能力了看看 Spring 源码,收获会很大。
保持学习:技术发展很快,要持续学习。
不要焦虑:技术太多学不完很正常,根据自己的节奏来。
编程是个手艺活,靠的是积累。 没有捷径,就是多练、多想、多总结。 坚持下去,你会越来越强。
写在最后
恭喜你看到了这里!
这篇教程涵盖了 Spring Boot 从入门到进阶的方方面面,从基础用法到核心原理,从技术选型到最佳实践,从传统开发到前沿趋势。
希望这篇教程能帮到你。
记住:
- 技术是工具,解决问题才是目的
- 没有最好的技术,只有最合适的
- 保持好奇心,保持学习的热情
- 实践出真知
路漫漫其修远兮,吾将上下而求索。
加油!