跳到主要内容Spring Boot 核心注解完全手册 | 极客日志Javajava
Spring Boot 核心注解完全手册
系统梳理了 Spring Boot 3.2+ 中的 11 大核心场景注解,涵盖启动、Bean 定义、依赖注入、配置绑定、Web 开发、全局处理、数据访问、高级能力、条件装配、安全及测试等模块。每个注解详解核心功能、底层原理、可运行示例及高频痛点解决方案,旨在帮助开发者快速掌握 Spring Boot 注解用法,提升企业级开发效率与排错能力。
DataScient0 浏览 Spring Boot 核心注解完全手册(逻辑版·痛点 + 原理 + 实战)
本文按企业级开发流程逻辑重构,覆盖 11 大核心场景,每个注解均包含「核心功能 + 底层原理 + 可运行示例 + 高频痛点 & 解决方案」,既是开发手册也是排错指南(基于 Spring Boot 3.2+)。
一、启动与自动配置(应用入口级)
1. @SpringBootApplication
核心功能
Spring Boot 应用唯一入口注解,三合一组合注解,标记主启动类,触发自动配置和组件扫描。
底层原理
@SpringBootConfiguration:本质是 @Configuration,标记主类为配置类;
@EnableAutoConfiguration:触发自动配置机制;
@ComponentScan:默认扫描主类所在包及其子包的组件。
启动时通过 SpringApplication 加载上下文,解析该注解完成初始化。实际示例
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
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 条件判断是否生效。
实际示例
@EnableAutoConfiguration
@Configuration
public class AutoConfigDemo {
}
使用痛点 & 解决方案
| 痛点 | 解决方案 |
|---|
| 不知道哪些自动配置生效/失效 | 在 application.yml 中添加 debug: true,启动日志会打印 Positive matches(生效)和 Negative matches(失效) |
| 自动配置类加载过多导致启动慢 | 用 @SpringBootApplication(exclude) 排除无需的自动配置;或自定义 starter 缩小依赖范围 |
3. @Import
核心功能
手动导入配置类/普通类到 Spring 容器,突破 @ComponentScan 的扫描限制。
底层原理
- 通过
ImportBeanDefinitionRegistrar 或 ImportSelector 将指定类注册为 Bean;
- 优先级高于
@ComponentScan,可导入非扫描路径下的类。
实际示例
package com.example.demo.config;
import com.example.external.ExternalService;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Configuration;
@Import(ExternalService.class)
@Configuration
public class ImportConfig {
}
使用痛点 & 解决方案
| 痛点 | 解决方案 |
|---|
| 导入的类无默认构造器导致初始化失败 | 确保导入类有无参构造器;或通过 @Bean 手动创建实例 |
| 导入过多类导致配置混乱 | 按功能分组导入,避免一次性导入大量类 |
4. @ComponentScan
核心功能
显式指定 Spring 组件扫描路径,替代默认扫描规则。
底层原理
- 通过
ClassPathScanningCandidateComponentProvider 扫描指定路径;
- 识别标注
@Component/@Service/@Controller 等注解的类,注册为 Bean。
实际示例
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 方法互相调用的场景。
实际示例
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
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() 时执行方法。
实际示例
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfig {
@Bean
public UserService userService() {
return new UserService();
}
@Bean(name = "customDataSource", initMethod = "init", destroyMethod = "close")
public DataSource dataSource() {
return new CustomDataSource();
}
@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,默认名称为类名首字母小写。
实际示例
package com.example.demo.component;
import org.springframework.stereotype.Component;
@Component
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 无区别,仅作为代码分层标识。
实际示例
package com.example.demo.service;
import org.springframework.stereotype.Service;
@Service
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 为该注解标注的类生成代理,实现异常转换。
实际示例
package com.example.demo.repository;
import org.springframework.stereotype.Repository;
@Repository
public class UserRepository {
public String findById(Long id) {
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 方法,绑定请求。
实际示例
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 {
@GetMapping("/user/page")
public String userPage() {
return "user";
}
@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 响应体。
实际示例
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RestApiController {
@GetMapping("/order/info")
public OrderVO getOrderInfo() {
OrderVO vo = new OrderVO();
vo.setId(1L);
vo.setOrderNo("20240310001");
vo.setAmount(99.9);
return vo;
}
public static class OrderVO {
private Long id;
private String orderNo;
private Double amount;
}
}
使用痛点 & 解决方案
| 痛点 | 解决方案 |
|---|
| 返回的 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 → 找到多个则按名称匹配 → 匹配失败抛出异常;
- 优先级:构造器注入 > 方法注入 > 字段注入。
实际示例
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;
}
@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。
实际示例
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")
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。
实际示例
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 {
@Primary
@Bean("masterDataSource")
public DataSource masterDataSource() {
return new MasterDataSource();
}
@Bean("slaveDataSource")
public DataSource slaveDataSource() {
return new SlaveDataSource();
}
}
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;
}
使用痛点 & 解决方案
| 痛点 | 解决方案 |
|---|
| 同一类型多个 @Primary Bean | 报错 IllegalStateException,确保同一类型只有一个 @Primary Bean |
| @Primary 不生效 | 检查是否同时使用了 @Qualifier(优先级更高);检查 Bean 是否注册成功 |
4. @Resource
核心功能
JSR-250 标准注解,默认按名称(byName)装配 Bean,找不到名称时按类型(byType)装配,是 @Autowired 的替代方案。
底层原理
- Spring 通过
CommonAnnotationBeanPostProcessor 解析该注解;
- 装配优先级:指定
name → 字段名/方法名 → 类型 → 抛出异常。
实际示例
package com.example.demo.service;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
@Service
public class CartService {
@Resource
private CartRepository cartRepository;
@Resource(name = "vipCartRepository")
private CartRepository vipCartRepository;
@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 请求/会话。
实际示例
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();
}
@Bean
@Scope("prototype")
public PrototypeBean prototypeBean() {
return new PrototypeBean();
}
@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 方法时才真正创建实例。
实际示例
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 解析该注解。
实际示例
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;
public CacheService() {
}
@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。
实际示例
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) {
}
}
private static class Connection {}
}
使用痛点 & 解决方案
| 痛点 | 解决方案 |
|---|
| @PreDestroy 方法未执行 | 1. 确保是单例 Bean;2. 确保容器正常关闭(如调用 ApplicationContext.close());3. 检查是否有异常导致方法未执行 |
| 资源释放失败 | 1. 捕获异常并记录日志;2. 确保资源释放逻辑幂等 |
四、配置绑定与多环境(外部配置级)
1. @Value
核心功能
从配置文件/环境变量中注入单个值,支持 SpEL 表达式,适合简单配置。
底层原理
- Spring 通过
AutowiredAnnotationBeanPostProcessor 解析该注解;
- 解析
${key} 从 Environment 获取值,解析 #{spel} 执行表达式。
实际示例
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;
@Value("#{@appConfig.appName + '-test'}")
private String testAppName;
public void printConfig() {
System.out.println("appName: " + appName);
System.out.println("appVersion: " + appVersion);
}
}
配置文件(application.yml)
使用痛点 & 解决方案
| 痛点 | 解决方案 |
|---|
报错 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)。
实际示例
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;
}
}
配置文件(application.yml)
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
注册配置类
@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。
实际示例
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)
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,否则跳过。
实际示例
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)
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 解析注解,触发校验。
实际示例
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
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 {}
public static class UserVO {
@NotNull(groups = UpdateGroup.class, message = "用户 ID 不能为空")
private Long id;
@NotEmpty(message = "用户名不能为空")
private String name;
}
}
使用痛点 & 解决方案
| 痛点 | 解决方案 |
|---|
| 校验不生效 | 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 找到对应的处理器方法。
实际示例
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 {
@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 一致。
实际示例
package com.example.demo.controller;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/order")
public class OrderController {
@GetMapping("/{id}")
public String getOrder(@PathVariable Long id) {
return "查询订单:" + id;
}
@PostMapping
public String createOrder(@RequestBody OrderVO order) {
return "创建订单:" + order.getOrderNo();
}
@PutMapping("/{id}")
public String updateOrder(@PathVariable Long id, @RequestBody OrderVO order) {
return "更新订单:" + id;
}
@DeleteMapping("/{id}")
public String deleteOrder(@PathVariable Long id) {
return "删除订单:" + id;
}
@PatchMapping("/{id}/status")
public String updateOrderStatus(@PathVariable Long id, @RequestParam Integer status) {
return "更新订单" + id + "状态为:" + status;
}
public static class OrderVO {
private String orderNo;
}
}
使用痛点 & 解决方案
| 痛点 | 解决方案 |
|---|
| 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 解析;
- 提取路径变量值,类型转换后绑定到参数。
实际示例
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;
}
@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)。
实际示例
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 对象。
实际示例
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;
}
}
使用痛点 & 解决方案
| 痛点 | 解决方案 |
|---|
| 接收不到 JSON 数据 | 1. 检查 Content-Type 是否为 application/json;2. 检查 JSON 格式;3. 检查实体类有 setter 方法;4. 用 @JsonProperty 匹配字段名 |
| 日期类型转换失败 | 1. 配置 Jackson 日期格式;2. 实体类字段加 @JsonFormat(pattern = "yyyy-MM-dd") |
核心功能
@RequestHeader:获取请求头信息;
@CookieValue:获取 Cookie 值;
@ModelAttribute:绑定请求参数到对象,或预处理数据。
实际示例
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;
}
@GetMapping("/cookie")
public String getCookie(@CookieValue(value = "JSESSIONID", required = false) String sessionId) {
return "JSESSIONID:" + sessionId;
}
@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
核心功能
实际示例
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 方法,注册为全局处理器。
实际示例(全局异常处理)
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;
@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 定义通知(增强逻辑)。
实际示例(接口性能监控)
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 {
@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 实现,生成代理对象;
- 方法执行前开启事务,执行后提交,异常时回滚。
实际示例
package com.example.demo.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderService {
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);
}
}
使用痛点 & 解决方案
| 痛点 | 解决方案 |
|---|
| 事务失效 | 1. 方法非 public(AOP 不支持);2. 同类内部调用(未走代理);3. 异常被捕获未抛出;4. 异常类型不匹配(默认仅 RuntimeException);5. 数据库引擎不支持事务(如 MyISAM) |
| 事务回滚不全 | 1. 指定 rollbackFor = Exception.class;2. 确保所有操作在同一个事务中;3. 检查是否有非事务操作(如调用外部接口) |
2. JPA 注解(@Entity / @Table / @Id / @GeneratedValue 等)
核心功能
JPA 实体映射核心注解,实现 Java 对象与数据库表的映射。
实际示例
package com.example.demo.entity;
import jakarta.persistence.*;
import java.util.Date;
@Entity
@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;
}
使用痛点 & 解决方案
| 痛点 | 解决方案 |
|---|
| 实体映射失败 | 1. 检查注解是否正确(jakarta.persistence 而非 javax);2. 检查表名/字段名是否匹配;3. 确保有无参构造器 |
| 主键生成策略失效 | 1. 检查数据库是否支持自增(如 MySQL 支持 IDENTITY,Oracle 支持 SEQUENCE);2. 确保主键字段类型匹配 |
3. MyBatis 注解(@Mapper / @MapperScan / @Select 等)
核心功能
MyBatis 核心注解,实现 SQL 映射,替代 XML 文件。
实际示例
package com.example.demo.mapper;
import org.apache.ibatis.annotations.*;
import java.util.List;
@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);
@Select("SELECT * FROM t_user WHERE user_name = #{name} AND age = #{age}")
List<User> findByCondition(@Param("name") String name, @Param("age") Integer age);
}
扫描 Mapper(启动类)
@SpringBootApplication
@MapperScan("com.example.demo.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 代理,提交到线程池执行。
实际示例
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 {
@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 调度执行,默认单线程池。
实际示例
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 {
@Scheduled(cron = "0 * * * * ?")
public void cronTask() {
System.out.println("Cron 任务执行:" + System.currentTimeMillis());
}
@Scheduled(fixedRate = 5000)
public void fixedRateTask() {
System.out.println("固定频率任务执行");
}
@Scheduled(fixedDelay = 5000)
public void fixedDelayTask() {
System.out.println("固定延迟任务执行");
}
}
使用痛点 & 解决方案
| 痛点 | 解决方案 |
|---|
| 定时任务阻塞 | 1. 自定义线程池(ThreadPoolTaskScheduler);2. 避免任务执行时间过长;3. 异步执行耗时任务 |
| cron 表达式错误 | 1. 在线工具验证 cron 表达式;2. 注意秒/分/时/日/月/周的顺序;3. 避免日和周同时指定 |
3. 消息队列注解(@RabbitListener / @KafkaListener)
核心功能
标记方法为消息队列消费者,监听指定队列/主题,消息到达时自动执行。
实际示例(RabbitMQ)
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 自动配置的核心机制。
实际示例
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 {
@ConditionalOnClass(name = "org.springframework.data.redis.core.RedisTemplate")
@Bean
public RedisService redisService() {
return new RedisService();
}
@ConditionalOnMissingBean(MyService.class)
@Bean
public MyService defaultMyService() {
return new DefaultMyService();
}
@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 安全支持和方法级权限控制。
实际示例
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
@EnableGlobalMethodSecurity(prePostEnabled = true)
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 {
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/info")
public String adminInfo() {
return "管理员信息";
}
@PreAuthorize("authentication.name == #username")
@GetMapping("/user/{username}")
public String userInfo(@PathVariable String username) {
return "用户信息:" + username;
}
}
高频痛点 & 解决方案
- 权限表达式不生效:检查是否开启
prePostEnabled = true;
- EL 表达式写错导致一直拒绝:优先使用
hasRole/hasAuthority,少手写复杂逻辑;
- 匿名访问也进入校验报错:接口放行写在
SecurityFilterChain 里,不要依赖注解。
4. @PreAuthorize
核心功能
方法执行前进行权限校验,支持 SpringEL 表达式,是 SpringSecurity 最常用、最灵活的权限注解。
底层原理
- 需配合
@EnableGlobalMethodSecurity(prePostEnabled =true) 开启;
- SpringAOP 拦截方法调用,执行 EL 表达式,返回 false 则拒绝执行并抛异常。
实际示例
@RestController
@RequestMapping("/user")
public class UserController {
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/list")
public List<User> list() {
return userService.list();
}
@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 表达式;
- 不满足则抛异常,不会回滚业务,只阻止结果返回。
实际示例
@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 中取出用户主体;
- 自动绑定到方法参数。
实际示例
@GetMapping("/me")
public R currentUser(@AuthenticationPrincipal UserDetails userDetails) {
return R.ok(userDetails.getUsername());
}
@GetMapping("/me")
public R me(@AuthenticationPrincipal LoginUser loginUser) {
return R.ok(loginUser.getUser());
}
高频痛点 & 解决方案
- 获取为 null:接口未登录 / 过滤器未正确将用户放入上下文;
- 无法获取自定义字段:自定义
UserDetails 并在登录时完整赋值。
7. @Secured
核心功能
简单的角色校验,老版本 Spring Security 常用,不支持 EL 表达式。
底层原理
- 需开启
@EnableGlobalMethodSecurity(securedEnabled = true);
- 仅判断是否拥有指定角色。
实际示例
@Secured("ROLE_ADMIN")
@GetMapping("/admin")
public String admin() {
return "admin page";
}
高频痛点 & 解决方案
- 功能太弱,不支持复杂判断:新项目直接用
@PreAuthorize 替代。
8. @RolesAllowed
核心功能
Java JSR250 标准注解,作用同 @Secured,标准通用,不依赖 Spring。
实际示例
@RolesAllowed("ADMIN")
@GetMapping("/manage")
public String manage() {
return "manage";
}
十一、测试与切片注解(单元/集成测试)
这一组专门用于不启动整个项目、只测某一层,极大提升测试速度。
1. @SpringBootTest
核心功能
启动完整 Spring 容器,做集成测试,接近真实运行环境。
底层原理
- 加载
@SpringBootApplication 启动类;
- 初始化几乎所有 Bean、配置、自动配置。
实际示例
@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 模拟。
实际示例
@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
public void testInfo() throws Exception {
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),测试完自动回滚,不污染真实库。
实际示例
@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;
- 生成代理对象,不会执行真实逻辑。
实际示例
@SpringBootTest
public class OrderServiceTest {
@Autowired
private OrderService orderService;
@MockBean
private UserService userService;
@Test
public void testCreate() {
Mockito.when(userService.getById(1L)).thenReturn(newUser());
orderService.create(1L);
}
}
高频痛点 & 解决方案
- 模拟不生效:确保是
@MockBean(Spring 注解),不是 @Mock(纯 Mockito)。
5. @SpyBean
核心功能
半真半假:真实对象,可部分模拟,没被 mock 的方法会真实执行。
实际示例
@SpyBean
private UserService userService;
@Test
public void testSpy() {
Mockito.when(userService.getById(1L)).thenReturn(newUser());
}
6. @Sql
核心功能
测试执行前后自动跑 SQL 脚本,用于初始化测试数据。
实际示例
@SpringBootTest
@Sql(scripts = "classpath:init-test-data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
public class SqlTest {
@Test
public void testQuery() {
}
}
7. @TestPropertySource
核心功能
实际示例
@SpringBootTest
@TestPropertySource("classpath:application-test.yml")
public class ConfigTest {}
十二、日志、缓存、接口文档(常用扩展注解)
1. @Slf4j
核心功能
Lombok 提供,自动生成 log 常量,不用手写 private final Logger log = ...。
实际示例
@Service
@Slf4j
public class UserService {
public void list() {
log.info("查询用户列表");
}
}
2. @Cacheable / @CacheEvict / @CachePut
核心功能
Spring 声明式缓存,简化 Redis 等缓存操作。
实际示例
@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)
@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
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online