Spring Boot 核心注解完全手册(逻辑版·痛点+原理+实战)
本文按企业级开发流程逻辑重构,覆盖11大核心场景,每个注解均包含「核心功能+底层原理+可运行示例+高频痛点&解决方案」,既是开发手册也是排错指南(基于Spring Boot 3.2+)。
简洁版(后附详细版带原理解释)
Spring Boot 核心注解手册
按开发逻辑排序(启动→Bean→注入→配置→Web→全局→数据→高级→条件→安全→测试)
1. 启动与自动配置(应用入口)
| 注解 | 核心功能 | 关键/痛点 |
|---|---|---|
| @SpringBootApplication | 启动类核心,三合一(@SpringBootConfiguration+@EnableAutoConfiguration+@ComponentScan) | 跨包Bean扫不到:用scanBasePackages指定路径 |
| @EnableAutoConfiguration | 开启自动配置,按需加载组件 | 看生效/失效:application.yml加debug: true |
| @Import | 手动导入配置类/Bean,突破扫描限制 | 导入类无默认构造器:手动@Bean创建 |
| @ComponentScan | 显式指定Bean扫描路径 | 报错No qualifying bean:检查包路径/注解 |
2. IoC容器与Bean定义(组件注册)
| 注解 | 核心功能 | 关键/痛点 |
|---|---|---|
| @Configuration | 标记配置类,替代XML | proxyBeanMethods=true:保证单例;false:启动更快 |
| @Bean | 方法返回值注册为Bean | 原型Bean注入单例:用ObjectFactory延迟获取 |
| @Component | 通用Bean注解 | 多实例冲突:@Qualifier指定名称 |
| @Service | 业务层Bean(语义化@Component) | 严格分层,别标在Controller上 |
| @Repository | DAO层Bean,自带持久层异常转换 | MyBatis的@Mapper无需再加 |
| @Controller | MVC控制器,返回视图 | 返回JSON需加@ResponseBody |
| @RestController | @Controller+@ResponseBody,默认返回JSON | 字段名不一致:用@JsonProperty |
3. 依赖注入与Bean控制(生命周期)
| 注解 | 核心功能 | 关键/痛点 |
|---|---|---|
| @Autowired | 按类型注入Bean | 多Bean冲突:@Qualifier/@Primary;构造器注入最优 |
| @Qualifier | 配合@Autowired按名称注入 | 名称区分大小写,检查Bean名是否匹配 |
| @Primary | 同类型多Bean时首选 | 同一类型只能一个@Primary |
| @Resource | JSR标准,默认按名称注入 | Spring Boot3+用jakarta.annotation.Resource |
| @Scope | 定义Bean作用域 | 原型Bean注入单例:用ObjectFactory/@Lookup |
| @Lazy | 单例Bean懒加载 | 循环依赖:加@Lazy延迟加载 |
| @PostConstruct | Bean初始化后执行 | 构造器中无法用@Autowired,放这里 |
| @PreDestroy | Bean销毁前释放资源 | 仅单例生效,容器正常关闭才执行 |
4. 配置绑定与多环境(外部配置)
| 注解 | 核心功能 | 关键/痛点 |
|---|---|---|
| @Value | 注入单个配置值,支持${}/SpEL | 静态变量注入:用非静态setter赋值 |
| @ConfigurationProperties | 批量绑定配置到Bean,支持List/Map/校验 | 绑定失败:检查prefix/setter/@EnableConfigurationProperties |
| @PropertySource | 加载自定义.properties文件 | 不支持YAML:自定义PropertySourceFactory |
| @Profile | 指定环境生效(dev/test/prod) | 激活:spring.profiles.active=dev |
| @Validated | 开启参数校验 | 需引入spring-boot-starter-validation依赖 |
5. Web MVC接口开发(HTTP请求)
| 注解 | 核心功能 | 关键/痛点 |
|---|---|---|
| @RequestMapping/@GetMapping等 | 绑定请求路径/方法 | PUT/DELETE被拦截:配置跨域/Security |
| @PathVariable | 提取路径参数(/user/{id}) | 类型转换失败:用正则限制({id:\d+}) |
| @RequestParam | 提取URL/表单参数 | 多值参数:前端传ids=1&ids=2 |
| @RequestBody | 接收JSON请求体 | Content-Type必须是application/json |
| @RequestHeader/@CookieValue | 提取请求头/Cookie | 非必填:设置required=false |
| @CrossOrigin | 允许跨域 | 跨域报错:检查origins/maxAge配置 |
6. 全局统一处理(AOP/异常)
| 注解 | 核心功能 | 关键/痛点 |
|---|---|---|
| @RestControllerAdvice | 全局异常处理,返回JSON | 不生效:检查扫描路径/异常类型匹配 |
| @ExceptionHandler | 处理指定异常 | 优先级:Controller内>全局 |
| @Aspect/@Pointcut/@Around等 | AOP切面编程 | 内部调用不生效:走代理对象 |
7. 数据访问与事务(数据库)
| 注解 | 核心功能 | 关键/痛点 |
|---|---|---|
| @Transactional | 声明式事务 | 失效场景:非public/内部调用/异常被捕获/MyISAM引擎 |
| @Entity/@Table/@Id等 | JPA实体映射 | 主键策略:MySQL用IDENTITY,Oracle用SEQUENCE |
| @Mapper/@MapperScan | MyBatis映射接口 | 多参数绑定:用@Param指定名称 |
8. 高级能力(异步/定时/消息)
| 注解 | 核心功能 | 关键/痛点 |
|---|---|---|
| @EnableAsync/@Async | 开启/标记异步方法 | 失效:未加@EnableAsync/内部调用;自定义线程池防耗尽 |
| @EnableScheduling/@Scheduled | 开启/标记定时任务 | 阻塞:自定义ThreadPoolTaskScheduler |
| @RabbitListener/@KafkaListener | 消息队列消费者 | 需配置对应MQ连接信息 |
9. 条件装配(自动配置灵魂)
| 注解 | 核心功能 | 关键/痛点 |
|---|---|---|
| @ConditionalOnClass/@ConditionalOnBean等 | 满足条件才加载Bean | 不生效:debug模式看条件匹配结果 |
10. 安全与权限
| 注解 | 核心功能 | 关键/痛点 |
|---|---|---|
| @EnableWebSecurity | 开启Web安全 | 需配置SecurityFilterChain |
| @PreAuthorize | 方法前权限校验(支持EL) | 需开启prePostEnabled=true |
| @AuthenticationPrincipal | 获取当前登录用户 | 需登录后才能获取,匿名访问为null |
11. 测试与切片
| 注解 | 核心功能 | 关键/痛点 |
|---|---|---|
| @SpringBootTest | 启动完整容器,集成测试 | 启动慢:单元测试用切片注解 |
| @WebMvcTest | 只启动Web层,测试Controller | 依赖Service需用@MockBean模拟 |
| @DataJpaTest | 只启动数据层,测试Repository | 默认用H2,真实库需加Replace.NONE |
| @MockBean | 模拟Bean,隔离依赖 | 区别@Mock:@MockBean放入Spring容器 |
核心逻辑总结(背会即通)
- 先启动:@SpringBootApplication;2. 装Bean:@Service/@Bean;3. 注入:@Autowired;4. 读配置:@ConfigurationProperties;5. 写接口:@RestController;6. 全局处理:@RestControllerAdvice;7. 数据库:@Transactional;8. 高级:@Async;9. 条件:@ConditionalOnXxx;10. 安全:@PreAuthorize;11. 测试:@WebMvcTest。
详细版
一、启动与自动配置(应用入口级)
1. @SpringBootApplication
核心功能
Spring Boot应用唯一入口注解,三合一组合注解,标记主启动类,触发自动配置和组件扫描。
底层原理
- 包含3个核心注解:
@SpringBootConfiguration:本质是@Configuration,标记主类为配置类;@EnableAutoConfiguration:触发自动配置机制;@ComponentScan:默认扫描主类所在包及其子包的组件。
- 启动时通过
SpringApplication加载上下文,解析该注解完成初始化。
实际示例
java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 核心入口:默认扫描com.example.demo及子包
@SpringBootApplication
// 自定义扫描路径(解决跨包扫描问题)
// @SpringBootApplication(scanBasePackages = {"com.example.service", "com.example.mapper"})
// 排除指定自动配置(解决冲突)
// @SpringBootApplication(exclude = RedisAutoConfiguration.class)
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 跨包的Bean扫描不到(如com.example.api下的Controller) | 1. 用scanBasePackages指定所有需要扫描的包;2. 将主类包名提升为根包(如com.example) |
| 自动配置冲突(如自定义RedisTemplate覆盖默认) | 用exclude排除冲突的自动配置类;或用@ConditionalOnMissingBean自定义Bean覆盖 |
| 启动慢 | 缩小扫描范围,排除无需自动配置的组件 |
2. @EnableAutoConfiguration
核心功能
单独开启Spring Boot自动配置机制(通常随@SpringBootApplication生效),根据项目依赖自动配置组件。
底层原理
- 注解内通过
@Import(AutoConfigurationImportSelector.class)导入自动配置选择器; AutoConfigurationImportSelector扫描META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件;- 加载文件中所有
XxxAutoConfiguration类,结合@ConditionalOnXxx条件判断是否生效。
实际示例
java
// 极少单独使用,通常随@SpringBootApplication生效
@EnableAutoConfiguration
@Configuration
public class AutoConfigDemo {
// 自动配置会根据依赖加载Tomcat、MVC、数据源等组件
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 不知道哪些自动配置生效/失效 | 在application.yml中添加debug: true,启动日志会打印Positive matches(生效)和Negative matches(失效) |
| 自动配置类加载过多导致启动慢 | 用@SpringBootApplication(exclude)排除无需的自动配置;或自定义starter缩小依赖范围 |
3. @Import
核心功能
手动导入配置类/普通类到Spring容器,突破@ComponentScan的扫描限制。
底层原理
- 通过
ImportBeanDefinitionRegistrar或ImportSelector将指定类注册为Bean; - 优先级高于
@ComponentScan,可导入非扫描路径下的类。
实际示例
java
package com.example.demo.config;
import com.example.external.ExternalService;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Configuration;
// 导入单个类
@Import(ExternalService.class)
// 导入多个类
// @Import({ExternalService.class, CustomConfig.class})
@Configuration
public class ImportConfig {
// ExternalService会被注册为Bean,即使它不在扫描路径下
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 导入的类无默认构造器导致初始化失败 | 确保导入类有无参构造器;或通过@Bean手动创建实例 |
| 导入过多类导致配置混乱 | 按功能分组导入,避免一次性导入大量类 |
4. @ComponentScan
核心功能
显式指定Spring组件扫描路径,替代默认扫描规则。
底层原理
- 通过
ClassPathScanningCandidateComponentProvider扫描指定路径; - 识别标注
@Component/@Service/@Controller等注解的类,注册为Bean。
实际示例
java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
// 显式指定扫描路径
@ComponentScan(basePackages = {
"com.example.demo.controller",
"com.example.demo.service",
"com.example.external" // 跨模块扫描
})
@org.springframework.boot.autoconfigure.SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
报错No qualifying bean of type 'XxxService' available |
1. 检查XxxService是否加@Service;2. 检查basePackages是否包含该类所在包;3. 用basePackageClasses精准扫描(如basePackageClasses = UserService.class) |
| 扫描路径过大导致启动慢 | 缩小扫描范围,只扫描核心业务包,排除测试/工具包 |
二、IoC 容器与 Bean 定义(组件注册级)
1. @Configuration
核心功能
标记类为配置类,替代传统XML配置文件,类中@Bean方法的返回值会注册为Spring Bean。
底层原理
- 默认通过CGLIB代理(
proxyBeanMethods = true),保证@Bean方法多次调用返回单例; proxyBeanMethods = false(Lite模式):无代理,启动更快,适合无@Bean方法互相调用的场景。
实际示例
java
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
// 全量模式(默认):保证@Bean方法单例
@Configuration
// 轻量模式:启动更快,适合无@Bean方法依赖
// @Configuration(proxyBeanMethods = false)
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
// 全量模式下,多次调用返回同一个实例
@Bean
public String appName() {
return "demo-app";
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 配置类中调用@Bean方法返回多实例 | 保持proxyBeanMethods = true;或通过@Autowired注入Bean而非直接调用方法 |
| 配置类过多导致维护混乱 | 按功能拆分配置类(如RedisConfig、DataSourceConfig),用@Import批量导入 |
2. @Bean
核心功能
标注在@Configuration类的方法上,将方法返回值注册为Spring Bean,是手动注册Bean的核心方式。
底层原理
- Spring解析
@Configuration类时,将@Bean方法封装为BeanDefinition; - 单例Bean:容器启动时执行方法创建实例;原型Bean:每次
getBean()时执行方法。
实际示例
java
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfig {
// 基础用法:默认Bean名称=方法名
@Bean
public UserService userService() {
return new UserService();
}
// 自定义Bean名称+初始化/销毁方法
@Bean(
name = "customDataSource",
initMethod = "init",
destroyMethod = "close"
)
public DataSource dataSource() {
return new CustomDataSource();
}
// 依赖其他Bean(方法参数自动注入)
@Bean
public OrderService orderService(UserService userService) {
return new OrderService(userService);
}
// 静态内部类模拟
public static class CustomDataSource {
public void init() { System.out.println("数据源初始化"); }
public void close() { System.out.println("数据源销毁"); }
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| @Bean方法依赖的Bean不存在 | 1. 检查依赖Bean是否注册;2. 用@ConditionalOnBean确保依赖存在;3. 捕获初始化异常 |
| 原型Bean注入到单例Bean中仅创建一次 | 用ObjectFactory<XxxBean>延迟获取;或手动调用ApplicationContext.getBean() |
3. @Component
核心功能
最通用的组件注解,标记类为Spring Bean,是所有业务组件注解的父注解。
底层原理
- 被
@Indexed修饰(Spring 5+),编译时生成META-INF/spring.components文件,提升扫描效率; - Spring扫描到该注解后,自动将类注册为Bean,默认名称为类名首字母小写。
实际示例
java
package com.example.demo.component;
import org.springframework.stereotype.Component;
// 默认Bean名称:userComponent
@Component
// 自定义Bean名称
// @Component("myUserComponent")
public class UserComponent {
public String getUserName(Long id) {
return "用户" + id;
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 同一类型多个@Component Bean导致注入冲突 | 1. 用@Qualifier指定Bean名称;2. 用@Primary标记首选Bean;3. 自定义Bean名称避免重复 |
| 工具类加@Component导致多实例 | 工具类建议设计为静态方法,无需注册为Bean;若必须注册,确保@Scope("singleton")(默认) |
4. @Service
核心功能
标注业务逻辑层(Service)类,是@Component的语义化特例,无额外功能,但明确代码分层。
底层原理
- 底层通过
@Component实现(@Service = @Component + 业务层语义); - Spring扫描时与
@Component无区别,仅作为代码分层标识。
实际示例
java
package com.example.demo.service;
import org.springframework.stereotype.Service;
// 默认Bean名称:userService
@Service
// 自定义Bean名称
// @Service("userBiz")
public class UserService {
public String getUserInfo(Long id) {
// 业务逻辑:查询用户信息
return "用户ID:" + id + ",名称:张三";
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 误将@Service标注在Controller/Repository上 | 严格按分层标注:Controller→@Controller,Repository→@Repository,Service→@Service |
| Service层依赖注入失败 | 1. 检查是否加@Service;2. 检查@ComponentScan是否包含该包;3. 检查依赖的Bean是否存在 |
5. @Repository
核心功能
标注数据访问层(DAO/Repository)类,除注册Bean外,自动将持久层异常(如SQLException)转换为Spring统一的DataAccessException。
底层原理
- 继承
@Component,具备Bean注册功能; - Spring通过
PersistenceExceptionTranslationPostProcessor为该注解标注的类生成代理,实现异常转换。
实际示例
java
package com.example.demo.repository;
import org.springframework.stereotype.Repository;
@Repository
public class UserRepository {
public String findById(Long id) {
// 模拟JDBC查询,抛出SQLException
// throw new SQLException("查询失败");
return "用户ID:" + id;
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 异常转换功能失效 | 1. 确保类加@Repository;2. 确保Spring上下文存在PersistenceExceptionTranslationPostProcessor(Spring Boot自动配置);3. 异常必须是持久层异常(如SQLException) |
| 与MyBatis的@Mapper混用 | MyBatis的@Mapper已包含Bean注册和代理生成,无需再加@Repository |
6. @Controller
核心功能
标注Spring MVC控制器类,处理HTTP请求,默认返回视图页面(如Thymeleaf/JSP)。
底层原理
- 继承
@Component,被Spring扫描为Bean; - Spring MVC通过
RequestMappingHandlerMapping识别类中的@RequestMapping方法,绑定请求。
实际示例
java
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class PageController {
// 返回视图(需配置视图解析器,如Thymeleaf)
@GetMapping("/user/page")
public String userPage() {
// 解析为:classpath:/templates/user.html
return "user";
}
// 配合@ResponseBody返回JSON
@GetMapping("/user/info")
@ResponseBody
public String userInfo() {
return "{\"id\":1,\"name\":\"张三\"}";
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 方法返回字符串被解析为视图路径而非JSON | 1. 方法加@ResponseBody;2. 类上加@RestController替代@Controller |
报错No mapping for GET /xxx |
1. 检查@GetMapping路径是否正确;2. 检查Controller是否加@Controller;3. 检查扫描路径 |
7. @RestController
核心功能
组合注解(@Controller + @ResponseBody),标注控制器类,所有方法默认返回JSON/XML数据,无需重复加@ResponseBody。
底层原理
- 底层通过
@Controller和@ResponseBody元注解实现; @ResponseBody通过HttpMessageConverter将返回值转换为JSON,写入HTTP响应体。
实际示例
java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RestApiController {
// 自动返回JSON,无需@ResponseBody
@GetMapping("/order/info")
public OrderVO getOrderInfo() {
OrderVO vo = new OrderVO();
vo.setId(1L);
vo.setOrderNo("20240310001");
vo.setAmount(99.9);
return vo; // Spring自动转换为JSON
}
// 静态内部类:订单VO
public static class OrderVO {
private Long id;
private String orderNo;
private Double amount;
// getter/setter省略
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 返回的JSON字段名与实体类不一致 | 1. 用@JsonProperty("order_no")标注实体类字段;2. 配置Jackson自定义命名策略 |
| 返回值为null时字段丢失 | 在application.yml中配置:spring.jackson.default-property-inclusion=ALWAYS |
三、依赖注入与 Bean 控制(生命周期/作用域)
1. @Autowired
核心功能
按类型(byType)自动装配Spring Bean,是依赖注入(DI)的核心注解,支持构造器、字段、方法注入。
底层原理
- Spring通过
AutowiredAnnotationBeanPostProcessor解析该注解; - 装配流程:按类型查找Bean → 找到多个则按名称匹配 → 匹配失败抛出异常;
- 优先级:构造器注入 > 方法注入 > 字段注入。
实际示例
java
package com.example.demo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
// 字段注入(简单但不推荐,不利于单元测试)
@Autowired
private UserService userService;
// 构造器注入(推荐,强制依赖,便于单元测试)
private final ProductService productService;
@Autowired
public OrderService(ProductService productService) {
this.productService = productService;
}
// 方法注入(适合可选依赖)
private PayService payService;
@Autowired(required = false)
public void setPayService(PayService payService) {
this.payService = payService;
}
// 多Bean时配合@Qualifier
@Autowired
@Qualifier("vipUserService")
private UserService vipUserService;
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
报错No qualifying bean of type 'XxxService' available |
1. 检查XxxService是否加@Service;2. 检查扫描路径;3. 设置required = false(可选依赖) |
报错NoUniqueBeanDefinitionException(同一类型多Bean) |
1. 用@Qualifier指定Bean名称;2. 用@Primary标记首选Bean;3. 自定义Bean名称匹配字段名 |
| 构造器注入时循环依赖 | 1. 重构代码解耦;2. 用@Lazy延迟加载其中一个Bean;3. 改用setter注入 |
2. @Qualifier
核心功能
配合@Autowired使用,按名称(byName)指定要注入的Bean,解决同一类型多Bean的冲突问题。
底层原理
- 通过
QualifierAnnotationAutowireCandidateResolver解析; - Spring按类型找到多个Bean后,匹配
@Qualifier指定的名称,确定唯一Bean。
实际示例
java
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class UserConfig {
@Bean("normalUserService")
public UserService normalUserService() {
return new NormalUserService();
}
@Bean("vipUserService")
public UserService vipUserService() {
return new VipUserService();
}
}
// 注入时指定名称
package com.example.demo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@Autowired
@Qualifier("vipUserService") // 精准注入vipUserService
private UserService userService;
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| @Qualifier指定的名称不存在 | 1. 检查Bean名称是否正确(区分大小写);2. 检查@Bean/@Service的名称配置 |
| 同时使用@Primary和@Qualifier | @Qualifier优先级更高,会忽略@Primary,按名称注入 |
3. @Primary
核心功能
标注在Bean上,当同一类型有多个Bean时,该Bean成为@Autowired注入的首选Bean。
底层原理
- Spring解析
@Autowired时,若找到多个同类型Bean,优先选择@Primary标注的Bean; - 通过
PrimaryAnnotationBeanPostProcessor标记Bean的primary属性为true。
实际示例
java
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class DataSourceConfig {
// 首选数据源(@Autowired默认注入这个)
@Primary
@Bean("masterDataSource")
public DataSource masterDataSource() {
return new MasterDataSource();
}
@Bean("slaveDataSource")
public DataSource slaveDataSource() {
return new SlaveDataSource();
}
}
// 注入时自动选择masterDataSource
package com.example.demo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DataService {
@Autowired
private DataSource dataSource; // 注入的是masterDataSource
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 同一类型多个@Primary Bean | 报错IllegalStateException,确保同一类型只有一个@Primary Bean |
| @Primary不生效 | 检查是否同时使用了@Qualifier(优先级更高);检查Bean是否注册成功 |
4. @Resource
核心功能
JSR-250标准注解,默认按名称(byName)装配Bean,找不到名称时按类型(byType)装配,是@Autowired的替代方案。
底层原理
- Spring通过
CommonAnnotationBeanPostProcessor解析该注解; - 装配优先级:指定
name→ 字段名/方法名 → 类型 → 抛出异常。
实际示例
java
package com.example.demo.service;
import jakarta.annotation.Resource; // Spring Boot 3+用jakarta
import org.springframework.stereotype.Service;
@Service
public class CartService {
// 默认按名称装配(字段名cartRepository)
@Resource
private CartRepository cartRepository;
// 指定name属性(精准按名称装配)
@Resource(name = "vipCartRepository")
private CartRepository vipCartRepository;
// 指定type属性(按类型装配,等同于@Autowired)
@Resource(type = OrderRepository.class)
private OrderRepository orderRepository;
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| Spring Boot 3+中找不到@Resource | 替换为jakarta.annotation.Resource;确保引入spring-boot-starter-web(包含jakarta依赖) |
| 按名称找不到,按类型找到多个 | 1. 指定name属性精准匹配;2. 避免同一类型多个Bean未命名 |
5. @Scope
核心功能
定义Bean的作用域,控制Bean的创建时机和实例数量。
底层原理
- Spring通过
Scope接口实现作用域管理,不同作用域对应不同的Scope实现类; - 单例(singleton):容器启动时创建,全局唯一;
- 原型(prototype):每次
getBean()创建新实例; - Web作用域(request/session):绑定到HTTP请求/会话。
实际示例
java
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class ScopeConfig {
// 单例(默认):全局唯一,启动时创建
@Bean
@Scope("singleton")
public SingletonBean singletonBean() {
return new SingletonBean();
}
// 原型:每次getBean()创建新实例
@Bean
@Scope("prototype")
public PrototypeBean prototypeBean() {
return new PrototypeBean();
}
// Web作用域:每个HTTP请求一个实例(需Web环境)
@Bean
@Scope("request")
public RequestBean requestBean() {
return new RequestBean();
}
// 静态内部类
public static class SingletonBean {}
public static class PrototypeBean {}
public static class RequestBean {}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 原型Bean注入到单例Bean中仅创建一次 | 1. 用ObjectFactory<PrototypeBean>延迟获取;2. 用@Lookup注解;3. 手动调用ApplicationContext.getBean() |
| request/session作用域在非Web环境报错 | 1. 仅在Web环境使用;2. 配置@ConditionalOnWebApplication确保仅Web环境加载 |
6. @Lazy
核心功能
针对单例Bean,延迟Bean的创建时机(从容器启动时推迟到第一次使用时)。
底层原理
- Spring为
@Lazy标注的Bean创建代理对象; - 容器启动时仅创建代理,第一次调用Bean方法时才真正创建实例。
实际示例
java
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@Configuration
public class LazyConfig {
// 延迟创建:第一次使用时才实例化
@Bean
@Lazy
public LazyBean lazyBean() {
System.out.println("LazyBean创建");
return new LazyBean();
}
// 立即创建(默认):启动时实例化
@Bean
public EagerBean eagerBean() {
System.out.println("EagerBean创建");
return new EagerBean();
}
public static class LazyBean {}
public static class EagerBean {}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| @Lazy标注后Bean仍启动时创建 | 1. 检查是否是单例Bean;2. 检查是否有其他Bean提前注入该Bean;3. 检查是否调用了ApplicationContext.getBean() |
| 循环依赖问题未解决 | 确保两个Bean都加@Lazy;或重构代码解耦 |
7. @PostConstruct
核心功能
JSR-250标准注解,标注在方法上,在Bean初始化完成后(构造器+@Autowired注入后)执行,用于初始化操作。
底层原理
- 执行顺序:构造器 →
@Autowired注入 →@PostConstruct方法 → Bean就绪; - 通过
CommonAnnotationBeanPostProcessor解析该注解。
实际示例
java
package com.example.demo.service;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class CacheService {
@Autowired
private StringRedisTemplate redisTemplate;
// 构造器中无法使用redisTemplate(未注入)
public CacheService() {
// System.out.println(redisTemplate); // 报错:NullPointerException
}
// 初始化完成后执行:加载数据到缓存
@PostConstruct
public void initCache() {
System.out.println("初始化缓存...");
redisTemplate.opsForValue().set("init_key", "init_value");
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| @PostConstruct方法抛出异常 | Bean初始化失败,容器启动报错;1. 捕获异常并处理;2. 确保初始化逻辑稳定 |
| 多个@PostConstruct方法执行顺序不确定 | 1. 合并为一个方法;2. 重构代码逻辑,避免多个初始化方法 |
8. @PreDestroy
核心功能
JSR-250标准注解,标注在方法上,在Bean销毁前执行,用于释放资源。
底层原理
- 仅对单例Bean生效;
- 容器关闭时调用该方法,然后销毁Bean。
实际示例
java
package com.example.demo.service;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Service;
@Service
public class ResourceService {
private Connection connection;
// 初始化连接
@PostConstruct
public void initConnection() {
System.out.println("创建数据库连接...");
connection = new Connection();
}
// 销毁前释放连接
@PreDestroy
public void closeConnection() {
System.out.println("关闭数据库连接...");
if (connection != null) {
// connection.close(); // 释放资源
}
}
// 模拟连接类
private static class Connection {}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| @PreDestroy方法未执行 | 1. 确保是单例Bean;2. 确保容器正常关闭(如调用ApplicationContext.close());3. 检查是否有异常导致方法未执行 |
| 资源释放失败 | 1. 捕获异常并记录日志;2. 确保资源释放逻辑幂等 |
四、配置绑定与多环境(外部配置级)
1. @Value
核心功能
从配置文件/环境变量中注入单个值,支持SpEL表达式,适合简单配置。
底层原理
- Spring通过
AutowiredAnnotationBeanPostProcessor解析该注解; - 解析
${key}从Environment获取值,解析#{spel}执行表达式。
实际示例
java
package com.example.demo.component;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class ConfigComponent {
// 基础用法:注入配置文件中的值
@Value("${app.name}")
private String appName;
// 注入默认值(配置不存在时使用)
@Value("${app.version:1.0.0}")
private String appVersion;
// SpEL表达式:引用其他Bean的属性
@Value("#{@appConfig.appName + '-test'}")
private String testAppName;
public void printConfig() {
System.out.println("appName: " + appName);
System.out.println("appVersion: " + appVersion);
}
}
配置文件(application.yml)
yaml
app:
name: demo-app
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
报错Could not resolve placeholder 'xxx' in value "${xxx}" |
1. 检查配置文件中是否存在该key;2. 设置默认值(如${xxx:default});3. 检查配置文件是否加载 |
| 注入静态变量失败 | 1. 不能直接标注静态变量;2. 提供非静态setter方法,标注@Value并赋值给静态变量 |
2. @ConfigurationProperties
核心功能
批量绑定配置文件中的属性到Java Bean,支持复杂类型(List/Map)和数据校验,是@Value的升级版。
底层原理
- Spring通过
ConfigurationPropertiesBindingPostProcessor解析该注解; - 按
prefix匹配配置,支持松散绑定(如app.upload-path→uploadPath)。
实际示例
java
package com.example.demo.config;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotEmpty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import java.util.List;
import java.util.Map;
@ConfigurationProperties(prefix = "app")
@Validated // 启用数据校验
public class AppProperties {
@NotEmpty(message = "应用名称不能为空")
private String name;
private Upload upload;
private List<String> allowedIps;
private Map<String, String> envConfig;
// 嵌套配置
public static class Upload {
@Min(value = 1, message = "最小文件大小不能小于1MB")
@Max(value = 100, message = "最大文件大小不能超过100MB")
private Integer maxSize;
@NotEmpty(message = "上传路径不能为空")
private String path;
// getter/setter省略
}
// getter/setter省略
}
配置文件(application.yml)
yaml
app:
name: demo-app
upload:
max-size: 50
path: /usr/local/upload
allowed-ips:
- 127.0.0.1
- 192.168.1.1
env-config:
dev: development
prod: production
注册配置类
java
@Configuration
@EnableConfigurationProperties(AppProperties.class)
public class ConfigRegister {
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 绑定失败(字段为null) | 1. 检查prefix是否正确;2. 检查字段名是否支持松散绑定;3. 确保有setter方法;4. 加@EnableConfigurationProperties |
| 数据校验不生效 | 1. 加@Validated注解;2. 引入spring-boot-starter-validation依赖;3. 检查校验注解是否正确(jakarta.validation.constraints) |
3. @PropertySource
核心功能
加载自定义.properties配置文件,将配置项加入Spring Environment。
底层原理
- 通过
PropertySourceAnnotationBeanPostProcessor加载指定文件; - 将文件中的键值对封装为
PropertySource,注册到Environment。
实际示例
java
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
// 加载自定义配置文件,指定编码
@PropertySource(value = "classpath:custom.properties", encoding = "UTF-8")
@Configuration
public class CustomConfig {
}
自定义配置文件(custom.properties)
properties
custom.name=自定义配置
custom.age=20
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 加载YAML文件失败 | 1. @PropertySource默认不支持YAML;2. 自定义YAML加载器(实现PropertySourceFactory);3. 改用application.yml的多环境配置 |
| 配置文件找不到 | 1. 检查文件路径(classpath:/xxx.properties);2. 确保文件在resources目录下 |
4. @Profile
核心功能
指定Bean/配置类仅在特定环境(dev/test/prod)下生效,实现多环境配置隔离。
底层原理
- 通过
ProfileCondition条件判断; - 读取
spring.profiles.active配置,匹配则加载Bean,否则跳过。
实际示例
java
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
public class EnvConfig {
// 开发环境数据源
@Bean
@Profile("dev")
public DataSource devDataSource() {
System.out.println("加载开发环境数据源");
return new DevDataSource();
}
// 生产环境数据源
@Bean
@Profile({"prod", "default"})
public DataSource prodDataSource() {
System.out.println("加载生产环境数据源");
return new ProdDataSource();
}
// 静态内部类
public static class DevDataSource {}
public static class ProdDataSource {}
}
激活环境(application.yml)
yaml
spring:
profiles:
active: dev # 激活开发环境
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 无匹配的Profile时Bean加载失败 | 1. 指定默认Profile(如@Profile("default"));2. 确保spring.profiles.active配置正确 |
| 多Profile激活时Bean冲突 | 1. 确保不同Profile的Bean名称不同;2. 避免同时激活冲突的Profile |
5. @Validated
核心功能
开启参数校验,支持分组校验,可配合@ConfigurationProperties或Controller参数校验。
底层原理
- 基于JSR-380(jakarta.validation)实现;
- 通过
MethodValidationPostProcessor解析注解,触发校验。
实际示例
java
package com.example.demo.controller;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Validated // 开启Controller参数校验
public class ValidateController {
@PostMapping("/user/save")
public String saveUser(@RequestBody @Validated UserVO user) {
return "保存用户:" + user.getName();
}
// 分组校验
@PostMapping("/user/update")
public String updateUser(@RequestBody @Validated(UpdateGroup.class) UserVO user) {
return "更新用户:" + user.getId();
}
// 分组接口
public interface UpdateGroup {}
// 用户VO
public static class UserVO {
@NotNull(groups = UpdateGroup.class, message = "用户ID不能为空")
private Long id;
@NotEmpty(message = "用户名不能为空")
private String name;
// getter/setter省略
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 校验不生效 | 1. 加@Validated注解;2. 引入spring-boot-starter-validation依赖;3. 确保参数前加@Validated/@Valid |
| 分组校验不生效 | 1. 确保分组接口正确;2. 校验注解指定分组;3. 参数前指定分组(如@Validated(UpdateGroup.class)) |
五、Web MVC 接口开发(HTTP 请求级)
5.1 请求映射
1. @RequestMapping
核心功能
基础请求映射注解,绑定HTTP请求路径到控制器方法,支持指定请求方法、请求头、参数等。
底层原理
- Spring MVC通过
RequestMappingHandlerMapping扫描该注解,生成RequestMappingInfo; - 请求到来时,匹配
RequestMappingInfo找到对应的处理器方法。
实际示例
java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user") // 类级别路径
public class UserController {
// 匹配GET请求
@RequestMapping(value = "/info", method = RequestMethod.GET)
public String getUserInfo() {
return "用户信息";
}
// 多路径映射
@RequestMapping(value = {"/save", "/create"}, method = RequestMethod.POST)
public String saveUser() {
return "保存用户";
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 请求路径冲突 | 1. 区分请求方法(GET/POST);2. 增加请求头/参数条件;3. 调整路径命名 |
| Ant风格路径匹配错误 | 1. 明确规则(*匹配一级,**匹配多级);2. 避免过度使用通配符 |
2. @GetMapping / @PostMapping / @PutMapping / @DeleteMapping / @PatchMapping
核心功能
@RequestMapping的简化版,分别对应GET/POST/PUT/DELETE/PATCH请求,语义更清晰。
底层原理
- 底层继承
@RequestMapping,仅限定请求方法; - 其他原理与
@RequestMapping一致。
实际示例
java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/order")
public class OrderController {
// 查询(GET)
@GetMapping("/{id}")
public String getOrder(@PathVariable Long id) {
return "查询订单:" + id;
}
// 新增(POST)
@PostMapping
public String createOrder(@RequestBody OrderVO order) {
return "创建订单:" + order.getOrderNo();
}
// 全量更新(PUT)
@PutMapping("/{id}")
public String updateOrder(@PathVariable Long id, @RequestBody OrderVO order) {
return "更新订单:" + id;
}
// 删除(DELETE)
@DeleteMapping("/{id}")
public String deleteOrder(@PathVariable Long id) {
return "删除订单:" + id;
}
// 部分更新(PATCH)
@PatchMapping("/{id}/status")
public String updateOrderStatus(@PathVariable Long id, @RequestParam Integer status) {
return "更新订单" + id + "状态为:" + status;
}
public static class OrderVO {
private String orderNo;
// getter/setter省略
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| PUT/PATCH/DELETE请求被拦截 | 1. 配置跨域(@CrossOrigin);2. 配置Spring Security允许这些方法;3. 前端用X-HTTP-Method-Override模拟 |
| PATCH请求接收参数复杂 | 1. 用@RequestBody接收JSON;2. 避免传递过多参数 |
5.2 参数接收
1. @PathVariable
核心功能
从URL路径模板中提取变量(如/user/{id}),绑定到方法参数。
底层原理
- 通过
PathVariableMethodArgumentResolver解析; - 提取路径变量值,类型转换后绑定到参数。
实际示例
java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/product")
public class ProductController {
// 基础用法:参数名与路径变量名一致
@GetMapping("/info/{id}")
public String getProductInfo(@PathVariable Long id) {
return "商品ID:" + id;
}
// 参数名与路径变量名不一致
@GetMapping("/name/{productName}")
public String getProductName(@PathVariable("productName") String name) {
return "商品名称:" + name;
}
// 可选路径变量(Spring 5.3+)
@GetMapping("/optional/{id:\\d+}?")
public String getOptionalProduct(@PathVariable(required = false) Long id) {
return id == null ? "无商品ID" : "商品ID:" + id;
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 类型转换失败(如String→Long) | 1. 确保路径变量格式正确;2. 用正则限制格式(如{id:\\d+});3. 全局异常处理转换异常 |
| 可选路径变量不生效 | 1. 确保Spring版本≥5.3;2. 路径模板后加?(如/{id}?);3. 设置required = false |
2. @RequestParam
核心功能
从HTTP请求中提取查询参数(URL中的?key=value)或表单数据,绑定到方法参数。
底层原理
- 通过
RequestParamMethodArgumentResolver解析; - 从
HttpServletRequest.getParameterMap()获取值,支持多值参数(List)。
实际示例
java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/search")
public class SearchController {
// 必填参数
@GetMapping("/user")
public String searchUser(@RequestParam String name) {
return "搜索用户:" + name;
}
// 可选参数+默认值
@GetMapping("/product")
public String searchProduct(
@RequestParam(required = false, defaultValue = "手机") String keyword,
@RequestParam(defaultValue = "1") Integer pageNum) {
return "关键词:" + keyword + ",页码:" + pageNum;
}
// 多值参数
@GetMapping("/batch")
public String searchBatch(@RequestParam List<Long> ids) {
return "批量搜索ID:" + ids;
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 必选参数缺失 | 1. 设置required = false;2. 提供默认值;3. 全局异常处理返回友好提示 |
| 多值参数接收不到 | 1. 前端传递多个相同参数名(如ids=1&ids=2);2. 用逗号分隔字符串,后端拆分 |
3. @RequestBody
核心功能
从HTTP请求体中读取JSON/XML数据,转换为Java对象,绑定到方法参数。
底层原理
- 通过
RequestResponseBodyMethodProcessor解析; - 根据Content-Type选择
HttpMessageConverter(如Jackson处理JSON),反序列化为Java对象。
实际示例
java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api")
public class JsonController {
// 接收单个对象
@PostMapping("/user")
public String receiveUser(@RequestBody UserVO user) {
return "接收用户:" + user.getName();
}
// 接收数组
@PostMapping("/users")
public String receiveUsers(@RequestBody List<UserVO> users) {
return "接收" + users.size() + "个用户";
}
public static class UserVO {
private String name;
private Integer age;
// getter/setter省略
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 接收不到JSON数据 | 1. 检查Content-Type是否为application/json;2. 检查JSON格式;3. 检查实体类有setter方法;4. 用@JsonProperty匹配字段名 |
| 日期类型转换失败 | 1. 配置Jackson日期格式;2. 实体类字段加@JsonFormat(pattern = "yyyy-MM-dd") |
4. @RequestHeader / @CookieValue / @ModelAttribute
核心功能
@RequestHeader:获取请求头信息;@CookieValue:获取Cookie值;@ModelAttribute:绑定请求参数到对象,或预处理数据。
实际示例
java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/header")
public class HeaderController {
// 获取请求头
@GetMapping("/user-agent")
public String getUserAgent(@RequestHeader("User-Agent") String userAgent) {
return "User-Agent:" + userAgent;
}
// 获取Cookie
@GetMapping("/cookie")
public String getCookie(@CookieValue(value = "JSESSIONID", required = false) String sessionId) {
return "JSESSIONID:" + sessionId;
}
// @ModelAttribute预处理数据
@ModelAttribute
public void preProcess(@RequestParam(required = false) String token, Model model) {
model.addAttribute("token", token == null ? "default-token" : token);
}
// 使用预处理数据
@GetMapping("/model")
public String getModel(Model model) {
return "Token:" + model.getAttribute("token");
}
}
5.3 响应与跨域
1. @ResponseBody
核心功能
将方法返回值直接写入HTTP响应体(JSON/XML),而非解析为视图路径。
底层原理
- 通过
HttpMessageConverter转换返回值; @RestController已包含该注解,无需重复添加。
2. @CrossOrigin
核心功能
开启跨域访问支持,解决前后端分离中的跨域问题。
实际示例
java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
// 类级别:允许所有域名跨域
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/cors")
public class CorsController {
// 方法级别:仅允许指定域名
@CrossOrigin(origins = "http://localhost:8080")
@GetMapping("/info")
public String getInfo() {
return "跨域访问成功";
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 跨域仍报错 | 1. 检查origins是否配置正确;2. 确保maxAge设置合理;3. 全局配置跨域(替代注解) |
六、全局统一处理(AOP & 异常 & 增强)
1. @ControllerAdvice / @RestControllerAdvice
核心功能
全局控制器增强,@RestControllerAdvice = @ControllerAdvice + @ResponseBody,用于全局异常处理、数据绑定、预处理。
底层原理
- 被
@Component标注,会被Spring扫描为Bean; - Spring MVC初始化时,提取其中的
@ExceptionHandler/@InitBinder/@ModelAttribute方法,注册为全局处理器。
实际示例(全局异常处理)
java
package com.example.demo.advice;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
// 全局异常处理,返回JSON
@RestControllerAdvice
public class GlobalExceptionHandler {
// 处理参数校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidException(MethodArgumentNotValidException e) {
Map<String, String> errors = new HashMap<>();
e.getBindingResult().getFieldErrors().forEach(error -> {
errors.put(error.getField(), error.getDefaultMessage());
});
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
// 处理通用异常
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(Exception e) {
return new ResponseEntity<>("服务器内部错误:" + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 全局异常处理不生效 | 1. 检查是否加@RestControllerAdvice/@ControllerAdvice;2. 检查异常类型是否匹配;3. 检查扫描路径是否包含该类 |
| 特定Controller不适用全局处理 | 1. 在Controller内定义@ExceptionHandler(优先级更高);2. 用@RestControllerAdvice(basePackages = "com.example.demo.controller")限定范围 |
2. @ExceptionHandler
核心功能
标注在方法上,声明该方法用于处理指定类型的异常,通常配合@ControllerAdvice使用。
实际示例
见@RestControllerAdvice示例。
3. @Aspect / @Before / @After / @Around / @Pointcut
核心功能
AOP核心注解,实现切面编程,无侵入式增强方法(如日志、性能监控、权限校验)。
底层原理
@Aspect标记切面类;@Pointcut定义切点表达式;@Before/@After/@Around定义通知(增强逻辑)。
实际示例(接口性能监控)
java
package com.example.demo.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect // 标记切面类
@Component
public class PerformanceAspect {
// 定义切点:所有Controller的方法
@Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
public void controllerPointcut() {}
// 环绕通知:监控方法执行时间
@Around("controllerPointcut()")
public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
// 执行目标方法
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
// 打印耗时
System.out.println(joinPoint.getSignature() + " 执行耗时:" + (end - start) + "ms");
return result;
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 切面不生效 | 1. 检查是否加@Aspect和@Component;2. 检查切点表达式是否正确;3. 确保目标方法是public(AOP不支持private) |
| 内部调用切面不生效 | 1. 通过代理对象调用方法;2. 启用@EnableAspectJAutoProxy(exposeProxy = true),用AopContext.currentProxy()获取代理 |
七、数据访问与事务(数据库级)
1. @Transactional
核心功能
声明式事务管理核心注解,标注在类/方法上,开启数据库事务。
底层原理
- 通过Spring AOP实现,生成代理对象;
- 方法执行前开启事务,执行后提交,异常时回滚。
实际示例
java
package com.example.demo.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderService {
// 类级别:所有方法都开启事务
// @Transactional
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
// 方法级别:覆盖类级别配置
@Transactional(
rollbackFor = Exception.class, // 所有异常都回滚(默认仅RuntimeException)
propagation = Propagation.REQUIRED, // 默认传播行为
isolation = Isolation.READ_COMMITTED // 隔离级别
)
public void createOrder(OrderVO order) {
// 业务逻辑:新增订单 + 更新库存
orderRepository.save(order);
// 模拟异常:触发回滚
// throw new RuntimeException("创建订单失败");
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 事务失效 | 1. 方法非public(AOP不支持);2. 同类内部调用(未走代理);3. 异常被捕获未抛出;4. 异常类型不匹配(默认仅RuntimeException);5. 数据库引擎不支持事务(如MyISAM) |
| 事务回滚不全 | 1. 指定rollbackFor = Exception.class;2. 确保所有操作在同一个事务中;3. 检查是否有非事务操作(如调用外部接口) |
2. JPA 注解(@Entity / @Table / @Id / @GeneratedValue 等)
核心功能
JPA实体映射核心注解,实现Java对象与数据库表的映射。
实际示例
java
package com.example.demo.entity;
import jakarta.persistence.*;
import java.util.Date;
@Entity // 标记为JPA实体
@Table(name = "t_user") // 映射数据库表名
public class User {
@Id // 主键
@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增策略
private Long id;
@Column(name = "user_name", length = 50, nullable = false) // 字段映射
private String userName;
@Temporal(TemporalType.DATE) // 日期精度
private Date createTime;
@Transient // 不映射到数据库
private String tempField;
// getter/setter省略
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 实体映射失败 | 1. 检查注解是否正确(jakarta.persistence而非javax);2. 检查表名/字段名是否匹配;3. 确保有无参构造器 |
| 主键生成策略失效 | 1. 检查数据库是否支持自增(如MySQL支持IDENTITY,Oracle支持SEQUENCE);2. 确保主键字段类型匹配 |
3. MyBatis 注解(@Mapper / @MapperScan / @Select 等)
核心功能
MyBatis核心注解,实现SQL映射,替代XML文件。
实际示例
java
package com.example.demo.mapper;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper // 标记为MyBatis Mapper接口
public interface UserMapper {
// 查询
@Select("SELECT * FROM t_user WHERE id = #{id}")
User findById(Long id);
// 新增:获取自增主键
@Insert("INSERT INTO t_user(user_name) VALUES (#{userName})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
// 更新
@Update("UPDATE t_user SET user_name = #{userName} WHERE id = #{id}")
int update(User user);
// 删除
@Delete("DELETE FROM t_user WHERE id = #{id}")
int delete(Long id);
// 多参数:用@Param指定名称
@Select("SELECT * FROM t_user WHERE user_name = #{name} AND age = #{age}")
List<User> findByCondition(@Param("name") String name, @Param("age") Integer age);
}
扫描Mapper(启动类)
java
@SpringBootApplication
@MapperScan("com.example.demo.mapper") // 扫描所有Mapper接口
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| Mapper注入失败 | 1. 检查是否加@Mapper或@MapperScan;2. 检查扫描路径是否正确;3. 确保Mapper是接口(非类) |
| 多参数绑定失败 | 1. 用@Param指定参数名称;2. 用Map传递参数;3. 用实体类封装参数 |
八、高级能力:异步、定时、消息
1. @EnableAsync / @Async
核心功能
开启异步方法执行能力,标记方法为异步方法,调用后立即返回,逻辑在线程池中执行。
底层原理
@EnableAsync开启异步支持;@Async标注的方法被AOP代理,提交到线程池执行。
实际示例
java
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync // 开启异步支持
public class AsyncConfig {
// 自定义线程池(避免使用默认的SimpleAsyncTaskExecutor)
@Bean("asyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5); // 核心线程数
executor.setMaxPoolSize(10); // 最大线程数
executor.setQueueCapacity(20); // 队列容量
executor.setThreadNamePrefix("async-");
executor.initialize();
return executor;
}
}
// 异步方法
package com.example.demo.service;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncService {
// 使用自定义线程池
@Async("asyncExecutor")
public void asyncTask() {
// 耗时操作:如发送邮件、生成报表
System.out.println("异步任务执行中,线程:" + Thread.currentThread().getName());
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 异步方法失效 | 1. 未加@EnableAsync;2. 同类内部调用(未走代理);3. 方法非public;4. 返回值为void时异常无法捕获 |
| 线程池耗尽 | 1. 自定义线程池,设置合理参数;2. 监控线程池状态;3. 避免提交大量异步任务 |
2. @EnableScheduling / @Scheduled
核心功能
开启定时任务支持,标记方法为定时任务,支持cron表达式、固定频率、固定延迟。
底层原理
@EnableScheduling开启定时任务;@Scheduled标注的方法被TaskScheduler调度执行,默认单线程池。
实际示例
java
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.concurrent.ScheduledExecutorService;
@Configuration
@EnableScheduling // 开启定时任务
public class SchedulerConfig {
// 自定义定时任务线程池(避免单线程阻塞)
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(5);
scheduler.setThreadNamePrefix("scheduler-");
return scheduler;
}
}
// 定时任务
package com.example.demo.service;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Service
public class ScheduledService {
// cron表达式:每分钟执行一次
@Scheduled(cron = "0 * * * * ?")
public void cronTask() {
System.out.println("Cron任务执行:" + System.currentTimeMillis());
}
// 固定频率:每隔5秒执行(以上次开始时间计算)
@Scheduled(fixedRate = 5000)
public void fixedRateTask() {
System.out.println("固定频率任务执行");
}
// 固定延迟:每隔5秒执行(以上次结束时间计算)
@Scheduled(fixedDelay = 5000)
public void fixedDelayTask() {
System.out.println("固定延迟任务执行");
}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 定时任务阻塞 | 1. 自定义线程池(ThreadPoolTaskScheduler);2. 避免任务执行时间过长;3. 异步执行耗时任务 |
| cron表达式错误 | 1. 在线工具验证cron表达式;2. 注意秒/分/时/日/月/周的顺序;3. 避免日和周同时指定 |
3. 消息队列注解(@RabbitListener / @KafkaListener)
核心功能
标记方法为消息队列消费者,监听指定队列/主题,消息到达时自动执行。
实际示例(RabbitMQ)
java
package com.example.demo.listener;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class RabbitMQListener {
// 监听指定队列
@RabbitListener(queues = "demo.queue")
public void receiveMessage(String message) {
System.out.println("接收RabbitMQ消息:" + message);
}
}
九、条件装配(Spring Boot 自动配置灵魂)
1. @Conditional / @ConditionalOnClass / @ConditionalOnMissingBean 等
核心功能
根据条件决定是否加载Bean,是Spring Boot自动配置的核心机制。
实际示例
java
package com.example.demo.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ConditionConfig {
// 类路径下存在RedisTemplate时生效
@ConditionalOnClass(name = "org.springframework.data.redis.core.RedisTemplate")
@Bean
public RedisService redisService() {
return new RedisService();
}
// 容器中不存在MyService时才创建
@ConditionalOnMissingBean(MyService.class)
@Bean
public MyService defaultMyService() {
return new DefaultMyService();
}
// 配置文件中my.feature.enabled=true时生效
@ConditionalOnProperty(name = "my.feature.enabled", havingValue = "true")
@Bean
public FeatureService featureService() {
return new FeatureService();
}
// 静态内部类
public static class RedisService {}
public static class MyService {}
public static class DefaultMyService extends MyService {}
public static class FeatureService {}
}
使用痛点&解决方案
| 痛点 | 解决方案 |
|---|---|
| 条件装配不生效 | 1. 检查条件是否满足(如类是否存在、配置是否正确);2. 启动日志添加debug: true查看条件匹配结果;3. 确保条件注解标注在@Bean方法/配置类上 |
十、安全与权限
1. @EnableWebSecurity / @EnableGlobalMethodSecurity
核心功能
开启Spring Security Web安全支持和方法级权限控制。
实际示例
java
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity // 开启Web安全
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启@PreAuthorize
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll() // 公开接口
.anyRequest().authenticated() // 其他接口需认证
)
.formLogin(); // 启用表单登录
return http.build();
}
}
// 方法级权限
package com.example.demo.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/admin")
public class AdminController {
// 仅ADMIN角色可访问
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/info")
public String adminInfo() {
return "管理员信息";
}
// 支持SpEL表达式,更复杂的权限校验
@PreAuthorize("authentication.name == #username")
@GetMapping("/user/{username}")
public String userInfo(@PathVariable String username) {
# 十、安全与权限(续)
### 4. @PreAuthorize
#### 核心功能
方法执行**前**进行权限校验,支持 Spring EL 表达式,是 Spring Security 最常用、最灵活的权限注解。
#### 底层原理
- 需配合 `@EnableGlobalMethodSecurity(prePostEnabled = true)` 开启;
- Spring AOP 拦截方法调用,执行 EL 表达式,返回 false 则拒绝执行并抛异常。
#### 实际示例
```java
@RestController
@RequestMapping("/user")
public class UserController {
// 必须有 ADMIN 角色才能访问
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/list")
public List<User> list() {
return userService.list();
}
// 只能查看自己的信息:登录用户名 == 路径参数 username
@PreAuthorize("authentication.name == #username")
@GetMapping("/info/{username}")
public User info(@PathVariable String username) {
return userService.getByUsername(username);
}
// 拥有某个权限
@PreAuthorize("hasAuthority('user:delete')")
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) {
userService.delete(id);
}
}
高频痛点 & 解决方案
- 权限表达式不生效:
检查是否开启prePostEnabled = true; - EL 表达式写错导致一直拒绝:
优先使用hasRole/hasAuthority,少手写复杂逻辑; - 匿名访问也进入校验报错:
接口放行写在SecurityFilterChain里,不要依赖注解。
5. @PostAuthorize
核心功能
方法执行后 再做权限校验,常用于校验返回结果是否允许当前用户查看。
底层原理
- 方法先执行完,再执行 EL 表达式;
- 不满足则抛异常,不会回滚业务,只阻止结果返回。
实际示例
java
@Service
public class OrderService {
// 返回订单的用户名 == 当前登录用户才允许返回
@PostAuthorize("returnObject.username == authentication.name")
public Order getOrder(Long orderId) {
return orderMapper.selectById(orderId);
}
}
高频痛点 & 解决方案
- 数据已经查出来/修改完才被拒绝:
只适合查询,不适合写操作; - 性能差:
优先用@PreAuthorize,少用@PostAuthorize。
6. @AuthenticationPrincipal
核心功能
在 Controller 方法上直接获取当前登录用户信息(UserDetails 或自定义用户对象)。
底层原理
- Spring Security 从认证上下文
SecurityContextHolder中取出用户主体; - 自动绑定到方法参数。
实际示例
java
@GetMapping("/me")
public R currentUser(@AuthenticationPrincipal UserDetails userDetails) {
return R.ok(userDetails.getUsername());
}
// 如果你自定义了 LoginUser 实现 UserDetails,可以直接写
@GetMapping("/me")
public R me(@AuthenticationPrincipal LoginUser loginUser) {
return R.ok(loginUser.getUser());
}
高频痛点 & 解决方案
- 获取为 null:
接口未登录 / 过滤器未正确将用户放入上下文; - 无法获取自定义字段:
自定义UserDetails并在登录时完整赋值。
7. @Secured
核心功能
简单的角色校验 ,老版本 Spring Security 常用,不支持 EL 表达式。
底层原理
- 需开启
@EnableGlobalMethodSecurity(securedEnabled = true); - 仅判断是否拥有指定角色。
实际示例
java
@Secured("ROLE_ADMIN")
@GetMapping("/admin")
public String admin() {
return "admin page";
}
高频痛点 & 解决方案
- 功能太弱,不支持复杂判断:
新项目直接用@PreAuthorize替代。
8. @RolesAllowed
核心功能
Java JSR250 标准注解,作用同 @Secured,标准通用,不依赖 Spring。
实际示例
java
@RolesAllowed("ADMIN")
@GetMapping("/manage")
public String manage() {
return "manage";
}
十一、测试与切片注解(单元/集成测试)
这一组专门用于不启动整个项目、只测某一层,极大提升测试速度。
1. @SpringBootTest
核心功能
启动完整 Spring 容器 ,做集成测试,接近真实运行环境。
底层原理
- 加载
@SpringBootApplication启动类; - 初始化几乎所有 Bean、配置、自动配置。
实际示例
java
@SpringBootTest
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testList() {
List<User> list = userService.list();
Assert.notNull(list, "列表不能为空");
}
}
高频痛点 & 解决方案
- 启动极慢:
只在全链路集成测试用,单元测试用切片注解。
2. @WebMvcTest
核心功能
只启动 Web 层(Controller),不加载 Service、Repository,专门测接口。
底层原理
- 只实例化:Controller、HandlerInterceptor、Filter、MessageConverter 等;
- 依赖的 Service 必须用
@MockBean模拟。
实际示例
java
@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
public void testInfo() throws Exception {
// 模拟 Service 返回
User user = new User();
user.setId(1L);
user.setUsername("test");
Mockito.when(userService.getById(1L)).thenReturn(user);
// 模拟请求
mockMvc.perform(MockMvcRequestBuilders.get("/user/1"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("username").value("test"));
}
}
高频痛点 & 解决方案
- Service 注入失败:
必须用@MockBean模拟,不能真正注入。
3. @DataJpaTest
核心功能
只启动数据访问层,专门测试 Repository / JPA。
底层原理
- 自动配置数据源、JPA、Repository;
- 默认使用嵌入式数据库 (H2),测试完自动回滚,不污染真实库。
实际示例
java
@DataJpaTest
public class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Test
public void testSave() {
User user = new User();
user.setUsername("test");
User saved = userRepository.save(user);
Assert.notNull(saved.getId(), "保存后ID不能为空");
}
}
高频痛点 & 解决方案
- 不想用 H2,想用真实库:
添加@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)。
4. @MockBean
核心功能
向 Spring 容器中放入一个模拟对象,替换真实 Bean,用于隔离依赖。
底层原理
- 基于 Mockito;
- 生成代理对象,不会执行真实逻辑。
实际示例
java
@SpringBootTest
public class OrderServiceTest {
@Autowired
private OrderService orderService;
@MockBean
private UserService userService; // 用模拟对象替换
@Test
public void testCreate() {
// 模拟 userService 返回
Mockito.when(userService.getById(1L)).thenReturn(new User());
orderService.create(1L);
}
}
高频痛点 & 解决方案
- 模拟不生效:
确保是@MockBean(Spring 注解),不是@Mock(纯 Mockito)。
5. @SpyBean
核心功能
半真半假 :真实对象,可部分模拟 ,没被 mock 的方法会真实执行。
实际示例
java
@SpyBean
private UserService userService;
@Test
public void testSpy() {
// 只模拟 getById,其他方法真实走业务
Mockito.when(userService.getById(1L)).thenReturn(new User());
}
6. @Sql
核心功能
测试执行前后自动跑 SQL 脚本,用于初始化测试数据。
实际示例
java
@SpringBootTest
// 执行测试前跑脚本
@Sql(scripts = "classpath:init-test-data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
public class SqlTest {
@Test
public void testQuery() {
// 数据库已有初始化数据
}
}
7. @TestPropertySource
核心功能
给测试单独指定配置文件,不与正式环境混用。
实际示例
java
@SpringBootTest
@TestPropertySource("classpath:application-test.yml")
public class ConfigTest {
}
十二、日志、缓存、接口文档(常用扩展注解)
1. @Slf4j
核心功能
Lombok 提供,自动生成 log 常量 ,不用手写 private final Logger log = ...。
实际示例
java
@Service
@Slf4j
public class UserService {
public void list() {
log.info("查询用户列表");
}
}
2. @Cacheable / @CacheEvict / @CachePut
核心功能
Spring 声明式缓存,简化 Redis 等缓存操作。
实际示例
java
// 查询:缓存不存在则执行方法,结果存入缓存
@Cacheable(value = "user", key = "#id")
public User getById(Long id) {
return userMapper.selectById(id);
}
// 更新:更新数据库,并更新缓存
@CachePut(value = "user", key = "#user.id")
public User update(User user) {
userMapper.updateById(user);
return user;
}
// 删除:删除缓存
@CacheEvict(value = "user", key = "#id")
public void delete(Long id) {
userMapper.deleteById(id);
}
3. 接口文档(SpringDoc / Knife4j)
java
@Tag(name = "用户管理")
@RestController
@RequestMapping("/user")
public class UserController {
@Operation(summary = "获取用户详情")
@Parameter(name = "id", description = "用户ID")
@GetMapping("/{id}")
public User getById(@PathVariable Long id) {
return userService.getById(id);
}
}
整套注解逻辑总结(背会这一段,Spring Boot 彻底通)
- 先启动 :
@SpringBootApplication全家桶 - 再装 Bean :
@Configuration+@Bean、@Service、@Component - 再注入 :
@Autowired、@Resource、@Qualifier - 读配置 :
@Value、@ConfigurationProperties、@Profile - 写接口 :
@RestController、@GetMapping、@RequestBody - 统一处理 :
@RestControllerAdvice、@ExceptionHandler、@Aspect - 数据库 :
@Transactional、@Mapper、@Entity - 高级能力 :
@Async、@Scheduled、@RabbitListener - 条件加载 :
@ConditionalOnXxx - 安全 :
@PreAuthorize - 测试 :
@WebMvcTest、@MockBean