本文系统梳理 Spring 框架中容器管理和 WebMVC 相关的核心注解,结合使用场景、属性说明和实战示例,帮助开发者深入理解注解的底层逻辑与正确用法。
一、容器相关注解
容器相关注解是 Spring IoC 容器实现 Bean 定义、加载、依赖管理的核心,决定了 Bean 的生命周期、加载条件和依赖关系。
1. @Configuration
@Configuration 是 Spring 3.0 引入的核心注解,用于替代传统 XML 配置文件,标记类为配置类,是 Bean 定义的核心载体。
- 作用:标注在类上,声明该类为 Spring 配置类,配合
@Bean注解定义 Bean; - 特性:被标注的类本身也会被注册为 Bean,纳入 Spring 容器管理;
- 底层逻辑:默认情况下,配置类会被 CGLIB 代理,保证
@Bean方法调用时返回容器中已初始化的单例 Bean,而非新创建实例。
属性说明:
| 属性 | 说明 | 补充说明 |
|---|---|---|
| value | Bean 名称 | 可选,默认使用类名首字母小写作为 Bean 名称 |
| proxyBeanMethods | 指定 @Bean 方法是否启用 CGLIB 代理 | ① true(默认):代理模式,调用 @Bean 方法返回容器中的 Bean;② false:轻量模式,调用 @Bean 方法直接 new 新对象,适合无依赖的简单 Bean,提升启动性能 |
2. @Bean
@Bean 用于在配置类中显式定义 Bean,是 XML 配置中<bean>标签的注解等价实现。
- 作用:标注在方法上,方法返回值将被注册为 Spring 容器中的 Bean;
- 作用域:方法必须定义在
@Configuration或@Component标注的类中; - 生命周期:支持指定初始化和销毁方法,对应 XML 中的
init-method和destroy-method。
属性说明:
| 属性 | 说明 | 补充说明 |
|---|---|---|
| value | 指定 Bean 名称 | 与name属性等价,优先级相同,支持数组形式定义多个别名 |
| name | 指定 Bean 名称 | 推荐使用,语义更清晰,如name = "userService" |
| autowireCandidate | 是否作为自动装配候选 Bean | ① true(默认):允许被@Autowired自动装配;② false:排除自动装配,仅能通过BeanFactory手动获取 |
| initMethod | 初始化方法名 | 方法需无参、无返回值,Bean 初始化完成后执行,替代InitializingBean接口 |
| destroyMethod | 销毁方法名 | 方法需无参、无返回值,容器关闭时执行(仅单例 Bean 生效),替代DisposableBean接口,默认会检测close()/shutdown()方法 |
3. @DependsOn
@DependsOn 用于声明 Bean 之间的依赖顺序,确保指定 Bean 在当前 Bean 之前创建。
- 非强依赖:区别于字段注入 / 构造器注入的 "依赖注入",仅保证创建顺序,不涉及属性赋值;
- 适用场景:如数据源 Bean 需在数据访问 Bean 之前初始化,或启动时需先加载配置 Bean。
属性说明:
| 属性 | 说明 |
|---|---|
| value | 依赖的 Bean 名称数组 |
示例:
java
@Configuration
public class DependsOnConfig {
@Bean
public DataSource dataSource() {
return new DruidDataSource();
}
@Bean
@DependsOn("dataSource") // 确保 dataSource 先创建
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
}
4. @ComponentScan
@ComponentScan 用于指定 Spring 扫描 Bean 的包路径,自动注册标注了@Component/@Service/@Repository/@Controller的类。
- 核心逻辑:通过过滤器(Filter)筛选需要纳入容器的类,支持包含 / 排除规则;
- 默认行为:未指定包路径时,扫描当前注解所在类的包及其子包。
属性说明:
| 属性 | 说明 | 补充说明 |
|---|---|---|
| value | 扫描的基础包路径 | 与basePackages等价,如value = "com.example.service" |
| nameGenerator | Bean 名称生成器 | 自定义 Bean 名称规则,需实现BeanNameGenerator接口 |
| scopeResolver | Bean 作用域解析器 | 自定义作用域解析逻辑,需实现ScopeMetadataResolver接口 |
| basePackages | 指定扫描的基础包 | 支持数组形式,如basePackages = {"com.example.service", "com.example.dao"} |
| basePackageClasses | 指定扫描的基准类 | 扫描指定类所在的包,如basePackageClasses = UserService.class |
| useDefaultFilters | 是否启用默认过滤器 | ① true(默认):扫描@Component及其衍生注解;② false:仅扫描自定义过滤器匹配的类 |
| includeFilters | 包含过滤器 | 仅匹配规则的类被扫描,如指定扫描标注@MyAnnotation的类 |
| excludeFilters | 排除过滤器 | 匹配规则的类不被扫描,如排除@Controller标注的类 |
| lazyInit | 是否懒加载扫描到的 Bean | ① true:所有扫描的 Bean 均懒加载;② false(默认):单例 Bean 立即加载 |
示例:
java
@Configuration
// 扫描指定包,排除 Controller,仅包含 Service
@ComponentScan(
basePackages = "com.example",
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class),
includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Service.class)
)
public class ScanConfig {
}
5. @ConditionalOnBean
@ConditionalOnBean 是 Spring Boot 条件注解,仅当容器中存在指定 Bean 时,才加载当前 Bean。
- 适用场景:按需加载 Bean,如仅当 RedisTemplate 存在时,才创建 Redis 缓存管理器。
属性说明:
| 属性 | 说明 | 补充说明 |
|---|---|---|
| value | 指定 Bean 的 Class 类型 | 如value = RedisTemplate.class |
| type | 指定 Bean 的类型名称 | 字符串形式,避免类依赖,如type = "org.springframework.data.redis.core.RedisTemplate" |
| annotation | 指定 Bean 需包含的注解 | 如annotation = Repository.class(仅扫描标注 @Repository 的 Bean) |
| name | 指定 Bean 的名称 | 如name = "redisTemplate" |
| search | 是否搜索父容器 / 祖先容器 | 枚举值:SearchStrategy.CURRENT(仅当前容器,默认)、SearchStrategy.PARENTS(父容器)、SearchStrategy.ALL(所有祖先容器) |
| parameterizedContainer | 指定参数化类型容器 | 匹配泛型容器中的 Bean,如parameterizedContainer = List.class(匹配 List 类型的 Bean) |
示例:
java
@Configuration
public class ConditionalBeanConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate() {
return new RedisTemplate<>();
}
// 仅当 redisTemplate 存在时,创建 RedisCacheManager
@Bean
@ConditionalOnBean(name = "redisTemplate")
public RedisCacheManager cacheManager() {
return RedisCacheManager.builder(redisTemplate()).build();
}
}
6. @ConditionalOnMissingBean
@ConditionalOnMissingBean 与@ConditionalOnBean相反,仅当容器中不存在指定 Bean 时,才加载当前 Bean。
- 核心用途:实现 "默认 Bean" 逻辑,如自定义 Bean 不存在时,加载框架默认实现。
属性说明:
| 属性 | 说明 |
|---|---|
| ignored | 忽略匹配的 Class |
| ignoredType | 忽略匹配的类型名称 |
示例:
java
@Configuration
public class DefaultBeanConfig {
// 仅当容器中无 UserService 时,加载默认实现
@Bean
@ConditionalOnMissingBean(type = "com.example.service.UserService")
public UserService defaultUserService() {
return new DefaultUserService();
}
}
7. @ConditionalOnClass
@ConditionalOnClass 仅当类路径中存在指定 Class 时,才加载当前 Bean。
- 适用场景:适配不同依赖环境,如仅当引入 MyBatis 依赖时,加载 MyBatis 配置 Bean。
属性说明:
| 属性 | 说明 | 补充说明 |
|---|---|---|
| value | 指定 Class 类型 | 如value = SqlSessionFactory.class |
| name | 指定 Class 全限定名 | 避免编译期依赖,如name = "org.apache.ibatis.session.SqlSessionFactory" |
示例:
java
@Configuration
// 仅当类路径存在 SqlSessionFactory 时,加载 MyBatis 配置
@ConditionalOnClass(SqlSessionFactory.class)
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
return factoryBean.getObject();
}
}
8. @ConditionalOnMissingClass
@ConditionalOnMissingClass 仅当类路径中不存在指定 Class 时,才加载当前 Bean。
- 适用场景:降级处理,如未引入某个依赖时,加载兜底实现。
属性说明:
| 属性 | 说明 |
|---|---|
| value | 指定 Class 全限定名数组 |
示例:
java
@Configuration
// 未引入 Redis 时,加载本地缓存
@ConditionalOnMissingClass("org.springframework.data.redis.core.RedisTemplate")
public class LocalCacheConfig {
@Bean
public CacheManager localCacheManager() {
return new ConcurrentMapCacheManager();
}
}
9. @ConditionalOnProperty
@ConditionalOnProperty 仅当配置文件中存在指定属性(且值匹配)时,才加载当前 Bean。
- 核心用途:通过配置开关控制 Bean 加载,实现环境适配。
属性说明:
| 属性 | 说明 | 补充说明 |
|---|---|---|
| value | 属性名数组 | 与name等价,如value = "spring.cache.enabled" |
| prefix | 属性名前缀 | 简化属性名书写,如prefix = "spring.cache" + name = "enabled" 等价于spring.cache.enabled |
| name | 属性名 | 支持数组,如name = {"enabled", "type"} |
| havingValue | 指定属性值 | 匹配属性的具体值,如havingValue = "redis" |
| matchIfMissing | 属性不存在时是否匹配 | ① true:属性不存在时仍加载 Bean;② false(默认):属性不存在则不加载 |
示例:
java
@Configuration
// 当 spring.cache.enabled=true 且 type=redis 时,加载 Redis 缓存
@ConditionalOnProperty(
prefix = "spring.cache",
name = {"enabled", "type"},
havingValue = "redis",
matchIfMissing = false
)
public class RedisCacheConfig {
// ...
}
10. @ConditionalOnResource
@ConditionalOnResource 仅当类路径中存在指定资源文件时,才加载当前 Bean。
- 适用场景:加载自定义配置文件,如仅当存在
app-custom.properties时,加载对应配置。
属性说明:
| 属性 | 说明 | 补充说明 |
|---|---|---|
| value | 资源路径数组 | 支持 classpath 路径,如value = "classpath:app-custom.properties" |
示例:
java
@Configuration
// 仅当存在自定义配置文件时,加载配置
@ConditionalOnResource(resources = "classpath:app-custom.properties")
@PropertySource("classpath:app-custom.properties")
public class CustomConfig {
// ...
}
11. @ConditionalOnSingleCandidate
@ConditionalOnSingleCandidate 仅当容器中指定类型的 Bean仅有一个候选 Bean(或有 @Primary 标记的 Bean)时,才加载当前 Bean。
- 适用场景:避免多 Bean 冲突,如仅当数据源 Bean 唯一时,加载 JdbcTemplate。
属性说明:
| 属性 | 说明 |
|---|---|
| value | Bean 的 Class 类型 |
| type | Bean 的 Class 全限定名 |
| search | 是否搜索父容器 |
示例:
java
@Configuration
// 仅当数据源 Bean 唯一时,创建 JdbcTemplate
@ConditionalOnSingleCandidate(DataSource.class)
public class JdbcConfig {
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
12. @Import
@Import 用于手动导入 Bean 到容器,支持三种导入方式,是扩展 Spring 容器的核心注解。
- 核心优势:无需扫描,直接显式注册 Bean,灵活控制 Bean 加载逻辑。
属性说明:
| 属性 | 说明 | 补充说明 |
|---|---|---|
| value | 导入的 Class 数组 | 支持三种类型:① 普通配置类(@Configuration 标注);② ImportSelector 实现类(返回 Class 全限定名,批量导入);③ ImportBeanDefinitionRegistrar 实现类(手动注册 BeanDefinition) |
示例 1:导入普通配置类
java
@Configuration
@Import(MyBatisConfig.class) // 导入 MyBatis 配置类
public class AppMainConfig {
}
示例 2:ImportSelector 实现类
java
// 自定义 ImportSelector
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 批量导入两个 Bean
return new String[]{"com.example.service.UserService", "com.example.dao.UserDao"};
}
}
// 导入 Selector
@Configuration
@Import(MyImportSelector.class)
public class SelectorConfig {
}
13. @ImportResource
@ImportResource 用于导入传统 XML 配置文件中的 Bean,实现注解与 XML 配置的兼容。
- 适用场景:迁移老项目时,保留 XML 配置,逐步过渡到注解配置。
属性说明:
| 属性 | 说明 | 补充说明 |
|---|---|---|
| value | XML 文件路径数组 | 与locations等价,如value = "classpath:spring-dao.xml" |
| locations | XML 文件路径数组 | 支持多个文件,如locations = {"classpath:spring-service.xml", "classpath:spring-dao.xml"} |
| reader | 自定义 BeanDefinition 读取器 | 需实现BeanDefinitionReader接口,自定义 XML 解析逻辑 |
示例:
java
@Configuration
@ImportResource(locations = "classpath:spring-legacy.xml") // 导入 XML 配置
public class LegacyConfig {
}
14. @Lazy
@Lazy 用于标记 Bean 为懒加载,即仅当首次获取 Bean 时才初始化,
而非容器启动时。
- 仅对单例 Bean 生效(原型 Bean 默认懒加载);
- 可标注在
@Configuration类上,批量设置该类中@Bean的懒加载。
属性说明:
| 属性 | 说明 |
|---|---|
| value | 是否懒加载 |
示例:
java
@Configuration
public class LazyConfig {
// 懒加载Bean,首次调用时初始化
@Bean
@Lazy(true)
public HeavyService heavyService() {
return new HeavyService(); // 初始化耗时较长的Bean
}
}
15. @Primary
@Primary 标记 Bean 为 "主 Bean",当存在多个同类型 Bean 时,@Autowired优先注入该 Bean。
- 适用场景:解决多 Bean 自动装配冲突,如默认数据源、默认缓存管理器。
示例:
java
@Configuration
public class PrimaryConfig {
@Bean
@Primary // 主数据源,@Autowired优先注入
public DataSource primaryDataSource() {
return new DruidDataSource();
}
@Bean
public DataSource secondaryDataSource() {
return new HikariDataSource();
}
}
// 使用时,自动注入primaryDataSource
@Service
public class UserService {
@Autowired
private DataSource dataSource; // 注入primaryDataSource
}
16. @Profile
@Profile 用于按环境加载 Bean,仅当指定环境激活时,才加载标注的 Bean。
- 常用环境:
dev(开发)、test(测试)、prod(生产); - 激活方式:通过
spring.profiles.active配置(如application.yml中设置)。
属性说明:
| 属性 | 说明 |
|---|---|
| value | 环境名称数组 |
示例:
java
@Configuration
public class ProfileConfig {
// 开发环境数据源
@Bean
@Profile("dev")
public DataSource devDataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
}
// 生产环境数据源
@Bean
@Profile("prod")
public DataSource prodDataSource() {
return new DruidDataSource();
}
}
17. @PropertySource
@PropertySource 用于加载自定义配置文件,将配置项纳入 Spring 的 Environment 管理。
- 核心特性:支持多种编码、忽略不存在的文件,配合
@Value或@ConfigurationProperties使用。
属性说明:
| 属性 | 说明 | 补充说明 |
|---|---|---|
| name | PropertySource 名称 | 自定义名称,如name = "customProperties" |
| value | 配置文件路径 | 如value = "classpath:app-custom.properties" |
| ignoreResourceNotFound | 是否忽略不存在的文件 | ① true:忽略;② false(默认):文件不存在则抛异常 |
| encoding | 配置文件编码 | 如encoding = "UTF-8" |
| factory | 自定义配置文件读取工厂 | 需实现PropertySourceFactory,支持 YAML 等非 properties 文件 |
示例:
java
@Configuration
@PropertySource(
value = "classpath:app-custom.properties",
encoding = "UTF-8",
ignoreResourceNotFound = true
)
public class PropertyConfig {
@Value("${app.name}")
private String appName; // 读取app-custom.properties中的app.name
}
18. @Role
@Role 定义 Bean 的角色,用于区分 Bean 的用途(框架内部 / 应用层)。
BeanDefinition.ROLE_APPLICATION(默认):应用层 Bean,开发者关注;BeanDefinition.ROLE_INFRASTRUCTURE:框架内部 Bean,对开发者透明;BeanDefinition.ROLE_SUPPORT:支撑性 Bean,辅助应用层 Bean 运行。
属性说明:
| 属性 | 说明 |
|---|---|
| value | 角色值 |
示例:
java
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) // 标记为框架内部配置类
public class FrameworkConfig {
@Bean
public TransactionManager transactionManager() {
return new DataSourceTransactionManager();
}
}
19. @Scope
@Scope 定义 Bean 的作用域,决定 Bean 的生命周期范围。
SCOPE_SINGLETON(默认):单例,容器中仅一个实例;SCOPE_PROTOTYPE:原型,每次获取创建新实例;SCOPE_REQUEST:请求作用域,每个 HTTP 请求一个实例;SCOPE_SESSION:会话作用域,每个 HTTP 会话一个实例。
属性说明:
| 属性 | 说明 | 补充说明 |
|---|---|---|
| value | 作用域名称 | 与scopeName等价,如value = ScopeConstants.SCOPE_REQUEST |
| scopeName | 作用域名称 | 语义更清晰,推荐使用 |
| proxyMode | 代理模式 | 解决作用域依赖问题(如单例 Bean 依赖请求作用域 Bean),可选:① ScopedProxyMode.NO(默认):无代理;② ScopedProxyMode.INTERFACES:JDK 动态代理;③ ScopedProxyMode.TARGET_CLASS:CGLIB 代理 |
作用域依赖问题: 单例bean依赖请求作用域bean,但是请求作用域bean,只有在请求时才会产生,而单例bean在启动时会进行依赖注入。如果不代理则无法注入请求作用域bean
示例:
java
@Configuration
public class ScopeConfig {
// 请求作用域Bean,通过CGLIB代理解决依赖问题
@Bean
@Scope(value = ScopeConstants.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public RequestContext requestContext() {
return new RequestContext();
}
// 单例Bean依赖请求作用域Bean(通过代理注入)
@Bean
public UserService userService() {
return new UserService(requestContext());
}
}
20. @AutoConfigureAfter / @AutoConfigureBefore / @AutoConfigureOrder
这三个注解用于控制@Configuration配置类的加载顺序,类似@DependsOn但针对配置类解析阶段。
- 核心区别:
@DependsOn控制 Bean 创建顺序,这三个注解控制配置类解析顺序。
属性说明:
| 属性 | 说明 |
|---|---|
| name | 配置类全限定名数组 |
| value | 配置类 Class 数组 |
示例:
java
// 数据源配置先解析
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
return new DruidDataSource();
}
}
// 事务配置在数据源配置之后解析
@Configuration
@AutoConfigureAfter(DataSourceConfig.class)
public class TransactionConfig {
@Bean
public TransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
二、WebMVC 相关注解
WebMVC 注解是 Spring MVC 处理请求、响应、参数绑定、异常处理的核心,专注于 Web 层的请求处理逻辑。
1. @ControllerAdvice
@ControllerAdvice 是 "控制器增强器",全局拦截 Controller 的请求处理流程,配合以下注解使用:
@ExceptionHandler:全局异常处理;@InitBinder:全局参数绑定;@ModelAttribute:全局模型属性设置;- 扩展能力:实现
ResponseBodyAdvice/RequestBodyAdvice,拦截请求 / 响应处理。
属性说明:
| 属性名 | 说明 | 补充说明 |
|---|---|---|
| basePackages | 拦截的基础包路径 | 仅指定包下的 Controller 被拦截,如basePackages = "com.example.controller" |
| assignableTypes | 拦截的 Controller 父类 / 接口 | 仅继承指定 Class 的 Controller 被拦截,如assignableTypes = BaseController.class |
| annotations | 拦截的 Controller 注解 | 仅标注指定注解的 Controller 被拦截,如annotations = RestController.class |
示例:
java
// 全局增强器,拦截所有@RestController标注的控制器
@ControllerAdvice(annotations = RestController.class)
public class GlobalControllerAdvice implements ResponseBodyAdvice<Object>, RequestBodyAdvice {
// 全局异常处理
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
ErrorResponse response = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
// 全局参数绑定(日期格式)
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
binder.registerCustomEditor(Date.class, new CustomDateEditor(sdf, true));
}
// 全局模型属性
@ModelAttribute
public void addGlobalModelAttribute(Model model) {
model.addAttribute("appName", "SpringMVC Demo");
}
// 拦截@ResponseBody响应,自定义处理
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
// 统一响应格式
return new Result<>("success", body);
}
// 拦截@RequestBody请求,自定义处理
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Class<?> targetType, Class<? extends HttpMessageConverter<?>> converterType) {
// 请求参数预处理
return body;
}
// 其他接口实现...
}
2. @ExceptionHandler
@ExceptionHandler 用于处理 Controller 中的异常,可局部(Controller 内)或全局(配合@ControllerAdvice)使用。
- 核心优势:精准捕获指定异常,统一异常响应格式,避免重复 try-catch。
属性说明:
| 属性名 | 说明 |
|---|---|
| value | 需处理的异常类型数组 |
示例(全局异常处理):
java
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
// 处理业务异常
@ExceptionHandler(BusinessException.class)
@ResponseBody
public Result<Void> handleBusinessException(BusinessException e) {
log.warn("业务异常:{}", e.getMessage());
return Result.fail(e.getCode(), e.getMessage());
}
// 处理参数校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public Result<Void> handleValidException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldError().getDefaultMessage();
log.warn("参数校验异常:{}", message);
return Result.fail(400, message);
}
// 处理所有未捕获的异常
@ExceptionHandler(Exception.class)
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Result<Void> handleException(Exception e) {
log.error("系统异常", e);
return Result.fail(500, "服务器内部错误");
}
}
// 统一响应结果
class Result<T> {
private String code;
private String message;
private T data;
// 静态方法:success/fail...
}
3. @InitBinder
@InitBinder 用于自定义 Web 请求参数到 Bean 的绑定规则,解决特殊类型(如日期、枚举)的参数转换问题。
- 作用域:局部(Controller 内)或全局(配合
@ControllerAdvice)。
示例(全局日期参数绑定):
java
@ControllerAdvice
public class GlobalInitBinder {
// 绑定多种日期格式
@InitBinder
public void initDateBinder(WebDataBinder binder) {
// 支持yyyy-MM-dd和yyyy-MM-dd HH:mm:ss两种格式
CustomDateEditor dateEditor = new CustomDateEditor(new FlexibleDateFormat(), true);
binder.registerCustomEditor(Date.class, dateEditor);
}
// 自定义枚举转换器
@InitBinder
public void initEnumBinder(WebDataBinder binder) {
binder.registerCustomEditor(StatusEnum.class, new EnumEditor(StatusEnum.class));
}
// 灵活的日期格式解析器
static class FlexibleDateFormat extends DateFormat {
private final List<SimpleDateFormat> formats = Arrays.asList(
new SimpleDateFormat("yyyy-MM-dd"),
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
);
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
return formats.get(0).format(date, toAppendTo, fieldPosition);
}
@Override
public Date parse(String source, ParsePosition pos) {
for (SimpleDateFormat format : formats) {
ParsePosition parsePos = new ParsePosition(0);
Date date = format.parse(source, parsePos);
if (date != null) {
pos.setIndex(parsePos.getIndex());
pos.setErrorIndex(parsePos.getErrorIndex());
return date;
}
}
return null;
}
}
}
4. @ModelAttribute
@ModelAttribute 用于在 Controller 方法执行前,向 Model 中添加通用属性,适用于页面渲染(前后端不分离场景)。
- 全局使用:配合
@ControllerAdvice,为所有 Controller 添加通用模型属性。
示例:
java
@ControllerAdvice(basePackages = "com.example.controller")
public class GlobalModelAttribute {
// 为所有请求添加当前登录用户信息
@ModelAttribute("currentUser")
public User getCurrentUser(HttpServletRequest request) {
String token = request.getHeader("token");
return UserContext.getCurrentUser(token); // 从上下文获取登录用户
}
}
// 页面中可直接使用${currentUser.name}
@Controller
@RequestMapping("/user")
public class UserController {
@GetMapping("/info")
public String userInfo() {
return "user/info"; // 页面渲染时可获取currentUser
}
}
5. @CookieValue
@CookieValue 用于从 HTTP Cookie 中获取指定值,绑定到 Controller 方法参数。
- 核心特性:支持默认值,避免 Cookie 不存在时抛异常。
属性说明:
| 属性名 | 说明 |
|---|---|
| required | 是否必须存在 |
| defaultValue | 默认值 |
示例:
java
@RestController
@RequestMapping("/cookie")
public class CookieController {
@GetMapping("/get")
public Result<String> getCookie(
@CookieValue(value = "userId", required = false, defaultValue = "0") String userId
) {
return Result.success("用户ID:" + userId);
}
}
6. @MatrixVariable
@MatrixVariable 用于获取URL 中的矩阵变量(以;分隔的键值对),如/user/info;id=1;name=test。
- 注意:Spring MVC 默认禁用矩阵变量,需手动开启:
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false); // 开启矩阵变量
configurer.setUrlPathHelper(urlPathHelper);
}
}
属性说明:
| 属性名 | 说明 | 补充说明 |
|---|---|---|
| value | 矩阵变量名 | 与name等价 |
| name | 矩阵变量名 | 如name = "id" |
| pathVar | 路径变量名 | 匹配指定路径变量中的矩阵变量,如pathVar = "userPath" |
| required | 是否必须存在 | ① true(默认):不存在抛异常;② false:不存在则为 null |
| defaultValue | 默认值 | 变量不存在时使用 |
示例:
java
@RestController
@RequestMapping("/matrix")
public class MatrixVariableController {
// 匹配URL:/matrix/user/info;id=1;name=test
@GetMapping("/user/{userPath}")
public Result<Map<String, String>> getMatrixVariable(
@MatrixVariable(name = "id", pathVar = "userPath") String id,
@MatrixVariable(name = "name", pathVar = "userPath", defaultValue = "guest") String name
) {
Map<String, String> data = new HashMap<>();
data.put("id", id);
data.put("name", name);
return Result.success(data);
}
}
7. @PathVariable
@PathVariable 用于获取 URL 路径中的变量(以{}声明),是 RESTful API 的核心注解。
属性说明:
| 属性名 | 说明 |
|---|---|
| required | 是否必须存在 |
| defaultValue | 默认值 |
示例:
java
@RestController
@RequestMapping("/path")
public class PathVariableController {
// 匹配URL:/path/user/123/detail
@GetMapping("/user/{userId}/detail")
public Result<User> getUser(
@PathVariable(value = "userId", required = true) Long userId
) {
User user = new User(userId, "测试用户");
return Result.success(user);
}
}
8. @RequestAttribute
@RequestAttribute 用于获取 HTTP Request 中的属性值,适用于请求转发 / 包含场景(如过滤器中设置的属性)。
属性说明:
| 属性名 | 说明 |
|---|---|
| required | 是否必须存在 |
示例:
java
@RestController
@RequestMapping("/request")
public class RequestAttributeController {
// 设置请求属性
@GetMapping("/setAttr")
public String setRequestAttribute(HttpServletRequest request) {
request.setAttribute("traceId", UUID.randomUUID().toString());
return "forward:/request/getAttr"; // 请求转发
}
// 获取请求属性
@GetMapping("/getAttr")
public Result<String> getRequestAttribute(
@RequestAttribute(value = "traceId", required = false) String traceId
) {
return Result.success("追踪ID:" + traceId);
}
}
9. @RequestPart
@RequestPart 用于处理multipart/form-data类型的请求,适用于文件上传、复杂参数提交(如 JSON、XML)。
- 核心区别:
@RequestParam仅处理简单表单字段,@RequestPart支持复杂类型和文件。
示例(文件上传 + JSON 参数):
java
@RestController
@RequestMapping("/upload")
public class RequestPartController {
// 接收文件+JSON参数
@PostMapping("/file")
public Result<String> uploadFile(
@RequestPart("file") MultipartFile file, // 上传的文件
@RequestPart("user") @Valid User user // JSON格式的用户信息
) {
String fileName = file.getOriginalFilename();
// 处理文件上传和用户信息
return Result.success("文件:" + fileName + " 上传成功,用户:" + user.getName());
}
}
// 请求示例(Postman):
// Content-Type: multipart/form-data
// 表单字段:
// - file:选择文件
// - user:{"name":"test","age":20}(JSON格式)
10. @ResponseStatus
@ResponseStatus 用于设置 HTTP 响应状态码,可标注在 Controller 方法或异常类上。
- 核心用途:自定义异常的响应状态码,或修改正常请求的状态码。
属性说明:
| 属性名 | 说明 | 补充说明 |
|---|---|---|
| value | 响应状态码 | 如HttpStatus.CREATED(201)、HttpStatus.BAD_REQUEST(400) |
| code | 同 value | 语义等价,推荐使用 value |
| reason | 响应描述 | 状态码对应的描述信息,如reason = "参数错误" |
示例 1:标注在方法上
java
@RestController
@RequestMapping("/status")
public class ResponseStatusController {
// 响应状态码:201 Created
@PostMapping("/create")
@ResponseStatus(value = HttpStatus.CREATED, reason = "资源创建成功")
public Result<Void> createResource() {
// 处理资源创建逻辑
return Result.success();
}
}
示例 2:标注在异常类上
java
// 自定义异常,指定响应状态码
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "资源不存在")
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
// Controller中抛出异常,自动返回404
@GetMapping("/resource/{id}")
public Result<Resource> getResource(@PathVariable Long id) {
Resource resource = resourceService.getById(id);
if (resource == null) {
throw new ResourceNotFoundException("资源ID:" + id + " 不存在");
}
return Result.success(resource);
}
11. @SessionAttribute
@SessionAttribute 用于从 HTTP Session 中获取指定属性值,适用于会话级数据(如登录状态、用户信息)。
属性说明:
| 属性名 | 说明 |
|---|---|
| required | 是否必须存在 |
| defaultValue | 默认值 |
示例:
java
@RestController
@RequestMapping("/session")
public class SessionAttributeController {
// 设置Session属性
@GetMapping("/login")
public Result<Void> login(HttpSession session) {
User user = new User(1L, "admin");
session.setAttribute("loginUser", user); // 存储登录用户
return Result.success("登录成功");
}
// 获取Session属性
@GetMapping("/user")
public Result<User> getLoginUser(
@SessionAttribute(value = "loginUser", required = false) User loginUser
) {
if (loginUser == null) {
return Result.fail(401, "未登录");
}
return Result.success(loginUser);
}
}
三、事务管理相关注解
事务管理注解是 Spring 实现声明式事务的核心,通过注解快速配置事务属性,替代传统 XML 事务配置。
1. @Transactional
@Transactional 是声明式事务的核心注解,标注在类或方法上,指定该类 / 方法的事务属性。
- 类级注解:所有 public 方法继承事务属性;
- 方法级注解:优先级高于类级,可单独配置;
- 底层依赖:AOP 动态代理实现事务增强,默认仅捕获运行时异常(
RuntimeException)。
属性说明:
| 属性名 | 说明 | 补充说明 |
|---|---|---|
| value | 事务管理器名称 | 如value = "transactionManager",多数据源时指定 |
| transactionManager | 同 value | 语义更清晰,推荐使用 |
| propagation | 事务传播行为 | 枚举值,如REQUIRED(默认)、REQUIRES_NEW、SUPPORTS等 |
| isolation | 事务隔离级别 | 枚举值,如DEFAULT(默认,跟随数据库)、READ_COMMITTED、SERIALIZABLE等 |
| timeout | 事务超时时间(秒) | 默认为 - 1(无超时),超时抛出TransactionTimedOutException |
| readOnly | 是否只读事务 | ① true:只读(查询操作,优化性能);② false(默认):读写事务 |
| rollbackFor | 触发回滚的异常类型 | 数组形式,如rollbackFor = {SQLException.class, BusinessException.class} |
| rollbackForClassName | 触发回滚的异常类名 | 字符串数组,如rollbackForClassName = "java.sql.SQLException" |
| noRollbackFor | 不触发回滚的异常类型 | 数组形式,如noRollbackFor = IllegalArgumentException.class |
| noRollbackForClassName | 不触发回滚的异常类名 | 字符串数组,如noRollbackForClassName = "java.lang.IllegalArgumentException" |
示例:
java
@Service
@Transactional(readOnly = true) // 类级:所有public方法默认只读事务
public class OrderService {
// 方法级:覆盖类级配置,读写事务,超时30秒,指定异常回滚
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 30,
readOnly = false,
rollbackFor = {OrderException.class, SQLException.class}
)
public void createOrder(OrderDTO orderDTO) {
// 订单创建逻辑(增删改操作)
orderMapper.insert(orderDTO);
inventoryMapper.deduct(orderDTO.getProductId(), orderDTO.getQuantity());
}
// 继承类级只读事务
public OrderDTO getOrderById(Long orderId) {
return orderMapper.selectById(orderId);
}
}
2. @EnableTransactionManagement
@EnableTransactionManagement 用于启用 Spring 声明式事务管理,标注在配置类上。
- 核心作用:激活 Spring 的事务注解驱动,自动扫描
@Transactional标注的 Bean 并生成代理; - 兼容模式:支持 Spring AOP 和 AspectJ 两种代理方式。
属性说明:
| 属性名 | 说明 | 补充说明 |
|---|---|---|
| proxyTargetClass | 是否使用 CGLIB 代理 | ① true:强制 CGLIB 代理(类代理);② false(默认):优先 JDK 动态代理(接口代理) |
| mode | 事务代理模式 | 枚举值:PROXY(默认,Spring AOP 代理)、ASPECTJ(AspectJ 代理,需额外依赖) |
| order | 事务切面优先级 | 数值越小优先级越高,默认Ordered.LOWEST_PRECEDENCE(最低) |
示例:
java
@Configuration
@EnableTransactionManagement(proxyTargetClass = true) // 启用事务管理,使用CGLIB代理
@MapperScan("com.example.mapper")
public class TransactionConfig {
// 配置事务管理器(基于数据源)
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
四、数据访问相关注解
数据访问注解涵盖 Spring Data JPA、MyBatis 等持久层框架的核心注解,简化数据操作逻辑。
1. Spring Data JPA 核心注解
(1)@RepositoryRestResource
@RepositoryRestResource 用于 Spring Data REST,将 Repository 自动暴露为 RESTful API,无需手动编写 Controller。
属性说明:
| 属性名 | 说明 | 补充说明 |
|---|---|---|
| path | API 路径 | 如path = "users",访问路径/users |
| collectionResourceRel | 集合资源关系名 | 用于 HATEOAS 链接,如collectionResourceRel = "userList" |
| itemResourceRel | 单个资源关系名 | 如itemResourceRel = "user" |
| exported | 是否暴露 API | ① true(默认):暴露;② false:不暴露 |
| excerptProjection | 默认投影 | 指定默认返回的字段投影类 |
示例:
java
// 自动暴露REST API:/users(GET/POST/PUT/DELETE)
@RepositoryRestResource(path = "users", collectionResourceRel = "userList")
public interface UserRepository extends JpaRepository<User, Long> {
// 自动生成API:/users/search/byName?name=xxx
@RestResource(path = "byName", rel = "findByName")
List<User> findByNameContaining(@Param("name") String name);
}
(2)@Query
@Query 用于自定义 JPQL/SQL 查询,标注在 Repository 方法上。
属性说明:
| 属性名 | 说明 | 补充说明 |
|---|---|---|
| value | 查询语句 | JPQL(默认)或 SQL(需配合nativeQuery = true) |
| nativeQuery | 是否原生 SQL | ① true:原生 SQL;② false(默认):JPQL |
| countQuery | 计数查询语句 | 配合分页查询,返回总条数 |
| countProjection | 计数投影 | 简化计数查询,如countProjection = "id" |
| namedParameters | 是否支持命名参数 | ① true(默认):支持@Param命名参数;② false:仅支持位置参数 |
示例:
java
public interface ProductRepository extends JpaRepository<Product, Long> {
// JPQL查询(参数使用:name)
@Query("SELECT p FROM Product p WHERE p.name LIKE %:name% AND p.price <= :maxPrice")
List<Product> findByCondition(@Param("name") String name, @Param("maxPrice") BigDecimal maxPrice);
// 原生SQL查询
@Query(
value = "SELECT * FROM product WHERE category_id = ?1 AND stock > 0",
nativeQuery = true
)
List<Product> findAvailableByCategoryId(Long categoryId);
// 分页+计数查询
@Query(value = "SELECT p FROM Product p WHERE p.createTime >= :startTime",
countQuery = "SELECT COUNT(p.id) FROM Product p WHERE p.createTime >= :startTime")
Page<Product> findByCreateTimeAfter(@Param("startTime") LocalDateTime startTime, Pageable pageable);
}
(3)@Modifying
@Modifying 配合@Query使用,标识该查询为修改操作(INSERT/UPDATE/DELETE),需在事务中执行。
示例:
java
public interface UserRepository extends JpaRepository<User, Long> {
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id IN :ids")
@Transactional // 修改操作需事务支持
int batchUpdateStatus(@Param("status") Integer status, @Param("ids") List<Long> ids);
}
2. MyBatis 核心注解
(1)@Mapper
@Mapper 标识接口为 MyBatis 映射器接口,MyBatis 会自动扫描并生成代理实现类。
- 替代方案:可通过
@MapperScan(配置类注解)批量扫描包下的 Mapper 接口。
示例:
java
// 单个Mapper接口标注
@Mapper
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User selectById(Long id);
@Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})")
@Options(useGeneratedKeys = true, keyProperty = "id") // 自动生成主键并回写
int insert(User user);
}
// 配置类批量扫描(推荐)
@Configuration
@MapperScan("com.example.mapper") // 扫描该包下所有Mapper接口
public class MyBatisConfig {
}
(2)MyBatis 查询 / 操作注解
MyBatis 提供一系列注解替代 XML 映射文件,核心包括:
@Select:查询操作@Insert:插入操作@Update:更新操作@Delete:删除操作@ResultMap:引用 XML 中的结果映射@Param:指定方法参数名(用于 SQL 中 #{name} 引用)
示例:
java
@Mapper
public interface OrderMapper {
// 查询(关联查询,使用@Results映射)
@Select("SELECT o.id, o.order_no, o.user_id, u.name user_name " +
"FROM order o LEFT JOIN user u ON o.user_id = u.id " +
"WHERE o.id = #{id}")
@Results({
@Result(column = "id", property = "id", id = true),
@Result(column = "order_no", property = "orderNo"),
@Result(column = "user_id", property = "userId"),
@Result(column = "user_name", property = "userName")
})
OrderVO selectOrderVOById(Long id);
// 批量插入
@Insert("<script>" +
"INSERT INTO order(user_id, order_no, amount) " +
"VALUES" +
"<foreach collection='list' item='item' separator=','>" +
"(#{item.userId}, #{item.orderNo}, #{item.amount})" +
"</foreach>" +
"</script>")
int batchInsert(@Param("list") List<Order> orderList);
// 更新
@Update("UPDATE order SET status = #{status} WHERE id = #{id}")
int updateStatus(@Param("id") Long id, @Param("status") Integer status);
}
五、Spring Security 安全相关注解
Spring Security 注解用于实现权限控制、身份认证相关功能,简化安全配置。
1. @EnableWebSecurity
@EnableWebSecurity 用于启用 Spring Security 的 Web 安全支持,标注在安全配置类上。
- 核心作用:激活 Spring Security 的自动配置,加载默认安全规则(如登录页面、CSRF 防护等)。
示例:
java
@Configuration
@EnableWebSecurity // 启用Web安全
@EnableMethodSecurity // 启用方法级权限控制(Spring Security 6+)
public class SecurityConfig {
// 配置用户认证(内存用户示例)
@Bean
public UserDetailsService userDetailsService() {
UserDetails admin = User.withUsername("admin")
.password(passwordEncoder().encode("123456"))
.roles("ADMIN")
.build();
UserDetails user = User.withUsername("user")
.password(passwordEncoder().encode("123456"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(admin, user);
}
// 密码加密器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 配置安全规则
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll() // 公开路径
.requestMatchers("/admin/**").hasRole("ADMIN") // 仅ADMIN角色可访问
.anyRequest().authenticated() // 其他路径需认证
)
.formLogin(form -> form.permitAll()) // 启用默认登录页面
.logout(logout -> logout.permitAll()); // 启用注销功能
return http.build();
}
}
2. @EnableMethodSecurity
@EnableMethodSecurity(Spring Security 6+,替代旧版@EnableGlobalMethodSecurity)用于启用方法级权限控制,支持@PreAuthorize、@PostAuthorize等注解。
属性说明:
| 属性名 | 说明 | 补充说明 |
|---|---|---|
| prePostEnabled | 是否启用 @PreAuthorize/@PostAuthorize | ① true(默认):启用;② false:禁用 |
| securedEnabled | 是否启用 @Secured | ① true:启用;② false(默认):禁用 |
| jsr250Enabled | 是否启用 JSR-250 注解(如 @RolesAllowed) | ① true:启用;② false(默认):禁用 |
3. 方法级权限注解
(1)@PreAuthorize
@PreAuthorize 方法执行前进行权限校验,支持 SpEL 表达式,灵活控制访问权限。
示例:
java
@Service
public class ResourceService {
// 仅ADMIN角色可访问
@PreAuthorize("hasRole('ADMIN')")
public void deleteResource(Long id) {
// 删除资源逻辑
}
// 仅本人或ADMIN可访问(SpEL表达式,#userId为方法参数)
@PreAuthorize("authentication.principal.username == #username or hasRole('ADMIN')")
public UserDTO getUserInfo(String username) {
// 查询用户信息
}
// 权限表达式结合自定义方法(需注册Bean:customPermissionEvaluator)
@PreAuthorize("@customPermissionEvaluator.hasPermission(#orderId, authentication)")
public OrderDTO getOrderById(Long orderId) {
// 查询订单
}
}
(2)@PostAuthorize
@PostAuthorize 方法执行后进行权限校验,适用于需要根据返回结果判断权限的场景(如数据权限)。
示例:
java
@Service
public class OrderService {
// 方法执行后校验:返回的订单所属用户与当前登录用户一致
@PostAuthorize("returnObject.userId == authentication.principal.id")
public OrderDTO getOrderById(Long orderId) {
return orderMapper.selectById(orderId);
}
}
(3)@Secured
@Secured 用于基于角色的权限控制,仅支持角色名称(需以ROLE_前缀开头,或配置rolePrefix)。
示例:
java
@Service
public class UserService {
// 仅ROLE_ADMIN角色可访问(注意前缀ROLE_)
@Secured("ROLE_ADMIN")
public void batchDelete(List<Long> ids) {
userMapper.batchDelete(ids);
}
// 多角色支持(OR关系)
@Secured({"ROLE_ADMIN", "ROLE_OPERATOR"})
public void updateUserStatus(Long id, Integer status) {
userMapper.updateStatus(id, status);
}
}
(4)@RolesAllowed(JSR-250)
@RolesAllowed 是 JSR-250 标准注解,功能与@Secured类似,支持角色权限控制。
示例:
java
@Service
public class ProductService {
// 支持ROLE_USER或ROLE_ADMIN角色
@RolesAllowed({"USER", "ADMIN"})
public List<ProductDTO> getProductList() {
return productMapper.selectAll();
}
}
4. @AuthenticationPrincipal
@AuthenticationPrincipal 用于获取当前登录用户的认证信息(Authentication中的Principal),简化用户信息获取。
示例:
java
@RestController
@RequestMapping("/user")
public class UserController {
// 获取当前登录用户的用户名(Principal为String类型)
@GetMapping("/info")
public Result<UserInfo> getUserInfo(@AuthenticationPrincipal String username) {
UserInfo userInfo = userService.getUserByUsername(username);
return Result.success(userInfo);
}
// 获取自定义UserDetails对象(需实现UserDetails接口)
@GetMapping("/profile")
public Result<CustomUserDetails> getProfile(@AuthenticationPrincipal CustomUserDetails userDetails) {
return Result.success(userDetails);
}
}
5. @CrossOrigin
@CrossOrigin 用于解决跨域资源共享(CORS)问题,标注在 Controller 或方法上,允许跨域请求访问。
- 核心场景:前后端分离架构中,前端域名与后端 API 域名不一致时配置;
- 底层原理:自动添加
Access-Control-*响应头,支持预检请求(OPTIONS)。
属性说明:
| 属性名 | 说明 | 补充说明 |
|---|---|---|
| origins | 允许的跨域源 | 数组形式,如origins = {"``https://example.com``", "``http://localhost:8080``"},*表示允许所有(不推荐生产环境) |
| allowedHeaders | 允许的请求头 | 数组形式,如allowedHeaders = {"Content-Type", "Authorization"},*表示允许所有 |
| methods | 允许的请求方法 | 数组形式,如methods = {RequestMethod.GET, RequestMethod.POST},默认允许所有请求方法 |
| exposedHeaders | 允许前端访问的响应头 | 数组形式,如exposedHeaders = "X-Total-Count",默认仅暴露基本响应头 |
| allowCredentials | 是否允许携带 Cookie | ① true:允许(需前端配合设置withCredentials: true);② false(默认):不允许 |
| maxAge | 预检请求缓存时间(秒) | 默认为 1800 秒(30 分钟),减少预检请求次数 |
示例:
java
// 类级配置:所有方法允许跨域
@RestController
@RequestMapping("/api")
@CrossOrigin(
origins = {"https://example.com", "http://localhost:8080"},
allowedHeaders = "*",
allowCredentials = true,
maxAge = 3600
)
public class ApiController {
// 方法级配置:覆盖类级,仅允许GET/POST方法
@CrossOrigin(methods = {RequestMethod.GET, RequestMethod.POST})
@GetMapping("/data")
public Result<List<DataDTO>> getData() {
return Result.success(dataService.list());
}
}
六、其他常用核心注解
1. 异步相关注解
(1)@EnableAsync
@EnableAsync 用于启用 Spring 异步方法支持,标注在配置类上。
- 核心作用:激活 Spring 对
@Async注解的识别,通过线程池执行异步方法。
属性说明:
| 属性名 | 说明 | 补充说明 |
|---|---|---|
| proxyTargetClass | 是否使用 CGLIB 代理 | ① true:类代理;② false(默认):接口代理 |
| mode | 代理模式 | PROXY(默认)或ASPECTJ(需额外依赖) |
| order | 异步切面优先级 | 数值越小优先级越高,默认最低 |
示例:
java
@Configuration
@EnableAsync(proxyTargetClass = true) // 启用异步支持
public class AsyncConfig {
// 配置自定义线程池(推荐)
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5); // 核心线程数
executor.setMaxPoolSize(10); // 最大线程数
executor.setQueueCapacity(20); // 队列容量
executor.setKeepAliveSeconds(60); // 空闲线程存活时间
executor.setThreadNamePrefix("Async-"); // 线程名前缀
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略
executor.initialize();
return executor;
}
}
(2)@Async
@Async 标注方法为异步方法,Spring 会通过线程池异步执行(非当前线程阻塞)。
- 异步方法不能在同一个类中调用(代理失效,如事务失效);
- 无返回值用
void,有返回值用Future<T>或CompletableFuture<T>。
示例:
java
@Service
public class AsyncService {
// 无返回值异步方法
@Async("taskExecutor") // 指定线程池(对应AsyncConfig中的Bean名称)
public void sendNotice(Long userId, String content) {
// 异步执行:发送短信/邮件通知(无返回值)
noticeClient.send(userId, content);
}
// 有返回值异步方法(CompletableFuture支持链式调用)
@Async
public CompletableFuture<Boolean> exportData(String condition) {
// 异步执行:数据导出逻辑
boolean success = exportService.doExport(condition);
return CompletableFuture.completedFuture(success);
}
}
// 调用示例
@Controller
public class ExportController {
@Autowired
private AsyncService asyncService;
@GetMapping("/export")
public Result<String> export(String condition) {
CompletableFuture<Boolean> future = asyncService.exportData(condition);
future.whenComplete((success, ex) -> {
if (success) {
// 导出成功回调
} else {
// 导出失败处理
}
});
return Result.success("导出任务已启动,请稍后查看结果");
}
}
2. 缓存相关注解
(1)@EnableCaching
@EnableCaching 用于启用 Spring 缓存支持,标注在配置类上。
- 核心作用:激活 Spring 对缓存注解(
@Cacheable、@CachePut等)的识别,整合第三方缓存(如 Redis、Caffeine)。
示例:
java
@Configuration
@EnableCaching // 启用缓存支持
public class CacheConfig {
// 配置Redis缓存管理器(需引入spring-boot-starter-data-redis依赖)
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30)) // 默认缓存过期时间30分钟
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
// 自定义不同缓存的过期时间
Map<String, RedisCacheConfiguration> cacheConfigs = new HashMap<>();
cacheConfigs.put("userCache", config.entryTtl(Duration.ofHours(1)));
cacheConfigs.put("productCache", config.entryTtl(Duration.ofMinutes(10)));
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.withInitialCacheConfigurations(cacheConfigs)
.build();
}
}
(2)核心缓存注解
| 注解 | 作用 | 核心属性 |
|---|---|---|
@Cacheable |
查询缓存:存在则返回缓存,不存在则执行方法并缓存结果 | value(缓存名称)、key(缓存键,SpEL 表达式)、condition(缓存条件) |
@CachePut |
更新缓存:执行方法后更新缓存(不查询缓存) | 同@Cacheable,用于新增 / 修改操作 |
@CacheEvict |
删除缓存:执行方法后删除指定缓存 | 同@Cacheable,allEntries(是否删除所有缓存)、beforeInvocation(是否执行前删除) |
@Caching |
组合缓存:一个方法配置多个缓存注解 | cacheable、put、evict(数组形式) |
@CacheConfig |
类级缓存配置:统一指定value(缓存名称),方法级可覆盖 |
value(默认缓存名称) |
示例:
java
@Service
@CacheConfig(value = "productCache") // 类级:默认缓存名称
public class ProductService {
// 查询缓存:key为产品ID,条件为ID>0
@Cacheable(key = "#id", condition = "#id > 0")
public ProductDTO getProductById(Long id) {
// 缓存不存在时执行:查询数据库
return productMapper.selectById(id);
}
// 更新缓存:新增产品后缓存结果(key为返回的ID)
@CachePut(key = "#result.id")
public ProductDTO addProduct(ProductForm form) {
Product product = new Product();
BeanUtils.copyProperties(form, product);
productMapper.insert(product);
ProductDTO dto = new ProductDTO();
BeanUtils.copyProperties(product, dto);
return dto;
}
// 删除缓存:删除产品后删除对应缓存,同时清空所有产品列表缓存
@Caching(evict = {
@CacheEvict(key = "#id"), // 删除单个产品缓存
@CacheEvict(value = "productListCache", allEntries = true) // 清空列表缓存
})
public void deleteProduct(Long id) {
productMapper.deleteById(id);
}
// 条件缓存:查询列表时缓存,条件为分类ID不为空
@Cacheable(value = "productListCache", key = "#categoryId", condition = "#categoryId != null")
public List<ProductDTO> getProductByCategoryId(Long categoryId) {
return productMapper.selectByCategoryId(categoryId);
}
}
3. 事件相关注解
(1)@EventListener
@EventListener 标注方法为 Spring 事件监听器,监听指定类型的事件。
- 核心场景:解耦业务逻辑(如订单创建后触发库存扣减、日志记录等);
- 支持事件:Spring 内置事件(如
ContextRefreshedEvent)或自定义事件。
示例:
java
// 1. 定义自定义事件(继承ApplicationEvent)
public class OrderCreatedEvent extends ApplicationEvent {
private OrderDTO orderDTO;
public OrderCreatedEvent(Object source, OrderDTO orderDTO) {
super(source);
this.orderDTO = orderDTO;
}
// getter/setter
}
// 2. 发布事件(在业务方法中)
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Transactional
public void createOrder(OrderDTO orderDTO) {
// 订单创建逻辑
orderMapper.insert(orderDTO);
// 发布事件(异步发布需配合@Async)
eventPublisher.publishEvent(new OrderCreatedEvent(this, orderDTO));
}
}
// 3. 监听事件(多个监听器可同时监听)
@Service
public class OrderEventListener {
// 同步监听:与发布事件的事务同线程
@EventListener(OrderCreatedEvent.class)
public void handleOrderCreated(OrderCreatedEvent event) {
OrderDTO orderDTO = event.getOrderDTO();
// 执行后续逻辑:如日志记录
logService.recordOrderLog(orderDTO.getId(), "订单创建成功");
}
// 异步监听:通过@Async指定线程池,不阻塞主线程
@Async("taskExecutor")
@EventListener(OrderCreatedEvent.class)
public void handleInventoryDeduct(OrderCreatedEvent event) {
OrderDTO orderDTO = event.getOrderDTO();
// 异步执行:库存扣减
inventoryService.deduct(orderDTO.getProductId(), orderDTO.getQuantity());
}
}
(2)@TransactionalEventListener
@TransactionalEventListener 是@EventListener的事务增强版,支持事务生命周期绑定。
- 核心特性:事件触发时机与事务状态关联(如事务提交后、回滚后),避免事务未提交时触发后续逻辑。
属性说明:
| 属性名 | 说明 | 补充说明 |
|---|---|---|
| phase | 事务阶段 | 枚举值:BEFORE_COMMIT(提交前)、AFTER_COMMIT(提交后,默认)、AFTER_ROLLBACK(回滚后)、AFTER_COMPLETION(完成后,无论提交 / 回滚) |
| fallbackExecution | 无事务时是否执行 | ① true:无事务时也执行;② false(默认):仅事务中执行 |
示例:
java
@Service
public class OrderEventListener {
// 事务提交后执行:确保订单创建成功后再发送通知
@TransactionalEventListener(
value = OrderCreatedEvent.class,
phase = TransactionPhase.AFTER_COMMIT,
fallbackExecution = false
)
public void sendOrderNotice(OrderCreatedEvent event) {
OrderDTO orderDTO = event.getOrderDTO();
// 事务提交后执行:发送短信/邮件通知
noticeService.send(orderDTO.getUserId(), "您的订单已创建成功,订单号:" + orderDTO.getOrderNo());
}
}
4. 依赖注入相关补充注解
(1)@Resource(JSR-250)
@Resource 是 JSR-250 标准注解,用于依赖注入,与@Autowired功能类似,区别如下:
@Autowired:Spring 提供,按类型注入,需配合@Qualifier指定名称;@Resource:JDK 标准,默认按名称注入,名称匹配失败则按类型注入。
属性说明:
| 属性名 | 说明 | 补充说明 |
|---|---|---|
| name | 注入 Bean 的名称 | 如name = "userService",优先按名称匹配 |
| type | 注入 Bean 的类型 | 如type = UserService.class,按类型匹配 |
| lookup | 查找方式 | 仅用于 EJB,Spring 中很少使用 |
示例:
java
@Service
public class OrderService {
// 按名称注入(Bean名称为userService)
@Resource(name = "userService")
private UserService userService;
// 按类型注入(仅当存在唯一UserMapper类型Bean时生效)
@Resource(type = UserMapper.class)
private UserMapper userMapper;
// 无属性:默认按字段名(orderMapper)匹配Bean名称
@Resource
private OrderMapper orderMapper;
}
(2)@Inject(JSR-330)
@Inject 是 JSR-330 标准注解,用于依赖注入,功能与@Autowired类似,需引入javax.inject依赖。
- 特点:无属性,仅按类型注入,需配合
@Named指定 Bean 名称。
示例:
xml
<!-- 引入依赖(Maven) -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
java
@Service
public class ProductService {
// 按类型注入
@Inject
private ProductMapper productMapper;
// 按名称注入(配合@Named)
@Inject
@Named("userService")
private UserService userService;
}
七、注解使用注意事项总结
- 代理失效问题:
@Transactional、@Async、@Cacheable等注解依赖 Spring AOP 代理,同一类中方法调用会导致代理失效;- 解决方案:通过
ApplicationContext获取 Bean 调用,或拆分业务逻辑到不同类。
- 事务相关注意:
@Transactional默认仅回滚RuntimeException,检查型异常(如SQLException)需通过rollbackFor指定;- 多数据源场景需通过
transactionManager属性指定对应的事务管理器。
- 缓存相关注意:
@Cacheable的key需使用 SpEL 表达式(如#id),避免硬编码;- 缓存对象需实现序列化(如 Redis 缓存时),否则会抛出序列化异常。
- 权限注解注意:
- Spring Security 6 + 推荐使用
@EnableMethodSecurity替代旧版@EnableGlobalMethodSecurity; @Secured注解的角色名称默认需带ROLE_前缀,可通过配置rolePrefix = ""取消前缀。
- 依赖注入选择:
- 优先使用
@Autowired(Spring 原生,支持更多特性); - 跨框架兼容场景可使用
@Resource(JSR-250)或@Inject(JSR-330)。
通过本文整理的 Spring 核心注解,可覆盖大多数开发场景,合理使用注解能大幅简化配置、解耦业务逻辑,提升开发效率。实际开发中需结合具体场景选择合适的注解,并注意避免常见陷阱(如代理失效、事务回滚异常等)。