第一章:Spring Boot 入门与环境搭建
1.1 Spring Boot 简介与核心特性
| 概念名称 | 说明 | 注意事项 |
|---|
| Spring Boot | 一个用于快速构建基于 Spring 的生产级应用的框架,通过”约定优于配置”理念简化开发流程。 | 不是 Spring 的替代品,而是其增强工具,用于减少样板代码和配置。 |
| 自动配置(Auto-configuration) | 根据项目依赖自动配置 Spring 和第三方库的 Bean。例如,引入 spring-boot-starter-web 后自动配置嵌入式 Tomcat 和 MVC。 | 可通过 @EnableAutoConfiguration(exclude = {...}) 排除不需要的配置。 |
| 起步依赖(Starter Dependencies) | 预定义的依赖组合,简化 Maven/Gradle 配置。如 spring-boot-starter-web 包含 Web 开发所需全部依赖。 | 避免手动添加重复依赖,优先使用 Starter。 |
| 嵌入式服务器 | 内置 Tomcat、Jetty 或 Undertow,无需部署 WAR 到外部容器,直接运行 JAR。 | 默认使用 Tomcat;可通过排除依赖更换为 Jetty 或 Undertow。 |
| Actuator | 提供生产环境监控和管理功能(如健康检查、指标、环境信息等)。 | 生产环境需配置安全访问控制,避免信息泄露。 |
| 无代码生成与 XML 配置 | 基于 Java 注解和自动配置,无需生成代码或编写大量 XML。 | 仍支持 XML 配置,但推荐使用注解方式。 |
1.2 开发环境准备(JDK、Maven/Gradle、IDE)
| 工具/环境 | 版本要求 | 用途 | 安装与配置说明 | 注意事项 |
|---|
| JDK | 8 或以上(推荐 17 或 21) | Java 运行环境 | 安装后设置 JAVA_HOME 环境变量,并将 bin 目录加入 PATH。 | Spring Boot 3.x 要求 JDK 17+。 |
| Apache Maven | 3.5 或以上 | 项目构建与依赖管理 | 下载解压,配置 MAVEN_HOME 和 PATH,修改 settings.xml 设置仓库镜像(如阿里云)。 | 推荐使用国内镜像加速依赖下载。 |
| Gradle | 6.8 或以上 | 替代 Maven 的构建工具 | 安装后配置 GRADLE_HOME 和 PATH,或使用 Wrapper(gradlew)。 | Gradle 脚本更灵活,但学习成本略高。 |
| IDE | IntelliJ IDEA / Eclipse / Spring Tool Suite (STS) | 代码编写与调试 | 推荐使用 IDEA 或 STS,内置 Spring Boot 支持。 | 安装 Lombok 插件以支持注解处理器。 |
1.3 创建第一个 Spring Boot 项目(使用 Spring Initializr)
| 步骤 | 操作 | 说明 | 注意事项 |
|---|
| 1. 访问 Initializr | 打开 https://start.spring.io | 官方项目生成器,支持 Web 和 IDE 集成。 | 可选择 Maven 或 Gradle 构建方式。 |
| 2. 选择项目元数据 | 填写 Group(如 com.example)、Artifact(如 demo)、Name、Description、Package 名称 | 定义项目的坐标和包结构。 | Package 名称决定项目根包路径。 |
| 3. 选择 Spring Boot 版本 | 推荐选择最新稳定版(如 3.3.x) | 版本影响依赖兼容性。 | 若需 JDK 8,应选择 Spring Boot 2.7.x。 |
| 4. 添加依赖 | 至少选择 Spring Web | 添加 Web 开发支持(内含 Tomcat + Spring MVC)。 | 可同时添加 Lombok、Actuator、Spring Data JPA 等。 |
| 5. 生成项目 | 点击 “Generate” 下载 ZIP 包 | 解压后导入 IDE。 | 解压路径避免中文或空格。 |
| 6. 导入 IDE | 使用 IDEA 或 Eclipse 打开项目 | Maven 会自动下载依赖。 | 首次导入可能耗时较长,请耐心等待。 |
1.4 项目结构解析与核心配置文件
| 目录/文件 | 路径 | 用途 | 注意事项 |
|---|
src/main/java | Java 源码目录 | 存放所有 Java 类文件。 | 主启动类默认在此目录下。 |
src/main/resources | 资源文件目录 | 存放配置文件、静态资源、模板等。 | application.properties 或 application.yml 必须在此目录。 |
src/test/java | 测试代码目录 | 存放单元测试和集成测试类。 | 测试类通常与主类结构对应。 |
pom.xml | 项目根目录 | Maven 构建配置文件,定义依赖、插件、版本等。 | 不要手动修改依赖版本,除非明确需要。 |
application.properties | src/main/resources | 主配置文件,键值对格式。 | 优先级低于 application.yml(若同时存在)。 |
application.yml | src/main/resources | YAML 格式配置文件,结构更清晰。 | 缩进敏感,必须使用空格,不能用 Tab。 |
static | src/main/resources/static | 存放静态资源(CSS、JS、图片等)。 | 可通过 HTTP 直接访问,如 /js/app.js。 |
templates | src/main/resources/templates | 存放模板文件(Thymeleaf、Freemarker 等)。 | 用于服务端渲染页面。 |
1.5 启动类与自动配置原理简介
| 概念 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
@SpringBootApplication | @SpringBootApplication | 组合注解,启用自动配置、组件扫描和配置类功能。 | @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } | 必须放在根包下,以便扫描所有子包组件。 |
SpringApplication.run() | SpringApplication.run(Application.class, args) | 启动 Spring Boot 应用,初始化 IoC 容器。 | 同上 | 返回 ApplicationContext 对象,可用于获取 Bean。 |
| 自动配置原理 | @EnableAutoConfiguration + spring.factories | Spring Boot 启动时扫描 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,加载自动配置类。 | 无需手动编写 | 配置类使用 @ConditionalOnXXX 控制生效条件。 |
| 组件扫描 | @ComponentScan(包含在 @SpringBootApplication 中) | 自动发现并注册标注了 @Component、@Service 等的类为 Bean。 | 默认扫描启动类所在包及其子包 | 可通过 basePackages 指定扫描路径。 |
第二章:核心配置与属性管理
2.1 application.properties 与 application.yml 基础语法
| 配置类型 | 语法格式 | 用途 | 示例 | 注意事项 |
|---|
| properties 键值对 | key=value | 定义单个配置项 | server.port=8080
spring.application.name=myapp | 不支持层级结构,重复键会覆盖。 |
| YAML 对象结构 | key:
subkey: value | 表示层级结构,更易读 | server:
port: 8080
spring:
application:
name: myapp | 缩进必须使用空格,层级对齐。 |
| YAML 数组 | key:
- item1
- item2 | 定义列表类型配置 | spring.profiles.active:
- dev
- db | 列表项前用 - 和空格。 |
| 占位符引用 | ${key:default} | 引用其他属性值,支持默认值 | server.port=${PORT:8080} | 常用于环境变量注入。 |
| 多文档块(YAML) | --- | 分隔多个独立文档 | spring:
profiles: dev
datasource:
url: jdbc:h2:mem:dev
---
spring:
profiles: prod
datasource:
url: jdbc:mysql://prod-db | 用于多 Profile 配置。 |
2.2 自定义配置属性绑定(@ConfigurationProperties)
| 方法/注解 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
@ConfigurationProperties | @ConfigurationProperties(prefix = "custom") | 将配置文件中以指定前缀开头的属性绑定到 Java Bean。 | @Component @ConfigurationProperties(prefix = "app") public class AppProperties { private String name; private int timeout; // getter/setter } | 必须提供 setter 方法(或使用构造函数绑定)。 |
@EnableConfigurationProperties | @EnableConfigurationProperties(AppProperties.class) | 启用配置属性绑定功能,注册为 Bean。 | 添加在启动类或配置类上 | 若使用 @Component,则无需此注解。 |
| 松散绑定(Relaxed Binding) | 支持 kebab-case、snake_case、camelCase | 配置名与字段名不完全一致时自动匹配 | app.my-property 对应 myProperty 字段 | 推荐使用 kebab-case 在配置文件中。 |
| 类型安全 | 支持基本类型、集合、嵌套对象 | 提供编译时类型检查 | 可绑定 List、Map<String, String> 等 | 集合建议使用 List 而非数组。 |
2.3 多环境配置(Profile)管理
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
| Profile 配置文件命名 | application-{profile}.properties/yml | 为不同环境提供独立配置。 | application-dev.yml
application-prod.yml | 主文件 application.yml 为默认配置。 |
| 激活 Profile | spring.profiles.active=dev | 指定当前激活的环境。 | 在 application.yml 中:spring:
profiles:
active: dev | 可同时激活多个,用逗号分隔。 |
@Profile | @Profile("dev") | 注解方式控制 Bean 在特定环境下注册。 | @Configuration @Profile("dev") public class DevConfig { ... } | 可用于类或方法级别。 |
| 环境变量激活 | JAVA_OPTS=-Dspring.profiles.active=prod | 通过命令行或环境变量设置。 | java -jar app.jar --spring.profiles.active=test | 优先级高于配置文件。 |
| Profile 分组 | spring.profiles.group.dev=dev,common | 将多个 Profile 组合成逻辑组。 | spring:
profiles:
group:
dev: dev,common | Spring Boot 2.4+ 支持。 |
2.4 外部化配置优先级详解
| 配置来源 | 优先级(从高到低) | 说明 | 示例 | 注意事项 |
|---|
| 命令行参数 | 1 | 最高优先级,可覆盖所有其他配置。 | java -jar app.jar --server.port=9090 | 多个参数用空格分隔。 |
SPRING_APPLICATION_JSON | 2 | 环境变量中内联 JSON 配置。 | export SPRING_APPLICATION_JSON='{"server":{"port":8080}}' | 适用于容器化部署。 |
| ServletConfig 初始化参数 | 3 | Servlet 容器级别的初始化参数。 | web.xml 中 <init-param> | 较少使用。 |
| JVM 系统属性 | 4 | System.setProperty() 或 -D 参数。 | -Dserver.port=8081 | |
| 操作系统环境变量 | 5 | 系统级环境变量,支持下划线命名转换。 | SERVER_PORT=8082 | 推荐使用大写下划线格式。 |
application-{profile}.yml | 6 | 特定环境的配置文件。 | application-dev.yml | |
application.yml | 7 | 主配置文件。 | application.yml | |
@PropertySource | 8 | 注解方式加载的属性文件。 | @PropertySource("classpath:db.properties") | 优先级较低。 |
默认属性(通过 SpringApplication.setDefaultProperties) | 9 | 最低优先级,作为最终 fallback。 | SpringApplication app = new SpringApplication(App.class); app.setDefaultProperties(...); | |
2.5 配置加密与敏感信息处理(可选)
| 方法 | 说明 | 工具/库 | 注意事项 |
|---|
| 环境变量存储 | 将数据库密码等敏感信息通过环境变量注入。 | Docker secrets、K8s Secrets、CI/CD 环境变量 | 避免硬编码在配置文件中。 |
| Jasypt 加密 | 使用 Jasypt-Spring Boot 实现配置项加解密。 | jasypt-spring-boot-starter | 需配置加密密码(PBE)和算法。 |
| Spring Cloud Config + Vault | 集成 HashiCorp Vault 实现集中化密钥管理。 | spring-cloud-starter-vault-config | 适用于微服务架构。 |
| AWS KMS / Azure Key Vault | 云平台提供的密钥管理服务。 | 云厂商 SDK | 安全性高,适合云原生应用。 |
| 不提交敏感文件 | 将包含敏感信息的配置文件加入 .gitignore。 | .gitignore | 如 application-prod.yml 不应提交到 Git。 |
第三章:Web 开发基础
3.1 Spring MVC 架构概述
| 概念 | 说明 | 注意事项 |
|---|
| DispatcherServlet | 前端控制器,接收所有请求并分发到处理器。 | 是 Spring MVC 的核心,由 Spring Boot 自动配置。 |
| HandlerMapping | 根据请求 URL 查找对应的处理器(Controller 方法)。 | 支持多种映射策略(如注解、XML)。 |
| Controller | 处理具体业务逻辑,返回模型和视图或数据。 | 使用 @Controller 或 @RestController 注解。 |
| ModelAndView | 封装模型数据和视图名(传统 MVC 模式)。 | 在 REST API 中较少使用。 |
| ViewResolver | 解析视图名,定位实际视图(如 JSP、Thymeleaf)。 | REST 场景下通常不需要。 |
| Model | 用于在控制器和视图之间传递数据。 | 可通过 Model、ModelMap 或 ModelAndView 添加属性。 |
| RESTful 支持 | @RestController 自动序列化返回对象为 JSON。 | 结合 @ResponseBody 使用。 |
3.2 控制器开发(@Controller 与 @RestController)
| 注解 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
@Controller | @Controller | 标识一个类为 Spring MVC 控制器,通常返回视图名。 | @Controller public class HomeController { @GetMapping("/") public String home(Model model) { model.addAttribute("msg", "Hello"); return "index"; } } | 需配合视图技术使用。 |
@RestController | @RestController | 组合注解(@Controller + @ResponseBody),返回数据而非视图。 | @RestController public class ApiController { @GetMapping("/api") public Map<String, String> getData() { return Collections.singletonMap("data", "value"); } } | 默认所有方法返回 JSON/XML。 |
@ResponseBody | @ResponseBody | 标注方法,表示返回值直接写入响应体。 | @Controller public class DataController { @GetMapping("/json") @ResponseBody public User getUser() { ... } } | 可用于单个方法,而非整个类。 |
@RequestBody | @RequestBody | 将请求体 JSON 自动反序列化为 Java 对象。 | public ResponseEntity<?> createUser(@RequestBody User user) { ... } | 常用于 POST/PUT 请求。 |
3.3 请求映射(@RequestMapping 及其衍生注解)
| 注解 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
@RequestMapping | @RequestMapping(value = "/users", method = RequestMethod.GET) | 通用映射,支持所有 HTTP 方法。 | @RequestMapping("/home") public String home() { ... } | 可标注类或方法。 |
@GetMapping | @GetMapping("/users") | 映射 GET 请求。 | @GetMapping("/{id}") public User getUser(@PathVariable Long id) { ... } | 语义更清晰,推荐使用。 |
@PostMapping | @PostMapping("/users") | 映射 POST 请求。 | @PostMapping public User createUser(@RequestBody User user) { ... } | 用于创建资源。 |
@PutMapping | @PutMapping("/users/{id}") | 映射 PUT 请求。 | @PutMapping("/{id}") public User updateUser(@PathVariable Long id, @RequestBody User user) { ... } | 用于更新资源。 |
@DeleteMapping | @DeleteMapping("/users/{id}") | 映射 DELETE 请求。 | @DeleteMapping("/{id}") public void deleteUser(@PathVariable Long id) { ... } | 用于删除资源。 |
@PatchMapping | @PatchMapping("/users/{id}") | 映射 PATCH 请求(部分更新)。 | @PatchMapping("/{id}") public User partialUpdate(@PathVariable Long id, @RequestBody Map<String, Object> updates) { ... } | 不常用,但符合 REST 规范。 |
3.4 请求参数接收(@RequestParam、@PathVariable、@RequestBody)
| 注解 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
@RequestParam | @RequestParam String name | 获取 URL 查询参数。 | @GetMapping("/search") public String search(@RequestParam String q) { ... } | 默认 required = true,可设 defaultValue。 |
@RequestParam(required=false) | @RequestParam(required = false) | 可选参数。 | @RequestParam(required = false, defaultValue = "1") int page | 若参数不存在,使用默认值。 |
@PathVariable | @PathVariable Long id | 获取路径变量。 | @GetMapping("/{id}") public User get(@PathVariable Long id) { ... } | 路径变量名需与方法参数名一致,或使用 value 指定。 |
@RequestBody | @RequestBody User user | 接收 JSON 请求体并反序列化。 | @PostMapping public User create(@RequestBody User user) { ... } | 需客户端设置 Content-Type: application/json。 |
@RequestHeader | @RequestHeader String userAgent | 获取请求头信息。 | @GetMapping("/info") public String getInfo(@RequestHeader("User-Agent") String agent) { ... } | 可用于鉴权、日志等。 |
@CookieValue | @CookieValue("JSESSIONID") String sessionId | 获取 Cookie 值。 | @GetMapping("/cookie") public String readCookie(@CookieValue String sessionId) { ... } | 注意隐私与安全。 |
3.5 响应处理(@ResponseBody、ResponseEntity、视图解析)
| 方法/类型 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
@ResponseBody | @ResponseBody(方法或类上) | 将返回值序列化为 JSON/XML 写入响应体。 | @ResponseBody @GetMapping("/data") public User getUser() { ... } | @RestController 已隐含此注解。 |
ResponseEntity | ResponseEntity | 封装响应体、状态码、响应头,实现精细控制。 | @GetMapping("/{id}") public ResponseEntity getUser(@PathVariable Long id) { User user = service.findById(id); return ResponseEntity.ok(user); } | 推荐用于需要自定义状态码的场景。 |
| 返回 String | return "index" | 返回视图名,由 ViewResolver 解析为页面。 | @Controller public String home() { return "home"; } | 仅适用于服务端渲染。 |
| 返回视图对象 | return new ModelAndView("view", model) | 同时指定视图和模型数据。 | @GetMapping("/mv") public ModelAndView show() { ModelAndView mv = new ModelAndView("page"); mv.addObject("key", "value"); return mv; } | 传统方式,REST 中不常用。 |
| 状态码设置 | ResponseEntity.status(HttpStatus.CREATED).body(data) | 自定义 HTTP 状态码。 | 创建成功返回 201,无内容返回 204 等。 | 遵循 REST 原则。 |
3.6 静态资源处理与 WebJars
| 资源类型 | 默认路径 | 用途 | 访问方式 | 注意事项 |
|---|
/static | src/main/resources/static | 存放通用静态资源(CSS、JS、图片)。 | http://localhost:8080/app.js | Spring Boot 自动映射。 |
/public | src/main/resources/public | 同 /static,优先级略低。 | 同上 | 可共存,/static 优先。 |
/resources | src/main/resources/resources | 传统资源目录。 | 同上 | |
/META-INF/resources | JAR 内资源 | 其他 JAR 包中的静态资源。 | 自动暴露 | 常用于 WebJars。 |
| WebJars | /webjars/{library}/{version}/file.js | 将前端库(如 jQuery、Bootstrap)打包为 JAR 管理。 | 引入依赖后访问:/webjars/jquery/3.6.0/jquery.js | 简化前端依赖管理。 |
| 自定义静态路径 | spring.web.resources.static-locations=classpath:/custom/ | 修改静态资源查找路径。 | 可添加多个路径,逗号分隔。 | 覆盖默认路径时需包含原有路径。 |
第四章:数据访问与持久化
4.1 Spring Data JPA 概述与基本概念
| 概念名称 | 说明 | 注意事项 |
|---|
| Spring Data JPA | 提供了基于 JPA 的数据访问抽象层,简化了数据库操作。 | 需要配置实体类和 Repository 接口。 |
| JPA | Java Persistence API,用于对象关系映射(ORM),将 Java 对象持久化到关系型数据库中。 | 支持多种数据库,需正确配置 persistence.xml 或使用 Spring Boot 自动配置。 |
| EntityManager | 管理实体的生命周期,执行 CRUD 操作。 | 在 Spring Boot 中通常由框架自动管理,无需手动获取。 |
| JPQL | Java Persistence Query Language,面向对象查询语言,类似于 SQL 但操作的是实体对象而非表。 | 不支持直接使用数据库函数,需通过原生查询实现。 |
| Criteria API | 动态构建类型安全的查询。 | 适合动态条件查询,但相比 JPQL 更复杂。 |
| Transient, Persistent, Detached | 实体的状态:瞬时状态(未被持久化)、托管状态(已持久化且在事务中)、游离状态(已持久化但不在事务中)。 | 正确理解状态转换对性能优化很重要。 |
4.2 实体类定义(@Entity、@Id、@GeneratedValue 等)
| 注解 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
@Entity | @Entity | 标识一个类为实体类,对应数据库中的表。 | @Entity public class User { ... } | 类名默认作为表名,可通过 @Table 修改。 |
@Id | @Id | 标识主键字段。 | @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; | 主键生成策略有多种,默认 AUTO。 |
@GeneratedValue | @GeneratedValue(strategy = GenerationType.IDENTITY) | 定义主键生成策略(AUTO, IDENTITY, SEQUENCE, TABLE)。 | GenerationType.IDENTITY 适用于自增字段。 | 使用 SEQUENCE 时需配置序列。 |
@Column | @Column(name = "user_name", nullable = false) | 映射字段到列,可指定列名、长度、是否允许空等属性。 | @Column(nullable = false, length = 50) private String name; | 若字段名与列名一致,可省略 name 属性。 |
@Table | @Table(name = "tbl_user") | 显式指定实体对应的表名。 | @Entity @Table(name = "tbl_user") public class User { ... } | 默认类名为表名,建议保持一致。 |
@Transient | @Transient | 标记字段不进行持久化。 | @Transient private transient String tempData; | 临时字段,不会保存到数据库。 |
4.3 Repository 接口定义与方法命名规则
| 方法/注解 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
CrudRepository | extends CrudRepository<T, ID> | 提供基本的 CRUD 操作接口。 | public interface UserRepository extends CrudRepository<User, Long> { } | 直接继承即可使用预定义方法。 |
PagingAndSortingRepository | extends PagingAndSortingRepository<T, ID> | 继承 CrudRepository 并增加分页排序功能。 | public interface UserRepository extends PagingAndSortingRepository<User, Long> { } | 可实现分页和排序查询。 |
Repository | extends Repository<T, ID> | 最基础的仓库接口,可定制化扩展。 | public interface CustomRepository extends Repository<User, Long> { } | 需自行定义具体方法。 |
| 方法命名规则 | findBy{PropertyName} | 根据实体属性自动生成查询方法。 | List findByName(String name); | 支持 And、Or、Between、LessThan、GreaterThan 等关键字。 |
existsBy{PropertyName} | boolean existsByName(String name); | 判断是否存在符合条件的记录。 | boolean existsByName(String name); | 返回布尔值,检查记录存在性。 |
countBy{PropertyName} | long countByName(String name); | 计算符合条件的记录数。 | long countByName(String name); | 常用于统计。 |
4.4 自定义查询(@Query 注解)
| 方法/注解 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
@Query | @Query("SELECT u FROM User u WHERE u.name = ?1") | 使用 JPQL 编写自定义查询。 | @Query("SELECT u FROM User u WHERE u.name = ?1") List findUsersByName(String name); | 参数从 1 开始编号,支持位置占位符。 |
@Param | @Param("name") String name | 使用命名参数代替位置参数。 | @Query("SELECT u FROM User u WHERE u.name = :name") List findUsersByName(@Param("name") String name); | 命名参数更易读,推荐使用。 |
nativeQuery = true | @Query(value = "SELECT * FROM users WHERE name = ?", nativeQuery = true) | 执行原生 SQL 查询。 | @Query(value = "SELECT * FROM users WHERE name = ?", nativeQuery = true) List findUsersByName(String name); | 需注意实体与表结构的一致性。 |
@Modifying | @Modifying | 执行更新或删除操作。 | @Modifying @Query("UPDATE User u SET u.status = 'active' WHERE u.id = ?1") void activateUser(Long id); | 需结合 @Transactional 注解使用。 |
4.5 分页与排序(Pageable、Page)
| 方法/类型 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
Pageable | Pageable pageable | 表示分页请求参数,包含页码、每页大小、排序信息。 | Page findAll(Pageable pageable); | 可使用 PageRequest.of(pageNumber, pageSize, Sort.by("field")) 创建实例。 |
Page | Page | 封装分页结果,包含总记录数、当前页数据、是否有下一页等信息。 | Page page = userRepository.findAll(PageRequest.of(0, 10)); | 总记录数通过额外查询获取,影响性能。 |
Sort | Sort sort | 表示排序规则,可按多个字段排序。 | Sort sort = Sort.by(Sort.Direction.ASC, "name"); | 复杂排序可通过 Sort.by() 链式调用。 |
PageRequest.of() | PageRequest.of(int page, int size, Sort sort) | 创建分页请求对象。 | PageRequest.of(0, 10, Sort.by("age").descending()); | 页码从 0 开始计数。 |
Slice | Slice | 类似于 Page,但只返回当前页的数据,不提供总记录数。 | Slice slice = userRepository.findSlice(PageRequest.of(0, 10)); | 更轻量,适合大数据集。 |
4.6 MyBatis 集成与使用(可选)
| 方法/注解 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
| MyBatis-Spring-Boot-Starter | org.mybatis.spring.boot:mybatis-spring-boot-starter | 引入 MyBatis 支持,简化集成步骤。 | 添加依赖后自动配置 Mapper 扫描。 | 需配置 application.yml 或 application.properties。 |
@MapperScan | @MapperScan("com.example.mapper") | 指定扫描 Mapper 接口的包路径。 | @SpringBootApplication @MapperScan("com.example.mapper") public class Application { ... } | 可避免每个 Mapper 加 @Mapper 注解。 |
@Select | @Select("SELECT * FROM users WHERE id = #{id}") | 定义查询语句。 | @Select("SELECT * FROM users WHERE id = #{id}") User getUserById(@Param("id") Integer id); | 使用 #{} 占位符传递参数。 |
@Insert | @Insert("INSERT INTO users (name, age) VALUES (#{name}, #{age})") | 插入数据。 | @Insert("INSERT INTO users (name, age) VALUES (#{name}, #{age})") void insertUser(User user); | 注意事务管理。 |
@Update | @Update("UPDATE users SET age = #{age} WHERE id = #{id}") | 更新数据。 | @Update("UPDATE users SET age = #{age} WHERE id = #{id}") void updateUserAge(@Param("id") Integer id, @Param("age") Integer age); | 更新操作可能影响多行,需谨慎处理。 |
@Delete | @Delete("DELETE FROM users WHERE id = #{id}") | 删除数据。 | @Delete("DELETE FROM users WHERE id = #{id}") void deleteUserById(Integer id); | 删除前确认逻辑删除还是物理删除。 |
4.7 数据源配置与连接池管理
| 方法/属性 | 语法 | 用途 | 示例 | 注意事项 |
|---|
spring.datasource.url | spring.datasource.url=jdbc:mysql://localhost:3306/mydb | 配置数据库连接 URL。 | jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC | 注意时区设置,确保与服务器一致。 |
spring.datasource.username | spring.datasource.username=root | 数据库用户名。 | root | 必填项,根据实际环境配置。 |
spring.datasource.password | spring.datasource.password=password | 数据库密码。 | password | 考虑使用环境变量或加密存储。 |
spring.datasource.driver-class-name | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver | JDBC 驱动类名。 | com.mysql.cj.jdbc.Driver | 根据使用的数据库选择正确的驱动。 |
| HikariCP | spring.datasource.hikari.maximum-pool-size=10 | 配置连接池参数(HikariCP 是默认连接池)。 | maximum-pool-size 控制最大连接数。 | 连接池参数对性能至关重要,需根据应用负载调整。 |
| C3P0 | spring.datasource.type=com.mchange.v2.c3p0.ComboPooledDataSource | 使用其他连接池(如 C3P0)。 | 需添加相应依赖并配置 spring.datasource.* 属性。 | 不推荐,HikariCP 性能更优。 |
| Druid | spring.datasource.type=com.alibaba.druid.pool.DruidDataSource | 使用阿里开源的 Druid 连接池。 | 需添加 druid-spring-boot-starter 依赖,并配置监控页面等高级功能。 | Druid 提供丰富的监控与安全特性。 |
第五章:RESTful API 设计与开发
5.1 REST 架构风格与设计原则
| 概念 | 说明 | 注意事项 |
|---|
| REST(Representational State Transfer) | 一种基于 HTTP 协议的软件架构风格,用于设计网络应用程序的接口。 | 不是协议或标准,而是一组约束和原则。 |
| 资源(Resource) | 系统中可被访问的数据实体,如用户、订单等,通过 URI 唯一标识。 | 每个资源应有唯一的 URL,如 /users/1。 |
| 统一接口(Uniform Interface) | 使用标准 HTTP 方法(GET、POST、PUT、DELETE)操作资源。 | 遵循语义:GET 查询、POST 创建、PUT 更新、DELETE 删除。 |
| 无状态(Stateless) | 每个请求包含处理所需全部信息,服务器不保存客户端上下文。 | 提高可伸缩性,但可能增加请求数据量。 |
| 表述性状态转移 | 客户端通过获取资源的”表述”(如 JSON)来了解其当前状态,并通过操作改变服务器状态。 | 响应应包含足够信息指导客户端下一步操作(HATEOAS 可选)。 |
| HATEOAS(Hypermedia as the Engine of Application State) | 响应在返回数据的同时提供相关链接,驱动客户端状态迁移。 | 如返回 { "id": 1, "name": "John", "links": [ { "rel": "self", "href": "/users/1" } ] } |
| URI 设计规范 | 使用名词表示资源,避免动词;使用复数形式;层级清晰。 | 推荐:/users, /users/1/orders;避免:/getUser, /deleteUser |
5.2 使用 Spring Boot 构建 REST API
| 注解/组件 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
@RestController | @RestController | 标识类为 REST 控制器,所有方法默认返回数据(JSON/XML)。 | 等价于 @Controller + @ResponseBody。 | |
@RequestMapping | @RequestMapping("/users") | 类级别映射基础路径。 | @RequestMapping("/users") public class UserController { ... } | 可省略,直接使用衍生注解。 |
@GetMapping | @GetMapping("/{id}") | 处理 GET 请求,获取资源。 | @GetMapping("/{id}") public User findById(@PathVariable Long id) { ... } | 用于查询操作。 |
@PostMapping | @PostMapping | 处理 POST 请求,创建资源。 | 建议创建成功返回 201 Created 和 Location 头。 | 返回 ResponseEntity.created(URI.create("/users/" + saved.getId())).body(saved) |
@PutMapping | @PutMapping("/{id}") | 处理 PUT 请求,更新整个资源。 | @PutMapping("/{id}") public User update(@PathVariable Long id, @RequestBody User user) { ... } | 应替换整个资源,客户端提供完整对象。 |
@DeleteMapping | @DeleteMapping("/{id}") | 处理 DELETE 请求,删除资源。 | 删除成功建议返回 204 No Content。 | 使用 ResponseEntity.noContent().build() |
@PatchMapping | @PatchMapping("/{id}") | 处理 PATCH 请求,部分更新资源。 | @PatchMapping("/{id}") public User partialUpdate(@PathVariable Long id, @RequestBody Map<String, Object> updates) { ... } | 需手动处理字段更新逻辑。 |
ResponseEntity | ResponseEntity | 封装响应体、状态码、响应头,实现精细控制。 | 见上文 POST 示例 | 推荐用于需要自定义状态码的场景。 |
5.3 请求与响应数据格式(JSON/XML)
| 特性 | 说明 | 配置方式/代码示例 | 注意事项 |
|---|
| 默认支持 JSON | Spring Boot 默认使用 Jackson 实现 Java 对象与 JSON 的序列化/反序列化。 | 引入 spring-boot-starter-web 即自动包含 Jackson。 | 无需额外配置即可处理 JSON。 |
@RequestBody | 将请求体中的 JSON 自动反序列化为 Java 对象。 | @PostMapping("/users") public User create(@RequestBody User user) { ... } | 请求头需设置 Content-Type: application/json。 |
@ResponseBody | 将返回对象自动序列化为 JSON 响应。 | 方法返回对象即可,@RestController 已隐含。 | 响应头自动设置 Content-Type: application/json。 |
| 支持 XML | 添加 Jackson XML 依赖后可支持 XML 格式。 | 依赖:com.fasterxml.jackson.dataformat:jackson-dataformat-xml;请求头 Accept: application/xml | 需 Java 类有默认构造函数和 getter/setter。 |
@JacksonXmlRootElement | 指定 XML 根元素名称。 | @JacksonXmlRootElement(localName = "user") public class User { ... } | 用于 XML 序列化。 |
| 内容协商(Content Negotiation) | 根据请求头 Accept 或参数决定返回 JSON 还是 XML。 | 访问 /users?format=xml 或设置 Accept: application/xml | 可通过 spring.mvc.contentnegotiation.favor-parameter=true 启用参数模式。 |
| Jackson 注解控制序列化 | 使用 @JsonIgnore, @JsonProperty, @JsonFormat 等定制 JSON 输出。 | @JsonIgnore private String password; 和 @JsonFormat(pattern = "yyyy-MM-dd") private LocalDate birthday; | 提高数据安全性和格式控制。 |
5.4 全局异常处理(@ControllerAdvice、@ExceptionHandler)
| 注解/方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
@ControllerAdvice | @ControllerAdvice | 定义全局异常处理器,应用于所有控制器。 | @ControllerAdvice public class GlobalExceptionHandler { ... } | 可指定包范围:@ControllerAdvice(basePackages = "com.example.controller") |
@ExceptionHandler | @ExceptionHandler(ResourceNotFoundException.class) | 捕获指定类型的异常并返回统一响应。 | 返回 ResponseEntity.status(HttpStatus.NOT_FOUND).body(error) | 可处理多种异常类型。 |
| 处理 Validation 异常 | @ExceptionHandler(MethodArgumentNotValidException.class) | 捕获参数校验失败异常。 | 遍历 ex.getBindingResult().getFieldErrors() 收集错误信息 | 需结合 @Valid 使用。 |
| 处理通用异常 | @ExceptionHandler(Exception.class) | 捕获未预期的异常,避免暴露堆栈信息。 | 返回 500 内部错误,记录日志 | 应放在最后,避免屏蔽特定异常处理。 |
| 自定义异常类 | public class ResourceNotFoundException extends RuntimeException | 定义业务异常,便于识别和处理。 | public class ResourceNotFoundException extends RuntimeException { public ResourceNotFoundException(String message) { super(message); } } | 继承 RuntimeException 可避免强制捕获。 |
| 返回结构统一 | 定义 ErrorResponse 类封装错误信息。 | public class ErrorResponse { private String code; private String message; } | 提高 API 可用性和前端处理一致性。 | |
5.5 API 文档生成(Springdoc OpenAPI / Swagger)
| 组件/注解 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
| springdoc-openapi 依赖 | Maven: org.springdoc:springdoc-openapi-starter-webmvc-ui | 集成 OpenAPI 3 和 Swagger UI。 | Spring Boot 3 推荐使用 starter-webmvc-ui | 替代已停更的 springfox-swagger。 |
| 访问 Swagger UI | 启动后访问 http://localhost:8080/swagger-ui.html | 图形化 API 文档界面,支持在线测试。 | 路径可配置:springdoc.swagger-ui.path=/api-docs | 生产环境建议禁用或加权限。 |
@Operation | @Operation(summary = "获取用户详情", description = "根据ID查询用户信息") | 描述接口方法的用途。 | 用于 @GetMapping 等方法上 | 提升文档可读性。 |
@Parameter | @Parameter(description = "用户ID", required = true) | 描述参数信息。 | 用于 @PathVariable、@RequestParam 等参数前 | 可用于路径、查询参数等。 |
@Schema | @Schema(description = "用户实体") | 描述数据模型。 | @Schema(description = "用户信息") public class User { @Schema(description = "用户唯一ID", example = "1") private Long id; } | 用于 POJO 类或字段。 |
@ApiResponse | @ApiResponse(responseCode = "200", description = "成功获取用户") | 描述响应状态码和含义。 | 用于 @Operation 的 responses 属性 | 明确接口契约。 |
| 分组配置 | @Tag(name = "用户管理") | 将接口分组显示。 | @Tag(name = "用户管理", description = "用户增删改查接口") public class UserController { ... } | 在 Swagger UI 中按组展示。 |
| 安全方案 | @SecurityScheme | 定义认证方式(如 JWT)。 | @SecurityScheme(name = "bearerAuth", type = SecuritySchemeType.HTTP, scheme = "bearer", bearerFormat = "JWT") | 配合 @SecurityRequirement 使用。 |
第六章:依赖注入与 Bean 管理
6.1 IoC 容器与依赖注入(DI)原理
| 概念 | 说明 | 注意事项 |
|---|
| IoC(Inversion of Control,控制反转) | 将对象的创建和管理权从程序代码中转移到外部容器(Spring 容器),实现解耦。 | 传统方式由 new 创建对象,IoC 由容器注入,降低耦合度。 |
| DI(Dependency Injection,依赖注入) | IoC 的实现方式之一,容器在运行时将依赖对象”注入”到目标对象中。 | 常见注入方式:构造器注入、Setter 注入、字段注入。 |
| Spring IoC 容器 | 核心是 ApplicationContext 接口,负责 Bean 的生命周期管理、配置、依赖解析。 | 常用实现类:AnnotationConfigApplicationContext(Java 配置)、ClassPathXmlApplicationContext(XML 配置)。 |
| Bean | 由 Spring IoC 容器管理的 Java 对象,也称为 Spring 组件。 | 所有被 @Component、@Service 等注解标记的类实例都是 Bean。 |
| 配置元数据 | 容器如何创建和装配 Bean 的信息来源,可以是注解、Java 配置类或 XML 文件。 | Spring Boot 推荐使用注解和 Java 配置。 |
| 依赖查找 vs 依赖注入 | 依赖查找是主动从容器获取 Bean(如 context.getBean()),依赖注入是被动接收。 | 推荐使用依赖注入,更符合 IoC 理念。 |
6.2 Bean 的定义与注册(@Component、@Service、@Repository、@Controller)
| 注解 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
@Component | @Component | 通用注解,标识一个类为 Spring 管理的组件,可被自动扫描并注册为 Bean。 | @Component public class EmailService { ... } | 是其他特定注解的元注解。 |
@Service | @Service | 用于业务逻辑层(Service 层)的类,语义更明确。 | @Service public class UserService { @Autowired private UserRepository userRepository; } | 与 @Component 功能相同,但层次清晰。 |
@Repository | @Repository | 用于数据访问层(DAO/Repository 层),能自动捕获持久层异常并转换为 Spring 的 DataAccessException。 | @Repository public class UserRepository { ... } | 推荐用于数据访问类。 |
@Controller | @Controller | 用于表现层(Web 层),结合 Spring MVC 处理 HTTP 请求。 | @Controller public class UserController { ... } | 通常返回视图名。 |
@RestController | @RestController | 用于 RESTful Web 服务,等价于 @Controller + @ResponseBody。 | @RestController public class ApiUserController { ... } | 返回数据(如 JSON),不返回视图。 |
@ComponentScan | @ComponentScan(basePackages = "com.example") | 启用组件扫描,自动发现并注册标注了上述注解的类为 Bean。 | @SpringBootApplication 已包含此注解,默认扫描启动类所在包及其子包。 | 无需手动配置,除非包结构特殊。 |
6.3 @Autowired 与 @Qualifier 注解详解
| 注解/方式 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
@Autowired | @Autowired | 自动装配 Bean,可标注在字段、构造器、Setter 方法或参数上。 | @Service public class UserService { @Autowired private UserRepository userRepository; } | 默认按类型(byType)匹配。 |
| 构造器注入 | @Autowired 构造器 | 推荐方式,确保依赖不可变且不为 null。 | @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; } | Spring 4.3+ 若类只有一个构造器,@Autowired 可省略。 |
| Setter 注入 | @Autowired setter 方法 | 用于可选依赖或需要运行时更改依赖的场景。 | @Autowired public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } | 依赖可变,不推荐作为主要方式。 |
| 字段注入 | @Autowired 字段 | 直接在字段上使用,代码简洁但不利于测试和不可变性。 | @Autowired private EmailService emailService; | 不推荐,违反封装原则。 |
@Qualifier | @Qualifier("specificBean") | 当存在多个同类型 Bean 时,指定具体 Bean 的名称。 | @Autowired @Qualifier("emailService") private MessageService service; | 必须与 @Autowired 配合使用。 |
required = false | @Autowired(required = false) | 指定依赖为可选,若找不到 Bean 不报错。 | @Autowired(required = false) private OptionalService optionalService; | 若未找到,字段为 null,需判空处理。 |
6.4 Bean 的作用域(@Scope)
| 作用域 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
| singleton | @Scope("singleton") 或默认 | 每个 Spring IoC 容器中仅存在一个 Bean 实例,为默认作用域。 | @Service @Scope("singleton") public class UserService { ... } | 多数 Bean 使用此作用域。 |
| prototype | @Scope("prototype") | 每次请求 Bean 时都创建一个新实例。 | @Component @Scope("prototype") public class RequestTracker { ... } | 适合有状态的 Bean。 |
| request | @Scope("request") | 每个 HTTP 请求创建一个实例,仅在 Web 环境有效。 | @Component @Scope("request") public class UserSessionData { ... } | 实例生命周期与请求一致。 |
| session | @Scope("session") | 每个 HTTP 会话创建一个实例,仅在 Web 环境有效。 | @Component @Scope("session") public class UserPreferences { ... } | 适合存储用户会话数据。 |
| application | @Scope("application") | 每个 ServletContext 生命周期内创建一个实例,仅在 Web 环境有效。 | @Component @Scope("application") public class AppSettings { ... } | 类似于 singleton,但绑定到 ServletContext。 |
| websocket | @Scope("websocket") | 每个 WebSocket 会话创建一个实例,仅在 WebSocket 环境有效。 | @Component @Scope("websocket") public class WebSocketSessionData { ... } | 较少使用。 |
| 自定义作用域 | 实现 Scope 接口并注册 | 满足特殊生命周期需求。 | 需通过 ConfigurableBeanFactory.registerScope() 注册。 | 高级用法,一般无需自定义。 |
6.5 Bean 的生命周期回调(@PostConstruct、@PreDestroy)
| 方法/注解 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
@PostConstruct | @PostConstruct | 标注在方法上,Bean 初始化完成后执行,用于执行初始化逻辑。 | @Component public class DatabaseInitializer { @PostConstruct public void init() { System.out.println("数据库连接初始化..."); } } | 方法必须为 public void,无参数,不能是静态。 |
@PreDestroy | @PreDestroy | 标注在方法上,Bean 销毁前执行,用于释放资源。 | @Component public class ConnectionPool { @PreDestroy public void cleanup() { System.out.println("关闭数据库连接池..."); } } | 仅在 singleton Bean 且容器正常关闭时调用。 |
InitializingBean 接口 | implements InitializingBean | 实现 afterPropertiesSet() 方法,替代 @PostConstruct。 | public class MyBean implements InitializingBean { @Override public void afterPropertiesSet() { ... } } | 不推荐,与 Spring API 耦合。 |
DisposableBean 接口 | implements DisposableBean | 实现 destroy() 方法,替代 @PreDestroy。 | public class MyBean implements DisposableBean { @Override public void destroy() { ... } } | 不推荐,与 Spring API 耦合。 |
| 自定义初始化方法 | @Bean(initMethod = "init") | 在 @Bean 注解中指定初始化方法。 | @Bean(initMethod = "start") public MyService myService() { return new MyService(); } | 适用于第三方类无法加注解时。 |
| 自定义销毁方法 | @Bean(destroyMethod = "shutdown") | 在 @Bean 注解中指定销毁方法。 | @Bean(destroyMethod = "shutdown") public DataSource dataSource() { ... } | 同上。 |
| 执行顺序 | 构造器 → 依赖注入 → @PostConstruct / InitializingBean → 使用 → @PreDestroy / DisposableBean | Bean 生命周期各阶段执行顺序。 | 确保资源在使用前初始化,销毁前释放。 | @PostConstruct 在依赖注入完成后执行。 |
第七章:AOP 与切面编程
7.1 AOP 核心概念(切面、连接点、通知、切入点)
| 概念 | 说明 | 示例 |
|---|
| AOP(Aspect-Oriented Programming) | 面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护,用于解耦横切关注点(如日志、事务)。 | 将日志记录从业务代码中分离。 |
| 切面(Aspect) | 横切关注点的模块化,通常是一个类,包含多个通知和切入点。 | LoggingAspect 类用于统一处理日志。 |
| 连接点(Join Point) | 程序执行过程中能够插入切面的点,如方法调用、异常抛出等。在 Spring AOP 中,仅支持方法执行连接点。 | 任意 public 方法的调用。 |
| 通知(Advice) | 切面在特定连接点上执行的动作(代码逻辑)。 | 在方法执行前打印日志。 |
| 切入点(Pointcut) | 匹配连接点的表达式,定义在哪些连接点上应用通知。 | execution(* com.example.service.*.*(..)) |
| 目标对象(Target Object) | 被一个或多个切面所通知的对象(即被代理的对象)。 | UserService 实例。 |
| 代理(Proxy) | AOP 框架创建的对象,用于实现切面契约(如 JDK 动态代理或 CGLIB 代理)。 | Spring 自动创建代理对象。 |
| 织入(Weaving) | 将切面应用到目标对象并创建代理对象的过程。 | 编译时、类加载时或运行时织入(Spring 使用运行时织入)。 |
7.2 使用 @Aspect 定义切面
| 步骤/注解 | 说明 | 代码示例 | 注意事项 |
|---|
| 添加依赖 | 引入 spring-boot-starter-aop | Maven: org.springframework.boot:spring-boot-starter-aop | Spring Boot 项目只需添加此依赖即可启用 AOP。 |
| 启用 AOP | 使用 @EnableAspectJAutoProxy(Spring Boot 已自动配置) | 无需手动添加,@SpringBootApplication 已包含。 | 一般无需显式声明。 |
| 定义切面类 | 使用 @Aspect 和 @Component 标注类 | @Aspect @Component public class LoggingAspect { ... } | 必须被 Spring 容器管理(如加 @Component)。 |
| 切面优先级 | 使用 @Order 注解控制多个切面的执行顺序 | @Aspect @Component @Order(1) public class SecurityAspect { ... } | 数值越小,优先级越高。 |
7.3 通知类型详解(@Before、@After、@Around、@AfterReturning、@AfterThrowing)
| 通知类型 | 语法 | 用途 | 代码示例 | 执行时机 |
|---|
@Before | @Before("pointcut()") | 在目标方法执行前执行。 | @Before("execution(* com.example.service.*.*(..))") public void logBefore(JoinPoint jp) { System.out.println("方法执行前: " + jp.getSignature()); } | 方法调用前 |
@After | @After("pointcut()") | 在目标方法执行后执行(无论是否抛出异常),类似 finally。 | @After("pointcut()") public void logAfter(JoinPoint jp) { System.out.println("方法执行后: " + jp.getSignature()); } | 方法执行后(正常或异常) |
@AfterReturning | @AfterReturning(pointcut = "pointcut()", returning = "result") | 在目标方法成功执行并返回后执行,可获取返回值。 | @AfterReturning(pointcut = "pointcut()", returning = "result") public void logReturn(JoinPoint jp, Object result) { System.out.println("返回值: " + result); } | 方法正常返回后 |
@AfterThrowing | @AfterThrowing(pointcut = "pointcut()", throwing = "ex") | 在目标方法抛出异常后执行,可捕获异常。 | @AfterThrowing(pointcut = "pointcut()", throwing = "ex") public void logException(JoinPoint jp, Exception ex) { System.out.println("异常: " + ex.getMessage()); } | 方法抛出异常后 |
@Around | @Around("pointcut()") | 环绕通知,最强大的通知类型,可自定义目标方法执行前后逻辑,甚至决定是否执行目标方法。 | @Around("pointcut()") public Object measureTime(ProceedingJoinPoint pjp) throws Throwable { long start = System.currentTimeMillis(); Object result = pjp.proceed(); long duration = System.currentTimeMillis() - start; System.out.println("耗时: " + duration + "ms"); return result; } | 包裹整个方法执行过程 |
7.4 切入点表达式语法
| 表达式类型 | 语法示例 | 说明 |
|---|
execution() | execution(* com.example.service.*.*(..)) | 匹配方法执行,最常用。* 表示任意返回类型,.. 表示任意参数。 |
within() | within(com.example.service.*) | 匹配指定类型内的方法执行。 |
this() | this(com.example.service.UserService) | 匹配代理对象类型为指定类型的 bean。 |
target() | target(com.example.service.UserService) | 匹配目标对象类型为指定类型的 bean。 |
args() | args(java.lang.String, ..) | 匹配参数列表,第一个参数为 String 类型。 |
@target() | @target(org.springframework.stereotype.Service) | 匹配带有指定注解的类。 |
@args() | @args(com.example.annotation.Loggable) | 匹配传入参数带有指定注解的方法。 |
@within() | @within(org.springframework.stereotype.Service) | 匹配带有指定注解的类中的所有方法。 |
@annotation() | @annotation(com.example.annotation.LogExecution) | 匹配被指定注解标注的方法。 |
| 组合表达式 | @annotation(LogExecution) && args(name, ..) | 使用 &&、` |
7.5 AOP 实际应用场景(日志、权限、事务等)
| 应用场景 | 实现方式 | 说明 |
|---|
| 日志记录 | 使用 @Before 和 @AfterReturning 记录方法调用、参数、返回值、耗时。 | 统一记录业务操作日志,便于排查问题。 |
| 性能监控 | 使用 @Around 记录方法执行时间。 | 识别系统性能瓶颈。 |
| 权限校验 | 使用 @Before 拦截方法,检查用户权限。 | 在进入敏感方法前进行安全控制。 |
| 事务管理 | Spring 内置 @Transactional 使用 AOP 实现。 | 声明式事务,无需手动管理提交/回滚。 |
| 缓存管理 | Spring Cache 抽象基于 AOP,使用 @Cacheable 等注解。 | 自动缓存方法结果,提高性能。 |
| 参数校验 | 在方法执行前使用 AOP 检查参数合法性。 | 可结合 JSR-303 注解实现。 |
| 分布式追踪 | 在方法调用前后注入 Trace ID,实现链路追踪。 | 如集成 Sleuth、SkyWalking。 |
第八章:Spring Boot 高级特性
8.1 条件化配置(@Conditional 注解族)
| 注解 | 用途 | 示例 |
|---|
@ConditionalOnClass | 存在指定类时才创建 Bean。 | @Bean @ConditionalOnClass(DataSource.class) public MyDataSourceConfig config() { ... } |
@ConditionalOnMissingClass | 不存在指定类时才创建 Bean。 | 用于兼容不同环境。 |
@ConditionalOnBean | 存在指定 Bean 时才创建当前 Bean。 | @Bean @ConditionalOnBean(name = "dataSource") public JdbcTemplate jdbcTemplate(DataSource ds) { ... } |
@ConditionalOnMissingBean | 不存在指定 Bean 时才创建当前 Bean(防止重复定义)。 | 常用于 Starter 中提供默认实现。 |
@ConditionalOnProperty | 指定配置属性存在且值为 true 时生效。 | @Bean @ConditionalOnProperty(name = "feature.enabled", havingValue = "true") public FeatureService featureService() { ... } |
@ConditionalOnWebApplication | 当前为 Web 应用时生效。 | 区分 Web 和非 Web 环境。 |
@ConditionalOnNotWebApplication | 当前不是 Web 应用时生效。 | |
@Conditional | 自定义条件,实现 Condition 接口。 | public class MyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return "prod".equals(context.getEnvironment().getProperty("spring.profiles.active")); } } |
8.2 自定义 Starter 的开发
| 步骤 | 说明 | 示例 |
|---|
| 命名规范 | 官方 Starter:spring-boot-starter-xxx;第三方:xxx-spring-boot-starter。 | myfeature-spring-boot-starter |
| 创建 autoconfigure 模块 | 包含自动配置类、条件化 Bean、配置属性类。 | MyFeatureAutoConfiguration |
| 创建 starter 模块 | 仅依赖 autoconfigure 模块,简化引入。 | myfeature-spring-boot-starter |
| 自动配置类 | 使用 @Configuration + @Conditional 系列注解。 | @Configuration @ConditionalOnClass(MyService.class) public class MyFeatureAutoConfiguration { @Bean @ConditionalOnMissingBean public MyService myService() { return new MyService(); } } |
| 配置属性 | 使用 @ConfigurationProperties 绑定 application.yml 中的配置。 | @ConfigurationProperties("myfeature") public class MyFeatureProperties { private String host; private int port; // getter/setter } |
| spring.factories | 在 resources/META-INF/spring.factories 中注册自动配置类。 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.MyFeatureAutoConfiguration |
| 测试 | 在 demo 项目中引入 starter,验证自动配置是否生效。 | compileOnly 'org.springframework.boot:spring-boot-autoconfigure-processor' |
8.3 事件驱动模型(ApplicationEvent 与 ApplicationListener)
| 组件 | 说明 | 示例 |
|---|
ApplicationEvent | 事件载体,继承此类定义自定义事件。 | public class UserRegisteredEvent extends ApplicationEvent { private final User user; public UserRegisteredEvent(User user) { super(user); this.user = user; } public User getUser() { return user; } } |
ApplicationListener | 监听特定事件,实现 onApplicationEvent() 方法。 | @Component public class UserEventListener implements ApplicationListener<UserRegisteredEvent> { @Override public void onApplicationEvent(UserRegisteredEvent event) { System.out.println("用户注册: " + event.getUser().getName()); } } |
@EventListener | 更简洁的监听方式,无需实现接口。 | @Component public class UserService { @EventListener public void handleUserRegistration(UserRegisteredEvent event) { sendWelcomeEmail(event.getUser()); } } |
| 发布事件 | 使用 ApplicationEventPublisher 发布事件。 | @Service public class UserService { @Autowired private ApplicationEventPublisher publisher; public void register(User user) { publisher.publishEvent(new UserRegisteredEvent(user)); } } |
| 异步监听 | 使用 @Async 实现事件异步处理。 | @EventListener @Async public void handleAsync(UserRegisteredEvent event) { ... } 需启用 @EnableAsync。 |
8.4 国际化(i18n)支持
| 配置/组件 | 说明 | 示例 |
|---|
| 消息文件 | 按 messages_{locale}.properties 命名,如 messages_en.properties、messages_zh_CN.properties。 | messages_zh_CN.properties:welcome=欢迎, {0} |
MessageSource | Spring 提供的消息解析接口,通常使用 ResourceBundleMessageSource。 | @Bean public MessageSource messageSource() { ResourceBundleMessageSource source = new ResourceBundleMessageSource(); source.setBasename("messages"); source.setDefaultEncoding("UTF-8"); return source; } |
LocaleResolver | 决定当前请求的 Locale,如 AcceptHeaderLocaleResolver(默认)、CookieLocaleResolver。 | @Bean public LocaleResolver localeResolver() { CookieLocaleResolver resolver = new CookieLocaleResolver(); resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE); return resolver; } |
| 切换语言 | 通过 LocaleChangeInterceptor 监听参数(如 ?lang=en)切换语言。 | 注册 LocaleChangeInterceptor 并设置 paramName 为 "lang",在 addInterceptors 中注册。 |
| 使用消息 | 在代码或 Thymeleaf 中使用 #messages 获取国际化文本。 | Java: messageSource.getMessage("welcome", new Object[]{"张三"}, locale);Thymeleaf: <p th:text="#{welcome(${user.name})}">Welcome</p> |
8.5 缓存抽象(@Cacheable、@CacheEvict 等)
| 注解 | 语法 | 用途 | 示例 | 注意事项 |
|---|
@EnableCaching | 标注在配置类上,启用缓存支持。 | @SpringBootApplication 已包含此功能。 | 必须启用才能使用缓存注解。 | |
@Cacheable | @Cacheable("users") | 标注在方法上,执行前检查缓存,命中则返回缓存值,否则执行方法并缓存结果。 | @Cacheable("users") public User findById(Long id) { return userRepository.findById(id); } | key 默认为方法参数,可用 key 属性自定义。 |
@CachePut | @CachePut("users", key = "#user.id") | 方法始终执行,并将结果存入缓存(用于更新)。 | @CachePut("users", key = "#user.id") public User update(User user) { ... } | 不检查缓存,直接执行并更新。 |
@CacheEvict | @CacheEvict("users", key = "#id") | 清除指定缓存条目。 | @CacheEvict("users", key = "#id") public void delete(Long id) { ... } | 可设置 allEntries = true 清空整个缓存区。 |
@Caching | @Caching(evict = { @CacheEvict("users"), @CacheEvict(value = "cache2", key = "#id") }) | 组合多个缓存操作。 | 用于复杂缓存逻辑。 | |
| 缓存管理器 | 如 ConcurrentMapCacheManager(内存)、RedisCacheManager、CaffeineCacheManager。 | 配置 spring.cache.type=redis 自动配置 Redis 缓存。 | 需引入对应依赖。 | |
| SpEL 表达式 | 在注解中使用 SpEL 定义 key、condition 等。 | @Cacheable(value = "users", key = "#id", condition = "#id > 0") | condition 满足时才缓存。 | |
第九章:安全控制
9.1 Spring Security 简介与核心概念
| 概念 | 说明 | 注意事项 |
|---|
| Spring Security | 一个功能强大且高度可定制的身份验证和访问控制框架,用于保护基于 Spring 的应用程序。 | 是 Spring 生态中最主流的安全框架。 |
| 认证(Authentication) | 验证用户身份的过程,即”你是谁”。 | 常见方式:用户名/密码、OAuth2、JWT。 |
| 授权(Authorization) | 在认证通过后,判断用户是否有权限执行某个操作,即”你能做什么”。 | 基于角色(Role)或权限(Authority)控制。 |
SecurityContextHolder | 存储当前安全上下文(包含认证信息)的容器,可通过 SecurityContextHolder.getContext().getAuthentication() 获取当前用户。 | 默认使用 ThreadLocal 存储,确保线程隔离。 |
Authentication 对象 | 表示当前用户的认证信息,包含 principal(主体)、credentials(凭证)、authorities(权限)、authenticated(是否已认证)等。 | 登录成功后由 AuthenticationManager 创建并存入上下文。 |
UserDetails | 载体接口,用于加载用户详细信息(如用户名、密码、权限、是否过期等)。 | 需自定义实现类(如 CustomUserDetails)并由 UserDetailsService 返回。 |
UserDetailsService | 接口,用于根据用户名加载 UserDetails 对象。 | 通常自定义实现,从数据库或其它存储中查询用户信息。 |
PasswordEncoder | 用于密码的加密与验证,防止明文存储。 | 推荐使用 BCryptPasswordEncoder。 |
| Filter Chain | Spring Security 通过一系列过滤器(Filter)实现安全控制,如 UsernamePasswordAuthenticationFilter、FilterSecurityInterceptor 等。 | 请求在到达控制器前需经过多个安全过滤器。 |
9.2 用户认证(Authentication)配置
| 配置项 | 说明 | 代码示例 | 注意事项 |
|---|
| 添加依赖 | 引入 spring-boot-starter-security | <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency> | 启用后所有接口默认受保护。 |
| 安全配置类 | 使用新式配置(推荐 SecurityFilterChain Bean)。 | @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(auth -> auth.requestMatchers("/public/**").permitAll().anyRequest().authenticated()).formLogin(); return http.build(); } } | 新版本推荐使用 SecurityFilterChain Bean。 |
自定义 UserDetailsService | 实现 UserDetailsService 接口,从数据库加载用户。 | @Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) { User user = userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("User not found")); return org.springframework.security.core.userdetails.User .withUsername(user.getUsername()).password(user.getPassword()).authorities("ROLE_USER").build(); } } | 必须返回 UserDetails 实现类。 |
配置 PasswordEncoder | 定义密码编码器 Bean。 | @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } | 密码入库前需加密,登录时自动验证。 |
| 登录页面配置 | 自定义登录页路径、登录处理 URL、成功/失败跳转。 | .formLogin(login -> login.loginPage("/login").loginProcessingUrl("/doLogin").defaultSuccessUrl("/home").failureUrl("/login?error").permitAll()) | loginProcessingUrl 是表单提交的 action。 |
| 注销配置 | 配置注销 URL、成功跳转页。 | .logout(logout -> logout.logoutUrl("/logout").logoutSuccessUrl("/login").permitAll()) | 默认 /logout,POST 请求。 |
9.3 授权(Authorization)与权限控制
| 配置项 | 说明 | 代码示例 | 注意事项 |
|---|
| 基于 URL 的授权 | 使用 authorizeHttpRequests() 配置不同路径的访问权限。 | .authorizeHttpRequests(auth -> auth.requestMatchers("/admin/**").hasRole("ADMIN").requestMatchers("/user/**").hasAnyRole("USER", "ADMIN").requestMatchers(HttpMethod.POST, "/api/**").hasAuthority("WRITE").anyRequest().authenticated()) | 规则按顺序匹配,越精确的路径应越靠前。 |
| 角色与权限 | 角色以 ROLE_ 开头(如 ROLE_ADMIN),权限可自定义(如 USER_READ)。 | hasRole("ADMIN") 等价于 hasAuthority("ROLE_ADMIN")。 | |
| 匿名访问 | 使用 permitAll() 允许未认证用户访问。 | .requestMatchers("/public/**", "/login").permitAll() | 登录页、静态资源通常需放行。 |
| 拒绝访问 | 使用 denyAll() 拒绝所有用户。 | .requestMatchers("/private/**").denyAll() | 用于临时关闭某些接口。 |
| IP 限制 | 限制特定 IP 或 IP 段访问。 | .requestMatchers("/admin/**").hasIpAddress("192.168.1.0/24") | 需在 HttpServletRequest 中获取真实 IP(考虑代理)。 |
9.4 方法级安全(@PreAuthorize、@Secured)
| 注解 | 说明 | 代码示例 | 注意事项 |
|---|
@EnableMethodSecurity | 启用方法级安全注解(Spring Security 5.6+ 推荐)。 | @Configuration @EnableMethodSecurity public class MethodSecurityConfig { } | 替代旧的 @EnableGlobalMethodSecurity。 |
@PreAuthorize | 在方法执行前进行权限检查,支持 SpEL 表达式。 | @PreAuthorize("hasRole('ADMIN')") public void deleteUser(Long id) { ... } 和 @PreAuthorize("#userId == authentication.principal.id") public User getUser(Long userId) { ... } | 可访问方法参数和认证信息。 |
@PostAuthorize | 在方法执行后进行权限检查(较少使用)。 | @PostAuthorize("returnObject.owner == authentication.principal.username") public Document getDocument() { ... } | 方法已执行,仅用于结果校验。 |
@Secured | 基于角色的访问控制,不支持 SpEL。 | @Secured("ROLE_ADMIN") public void adminOnly() { ... } 或 @Secured({"ROLE_USER", "ROLE_ADMIN"}) public void userOrAdmin() { ... } | 语法简单,但功能有限。 |
@RolesAllowed | JSR-250 标准注解,类似 @Secured。 | @RolesAllowed("ADMIN") public void criticalOperation() { ... } | 需启用 @EnableGlobalMethodSecurity(jsr250Enabled = true)。 |
9.5 JWT 集成与无状态认证
| 组件/步骤 | 说明 | 代码示例/配置 | 注意事项 |
|---|
| JWT(JSON Web Token) | 一种无状态的令牌格式,包含 Header、Payload、Signature。 | 用于前后端分离、微服务架构中的身份认证。 | 令牌自包含,服务端无需存储会话。 |
| 添加 JWT 依赖 | 如 io.jsonwebtoken:jjwt-api, jjwt-impl, jjwt-jackson。 | <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version></dependency> | 注意版本兼容性。 |
| JWT 工具类 | 创建和解析 JWT 令牌。 | Jwts.builder().setSubject(username).setIssuedAt(new Date()).setExpiration(...).signWith(SignatureAlgorithm.HS512, SECRET_KEY).compact() | 秘钥需安全存储。 |
| 自定义过滤器 | 继承 OncePerRequestFilter,在每次请求时检查 JWT。 | 提取 token → 验证 → 设置 SecurityContextHolder.getContext().setAuthentication(auth) | 需注册到 SecurityFilterChain 中。 |
| 登录接口 | 验证用户名密码后返回 JWT。 | @PostMapping("/login") public ResponseEntity<JwtResponse> login(@RequestBody LoginRequest request) { ... } | 不使用 Spring Security 默认登录流程。 |
| 放行登录接口 | 在 SecurityFilterChain 中放行 /login。 | .requestMatchers("/login", "/register").permitAll() | 避免登录接口被拦截。 |
第十章:测试
10.1 单元测试(JUnit 5 与 Mockito)
| 组件/注解 | 说明 | 代码示例 | 注意事项 |
|---|
| JUnit 5 | 新一代 Java 测试框架,由 junit-platform、junit-jupiter、junit-vintage 组成。 | @Test void shouldReturnCorrectValue() { Calculator calc = new Calculator(); assertEquals(4, calc.add(2, 2)); } | Spring Boot 2.2+ 默认使用 JUnit 5。 |
@Test | 标记测试方法。 | @Test void myTest() { ... } | 方法必须为 public void,可省略 public(JUnit 5 允许)。 |
| 断言(Assertions) | assertEquals, assertTrue, assertNull, assertThrows 等。 | assertThrows(IllegalArgumentException.class, () -> service.process(null)); | 提供丰富的断言方法。 |
| Mockito | 模拟框架,用于创建 Mock 对象,模拟依赖行为。 | UserRepository mockRepo = mock(UserRepository.class); when(mockRepo.findById(1L)).thenReturn(Optional.of(new User("John"))); | 隔离被测类与外部依赖。 |
@Mock | 创建 Mock 对象(需配合 @ExtendWith(MockitoExtension.class))。 | @ExtendWith(MockitoExtension.class) class UserServiceTest { @Mock private UserRepository userRepository; @InjectMocks private UserService userService; } | 自动注入 Mock 依赖。 |
@InjectMocks | 将 @Mock 对象注入到被测类中。 | 见上例。 | 适用于构造器或字段注入。 |
10.2 集成测试(@SpringBootTest)
| 注解/配置 | 说明 | 代码示例 | 注意事项 |
|---|
@SpringBootTest | 启动完整的 Spring 应用上下文,用于测试组件间的集成。 | @SpringBootTest @TestPropertySource(locations = "classpath:application-test.properties") class UserServiceIntegrationTest { @Autowired private UserService userService; @Test void shouldSaveUser() { User saved = userService.save(new User("Alice")); assertNotNull(saved.getId()); } } | 加载 application.yml 配置,启动嵌入式容器(可选)。 |
webEnvironment | 指定 Web 环境模式。 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) 或 RANDOM_PORT | MOCK 使用 MockMvc,RANDOM_PORT 启动真实服务器。 |
@TestPropertySource | 指定测试专用的配置文件。 | @TestPropertySource(properties = "app.feature.enabled=false") | 避免影响生产配置。 |
@DirtiesContext | 标记测试类或方法后重置应用上下文。 | @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) | 用于状态被修改的测试,确保隔离。 |
TestRestTemplate | 用于发送 HTTP 请求测试 REST API(MOCK 模式)。 | @Autowired private TestRestTemplate restTemplate; @Test void shouldReturnUser() { ResponseEntity<User> response = restTemplate.getForEntity("/users/1", User.class); assertEquals(HttpStatus.OK, response.getStatusCode()); } | RANDOM_PORT 模式下使用。 |
10.3 Web 层测试(@WebMvcTest)
| 注解/组件 | 说明 | 代码示例 | 注意事项 |
|---|
@WebMvcTest | 仅加载 Web 层组件(如 @Controller、@RestController),不加载其他 Bean。 | @WebMvcTest(UserController.class) class UserControllerTest { @Autowired private MockMvc mockMvc; @MockBean private UserService userService; } | 用于快速测试控制器逻辑。 |
MockMvc | 模拟 MVC 请求,无需启动服务器。 | @Test void shouldReturnUser() throws Exception { when(userService.findById(1L)).thenReturn(new User("John")); mockMvc.perform(get("/users/1")).andExpect(status().isOk()).andExpect(jsonPath("$.name").value("John")); } | 支持链式调用,验证状态、JSON 内容等。 |
@MockBean | 为 Spring 上下文中的 Bean 创建 Mock 实例。 | @MockBean private UserService userService; | 替换容器中的真实 Bean。 |
| ResultMatcher | 用于验证响应结果。 | status().isOk(), content().json(...), jsonPath("$.name").value("John") | 需引入 spring-boot-starter-test。 |
10.4 数据层测试(@DataJpaTest)
| 注解/组件 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
@DataJpaTest | @DataJpaTest | 专用于测试 JPA 数据访问层,加载最小化 Spring 上下文,默认启用内存数据库和事务管理。 | @DataJpaTest class UserRepositoryTest { @Autowired private TestEntityManager entityManager; @Autowired private UserRepository userRepository; @Test void findByEmail_shouldReturnUser() { User user = new User("john", "john@example.com"); entityManager.persistAndFlush(user); Optional<User> found = userRepository.findByEmail("john@example.com"); assertThat(found).isPresent(); assertThat(found.get().getName()).isEqualTo("john"); } } | 默认使用内存数据库(H2),仅加载 JPA 相关组件,每个测试方法在事务中自动回滚。 |
TestEntityManager | @Autowired private TestEntityManager entityManager; | Spring Boot 提供的工具类,用于在测试中持久化实体、刷新会话、清除缓存等。 | entityManager.persistAndFlush(user); | 推荐用于测试中插入测试数据。 |
@Rollback | @Rollback(false) | 控制测试方法结束后是否回滚事务。默认为 true(回滚)。 | 设置为 false 可保留数据(用于调试)。 | 生产环境慎用。 |
@Sql | @Sql("/test-data.sql") | 在测试方法执行前或后执行指定的 SQL 脚本。 | @Test @Sql("/user-test-data.sql") void whenUserExists_thenFindByEmail() { ... } | 支持 executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD。 |
| 自定义数据源 | 配置 application-test.properties 中的数据库连接属性。 | spring.datasource.url=jdbc:postgresql://localhost:5432/testdb | 需通过 @ActiveProfiles("test") 激活。 | |
10.5 测试配置与 Mock 数据
| 技术/注解 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|
@TestConfiguration | @TestConfiguration | 在测试中定义额外的 Bean 或覆盖主配置中的 Bean。 | @TestConfiguration public class TestConfig { @Bean @Primary public EmailService mockEmailService() { return Mockito.mock(EmailService.class); } } | 使用 @Primary 确保测试 Bean 优先。 |
@MockBean | @MockBean private EmailService emailService; | 为 Spring 上下文中的 Bean 创建 Mockito 模拟对象并替换原 Bean。 | @MockBean private UserRepository userRepository; @Test void getUser_shouldReturnFromMock() { when(userRepository.findById(1L)).thenReturn(Optional.of(new User("John"))); } | 适用于集成测试中隔离外部依赖。 |
@SpyBean | @SpyBean private UserService userService; | 对真实 Bean 创建”间谍”对象,可部分模拟其方法。 | @SpyBean private EmailService emailService; @Test void sendEmail_shouldCallRealMethod() { doNothing().when(emailService).send(any()); } | 适用于需要保留大部分真实行为的场景。 |
@ActiveProfiles | @ActiveProfiles("test") | 激活指定的 Spring Profile。 | @SpringBootTest @ActiveProfiles("test") class UserServiceTest { ... } | 用于区分开发、测试、生产环境的配置。 |
Mockito 模拟行为 | when(...).thenReturn(...) / doThrow(...).when(...) | 定义模拟对象的方法返回值或抛出异常。 | when(userRepository.findById(999L)).thenThrow(new EntityNotFoundException()); | 灵活控制测试场景。 |
@DirtiesContext | @DirtiesContext | 标记测试类或方法会”污染”应用上下文,测试后需重建。 | @Test @DirtiesContext void changeGlobalState_shouldRebuildContext() { ... } | 影响性能,仅在必要时使用。 |
| 嵌入式数据库 | 使用 H2、HSQLDB 等内存数据库 | 用于快速、隔离的数据层测试。 | spring.datasource.url=jdbc:h2:mem:testdb | 推荐用于单元测试和集成测试。 |
第十一章:部署与监控
11.1 打包方式(Jar 与 War)
| 打包方式 | 说明 | 适用场景 | 配置方式 | 注意事项 |
|---|
| 可执行 JAR(Executable JAR) | Spring Boot 默认打包方式,内置 Tomcat/Jetty/Undertow 容器,通过 java -jar app.jar 启动。 | 微服务、独立应用、云原生部署。 | pom.xml 中 <packaging>jar</packaging>(默认)。 | 启动简单,无需外部容器;包含所有依赖,文件较大;支持 fat jar 结构。 |
| WAR 包 | 传统 Web 应用打包格式,需部署到外部 Servlet 容器(如 Tomcat)。 | 企业传统环境、需共享容器资源、与旧系统共存。 | <packaging>war</packaging>,主类继承 SpringBootServletInitializer,重写 configure 方法。 | 需外部容器支持;可与其他 WAR 共享容器;启动速度略慢于 JAR。 |
| Thin JAR | 使用 spring-boot-thin-layout 插件,仅打包项目代码,依赖在运行时下载。 | CI/CD 流水线、节省存储空间、快速部署。 | 添加 spring-boot-thin-layout 插件。 | 首次启动需联网下载依赖;适合网络稳定的环境。 |
11.2 使用 Maven/Gradle 构建项目
| 工具 | 说明 | 构建命令 | 配置文件 | 优势 |
|---|
| Maven | 基于 XML 的构建工具,结构标准化,插件生态丰富。 | mvn clean / mvn compile / mvn test / mvn package / mvn install / mvn spring-boot:run | pom.xml | 标准化强,Spring Initializr 默认支持,适合大型企业项目。 |
| Gradle | 基于 Groovy/Kotlin DSL 的构建工具,性能更高,配置更灵活。 | gradle build / gradle bootRun / gradle test | build.gradle 或 build.gradle.kts | 构建速度快(增量编译),脚本化配置,Android 开发标准工具。 |
| Spring Boot 插件 | 提供打包、运行、依赖管理等功能。 | Maven: spring-boot-maven-plugin;Gradle: id 'org.springframework.boot' version '3.3.3' | pom.xml / build.gradle | 自动打包为可执行 JAR/WAR,内置依赖版本管理。 |
11.3 部署到服务器(Tomcat、Docker)
| 部署方式 | 步骤 | 说明 | 注意事项 |
|---|
| 部署到外部 Tomcat | 1. 打包为 WAR;2. 复制到 tomcat/webapps/;3. 启动 Tomcat:bin/startup.sh | 适用于传统 Java EE 环境。 | 确保 Tomcat 版本兼容;检查端口冲突(默认 8080);日志位于 logs/catalina.out。 |
| Docker 部署(推荐) | 编写 Dockerfile → docker build -t myapp:1.0 . → docker run -d -p 8080:8080 myapp:1.0 | 实现环境一致性、快速部署、弹性伸缩。 | 使用 .dockerignore 忽略不必要文件;推荐使用多阶段构建优化镜像大小。 |
| 云平台部署 | 如 AWS ECS、阿里云容器服务、Kubernetes | 结合 CI/CD 实现自动化部署。 | 配置健康检查;设置资源限制(CPU、内存);使用 Secret 管理敏感配置。 |
11.4 Actuator 监控端点使用
| 端点 | 默认路径 | 是否敏感 | 用途 | 启用配置 |
|---|
/health | /actuator/health | 否(生产建议保护) | 显示应用健康状态(数据库、磁盘、自定义检查)。 | management.endpoints.web.exposure.include=health,info,metrics |
/info | /actuator/info | 否 | 显示应用信息(版本、构建时间等)。 | 在 application.yml 中配置 info.app.name 和 info.app.version。 |
/metrics | /actuator/metrics | 是 | 展示 JVM、HTTP 请求、线程等指标。 | 可查看具体指标如 /actuator/metrics/jvm.memory.used。 |
/env | /actuator/env | 是 | 显示所有环境变量和配置属性。 | 建议生产环境禁用或保护。 |
/beans | /actuator/beans | 是 | 显示所有 Spring Bean 及其依赖关系。 | 调试依赖注入问题。 |
/conditions | /actuator/conditions | 是 | 显示自动配置类的匹配/不匹配原因。 | 排查自动配置问题。 |
/loggers | /actuator/loggers | 是 | 动态调整日志级别。 | POST /actuator/loggers/com.example 设置 {"configuredLevel": "DEBUG"} |
/shutdown | /actuator/shutdown | 是(默认关闭) | 关闭应用(需启用)。 | management.endpoint.shutdown.enabled=true |
安全提示:生产环境应通过 Spring Security 保护敏感端点,或仅在内网暴露。
11.5 自定义健康检查与指标暴露
| 功能 | 实现方式 | 代码示例 | 说明 |
|---|
| 自定义健康检查 | 实现 HealthIndicator 接口或使用 @Component 标注类。 | @Component public class DatabaseHealthIndicator implements HealthIndicator { @Autowired private DataSource dataSource; @Override public Health health() { try (Connection conn = dataSource.getConnection()) { if (conn.isValid(1)) { return Health.up().withDetail("database", "MySQL").build(); } } catch (SQLException e) { return Health.down(e).build(); } return Health.down().build(); } } | 访问 /actuator/health 将包含自定义检查结果。 |
| 自定义指标(Metrics) | 使用 MeterRegistry 注册计数器、计时器等。 | @Service public class UserService { @Autowired private MeterRegistry meterRegistry; public User createUser(User user) { meterRegistry.counter("user.create.count").increment(); return Timer.builder("user.create.duration").register(meterRegistry).record(() -> userRepository.save(user)); } } | 指标可通过 /actuator/metrics 查看,也可集成 Prometheus。 |
| Prometheus 集成 | 添加 micrometer-registry-prometheus 依赖。 | 访问 /actuator/prometheus 获取指标数据,供 Prometheus 抓取。 | 用于构建监控告警系统(如 Grafana 展示)。 |
第十二章:微服务基础(可选扩展)
12.1 微服务架构概述
| 概念 | 说明 | 优势 | 挑战 |
|---|
| 微服务架构 | 将单体应用拆分为多个小型、独立部署的服务,每个服务围绕业务能力构建。 | - 技术异构性 - 独立部署 - 弹性伸缩 - 故障隔离 | - 分布式复杂性 - 数据一致性 - 服务治理 - 运维成本高 |
| 服务拆分原则 | 按业务边界(如用户、订单、支付)拆分,高内聚、低耦合。 | 避免”分布式单体”。 | 推荐使用领域驱动设计(DDD)指导拆分。 |
| 通信方式 | 同步:HTTP/REST、gRPC 异步:消息队列(Kafka、RabbitMQ) | REST 简单通用,gRPC 高性能。 | 注意超时、重试、熔断机制。 |
| Spring Cloud | Spring 提供的微服务开发工具集,整合 Netflix、Alibaba 等组件。 | 快速构建微服务生态系统。 | 生态演进快,需关注版本兼容性。 |
12.2 使用 Spring Cloud Alibaba/Nacos 进行服务注册与发现
| 组件 | 说明 | 配置示例 | 作用 |
|---|
| Nacos Server | 阿里开源的注册中心与配置中心,支持服务发现、健康检查、动态配置。 | 下载启动:sh bin/startup.sh -m standalone | 替代 Eureka + Config Server。 |
| 服务提供者 | 向 Nacos 注册自身服务。 | yaml spring.cloud.nacos.discovery.server-addr: 127.0.0.1:8848
@SpringBootApplication @EnableDiscoveryClient public class UserServiceApplication { ... } | 启动后在 Nacos 控制台可见。 |
| 服务消费者 | 从 Nacos 发现服务并调用。 | 同上配置 nacos.discovery.server-addr | 使用 RestTemplate + @LoadBalanced 或 OpenFeign 调用。 |
| 健康检查 | Nacos 自动检测服务实例健康状态,自动剔除故障节点。 | 默认基于心跳机制。 | 保障服务调用的可靠性。 |
12.3 OpenFeign 声明式服务调用
| 功能 | 说明 | 代码示例 | 注意事项 |
|---|
| 声明式 HTTP 客户端 | 通过接口 + 注解方式调用远程服务,无需手动拼接 URL。 | @FeignClient(name = "order-service", path = "/orders") public interface OrderClient { @GetMapping("/{id}") Order findById(@PathVariable("id") Long id); @PostMapping Order create(@RequestBody Order order); } | - 必须启用 @EnableFeignClients - 依赖服务名注册到注册中心 |
| 集成负载均衡 | 自动集成 Ribbon 或 Spring Cloud LoadBalancer。 | 调用 orderClient.findById(1L) 时自动选择实例。 | 无需手动指定 IP 和端口。 |
| 超时与重试 | 可配置连接、读取超时及重试策略。 | yaml feign.client.config.default.connectTimeout: 5000 feign.client.config.default.readTimeout: 5000 | 避免雪崩效应。 |
12.4 Gateway 网关使用
| 功能 | 说明 | 配置示例 | 作用 |
|---|
| API 网关 | 统一入口,负责路由、鉴权、限流、日志等横切关注点。 | 使用 spring-cloud-starter-gateway | 隐藏内部服务结构。 |
| 路由配置 | 定义请求路径到微服务的映射规则。 | yaml spring.cloud.gateway.routes: - id: user-service uri: lb://user-service predicates: - Path=/api/users/** | lb:// 表示从注册中心负载均衡调用。 |
| 过滤器(Filter) | 在请求前后执行逻辑,如添加头、权限校验。 | 自定义 GlobalFilter 或使用内置过滤器。 | 实现统一认证、日志记录。 |
| 限流(Rate Limiting) | 基于 Redis 实现请求频率控制。 | 使用 RequestRateLimiter 过滤器。 | 防止恶意请求或系统过载。 |
12.5 分布式配置中心
| 组件 | 说明 | 配置方式 | 优势 |
|---|
| Nacos Config | Nacos 提供的动态配置管理功能。 | yaml spring.cloud.nacos.config.server-addr: 127.0.0.1:8848 spring.cloud.nacos.config.group: DEFAULT_GROUP spring.cloud.nacos.config.namespace: public spring.cloud.nacos.config.file-extension: yaml | - 配置实时推送 - 多环境管理(dev/test/prod) - 版本回滚 |
| 配置自动刷新 | 使用 @RefreshScope 使 Bean 在配置更新后重新加载。 | @Component @RefreshScope public class FeatureToggle { @Value("${feature.enabled:false}") private boolean enabled; } | 无需重启服务即可生效。 |
| 配置优先级 | 命令行 > JVM 参数 > bootstrap.yml > 远程配置中心 > 本地 application.yml | 合理设计配置层级。 | 便于环境差异化配置。 |