Spring / Spring Boot 常用注解
核心原则(先看这段)
把行为(业务)放在 Service 层,事务/缓存/异步等注解一般放在 Service 层,不要把复杂逻辑放到 Controller。
首选构造器注入(constructor injection),比字段注入更利于测试与不可变性。
注解很多都是“语义化的标记 + 框架在运行时的处理”,理解背后的代理/生命周期很重要(例如 @Transactional 基于 AOP 代理,自调用不会触发代理逻辑)。
核心/启动与配置注解
@SpringBootApplication // 等价于 @Configuration + @EnableAutoConfiguration + @ComponentScan(默认扫描启动类包及子包)
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}
@Configuration:Java 配置类(替代 XML)。
@Bean:方法级别把返回对象注册为 Spring Bean(可设置 init/destroy)。
@ComponentScan:定制扫描包路径(启动类包位置很关键,建议把主类放在根包)。
@EnableConfigurationProperties:启用 @ConfigurationProperties 绑定(在 Spring Boot 中,直接将 @ConfigurationProperties 标注成 @Component 也可被扫描到)。
@ConfigurationProperties(prefix="..."):批量强类型注入配置(推荐用于复杂/层级配置,支持校验 @Validated)。
示例(YAML + 配置类):
# application.yml
app:name: demotimeout: 30nested:enabled: true
@Component
@ConfigurationProperties(prefix = "app")
@Validated
public class AppProperties {@NotBlankprivate String name;private int timeout;private Nested nested = new Nested();// getters/setterspublic static class Nested { private boolean enabled; /* getter/setter */ }
}
组件(stereotype)注解与区别
@Component:通用组件(任何层通用)。
@Service:业务层语义化(仅为语义,便于识别)。
@Repository:持久层语义化,且启用 异常翻译(把 JPA/Hibernate 的异常翻译为 Spring 的 DataAccessException)。
@Controller:MVC 控制器,通常返回视图(与 @ResponseBody 组合可返回 JSON)。
@RestController:@Controller + @ResponseBody,更常用于 REST API。
何时用?按层次用对应注解(可读性和工具/监控的好处)。
依赖注入 / Bean 选择 / Scope / 生命周期
注入方式对比(推荐):
构造器注入(推荐):支持 final,利于单元测试。
Setter 注入:有状态或可选依赖时可用。
字段注入(@Autowired 在字段上):不推荐(难测、不可 final)。
@Service
public class MyService {private final UserRepository repo;public MyService(UserRepository repo) { this.repo = repo; } // 自动注入(Spring 4.3+)
}
@Autowired(按类型注入),@Qualifier("name") 或 @Primary(用于同类型多个 Bean);
@Resource(JSR-250,按名称优先);
@Value("${xxx}")(注入单个配置值);
@ConfigurationProperties(批量注入复杂配置);
Bean scope:
@Scope("singleton")(默认)
@Scope("prototype")(每次请求新实例)
还有 request、session(web 场景)等
生命周期:
@PostConstruct、@PreDestroy(JSR)
或在 @Bean(initMethod="...", destroyMethod="...") 指定
InitializingBean / DisposableBean(接口)
示例:
@PostConstruct
public void init(){ /* init work */ }
@PreDestroy
public void cleanup(){ /* release */ }
Web / Controller 相关注解(常用)
路由映射:@RequestMapping、@GetMapping、@PostMapping、@PutMapping、@DeleteMapping。
参数绑定:@PathVariable、@RequestParam、@RequestBody、@RequestHeader、@CookieValue。
返回:@ResponseBody(将返回对象序列化为 JSON) → @RestController 更常用。
状态码:@ResponseStatus(HttpStatus.CREATED)。
验证:@Valid(与 Bean Validation 联动) + DTO 上加注解 @NotNull/@Size...。
异常处理:@ControllerAdvice + @ExceptionHandler(全局异常/统一返回)或 @RestControllerAdvice。
示例:
@RestController
@RequestMapping("/users")
public class UserController {@PostMapping@ResponseStatus(HttpStatus.CREATED)public UserDto create(@Valid @RequestBody CreateUserDto dto) { ... }
}
数据访问与事务(重要)
@Repository(DAO层) + JPA @Entity / MyBatis 接口等。
@Transactional(关键点):
放在 Service 层方法上最合适(控制事务边界)。
常用属性:propagation、isolation、readOnly、rollbackFor。
注意:@Transactional 基于代理(AOP),内部自调用不会触发事务(self-invocation)。
@Service
public class OrderService {@Transactional(rollbackFor = Exception.class)public void placeOrder(OrderDto dto){ ... }
}
常见坑:
在同一个类里面 public methodA() 调 private methodB() 并给 methodB 标注 @Transactional,事务不会生效(因为代理不会拦截内部调用)。解决:把方法拆到另一个 Bean,或使用 AopContext.currentProxy() (不常用)。
AOP(切面)相关注解
@Aspect(切面类) + @Before / @After / @AfterReturning / @Around / @Pointcut。
典型用途:日志、监控、权限校验、限流、重试等。
@Aspect
@Component
public class LoggingAspect {@Pointcut("execution(* com.example.service..*(..))")public void serviceMethods() {}@Around("serviceMethods()")public Object log(ProceedingJoinPoint pjp) throws Throwable {long t = System.currentTimeMillis();Object ret = pjp.proceed();System.out.println("took " + (System.currentTimeMillis() - t));return ret;}
}
异步 / 调度 / 缓存 / 安全(常见功能注解)
异步:@EnableAsync(配置类) + @Async(方法) → 返回 void / Future / CompletableFuture。注意自调用失效。
调度:@EnableScheduling + @Scheduled(cron = "...")。
缓存:@EnableCaching + @Cacheable / @CacheEvict / @CachePut。
方法级安全:@PreAuthorize("hasRole('ADMIN')") / @Secured,配合 @EnableMethodSecurity(或旧版 @EnableGlobalMethodSecurity)。
示例(异步):
@Configuration
@EnableAsync
public class AsyncConfig {}
@Service
public class MailService {@Asyncpublic CompletableFuture<Void> sendAsync(...) { ... }
}
条件化 & 自动配置注解(Spring Boot 自动化)
@ConditionalOnProperty(name="...", havingValue="..."):基于配置决定是否装配某 Bean。
@ConditionalOnClass:当类在 classpath 上时才生效(常用在 starter/自动配置中)。
@ConditionalOnMissingBean:容器中没有指定类型的 Bean 时才创建默认 bean。
@Profile("dev"):按 profile 加载(spring.profiles.active=dev)。
示例(有默认实现但允许覆盖):
@Configuration
@ConditionalOnClass(SomeClient.class)
public class AutoConfig {@Bean@ConditionalOnMissingBeanpublic MyClient myClient(){ return new DefaultMyClient(); }
}
测试相关注解(常见)
@SpringBootTest:集成测试,启动整个上下文(慢)。
@WebMvcTest(controllers = ...):只加载 Web 层(适合 Controller 测试)。
@DataJpaTest:只加载 JPA 相关组件(内存数据库)。
@MockBean:在 Spring 容器中替换某个 Bean 为 mock。
@TestConfiguration:测试专用配置类。
常见坑、注意事项与最佳实践(实战派)
优先构造器注入(可用 final,更安全)。
@Transactional 放在 public 方法上且不要依赖自调用来触发事务。
Controller 层只做参数校验/返回,不做事务处理和复杂业务(业务放 Service)。
使用 @ConfigurationProperties 管理复杂配置,@Value 仅用于单值注入。
@Repository 会做异常翻译(推荐持久层使用)。
@RestControllerAdvice + @ExceptionHandler 做统一异常处理和统一响应结构。
生产环境建议对定时任务、异步线程池、缓存策略做合理配置与监控。
用 @Profile 管理 dev/test/prod 配置差异。
注意 @Async / @Transactional/@Cacheable 等基于代理的注解,均受自调用影响。
速查小表(快速记忆)
启动:@SpringBootApplication
配置:@Configuration / @Bean / @ConfigurationProperties
组件:@Component / @Service / @Repository / @Controller / @RestController
注入:@Autowired / @Qualifier / @Value / 构造器注入
Web:@GetMapping / @PostMapping / @RequestBody / @PathVariable
事务:@Transactional(放 Service)
AOP:@Aspect / @Around / @Pointcut
异步:@EnableAsync + @Async
调度:@EnableScheduling + @Scheduled
缓存:@EnableCaching + @Cacheable
条件/自动配置:@ConditionalOnProperty / @ConditionalOnClass / @ConditionalOnMissingBean
测试:@SpringBootTest / @WebMvcTest / @MockBean
代码示例:Controller → Service → Repository + 配置属性(可拷贝运行)
// Application.java
@SpringBootApplication
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}
}// AppProperties.java
@Component
@ConfigurationProperties(prefix = "app")
@Validated
public class AppProperties {@NotBlank private String name;private int timeout;// getters/setters
}// UserEntity.java
@Entity
@Table(name = "users")
public class User {@Id @GeneratedValue private Long id;private String username;// getters/setters
}// UserRepository.java (Spring Data JPA example)
@Repository
public interface UserRepository extends JpaRepository<User, Long> {}// UserService.java
@Service
public class UserService {private final UserRepository userRepository;public UserService(UserRepository userRepository) { this.userRepository = userRepository; }@Transactionalpublic User createUser(String username) {User u = new User(); u.setUsername(username);return userRepository.save(u);}@Transactional(readOnly = true)public User getUser(Long id){ return userRepository.findById(id).orElse(null); }
}// UserController.java
@RestController
@RequestMapping("/users")
public class UserController {private final UserService userService;private final AppProperties props;public UserController(UserService userService, AppProperties props){this.userService = userService; this.props = props;}@PostMappingpublic ResponseEntity<User> create(@RequestParam String name){User u = userService.createUser(name);return ResponseEntity.status(HttpStatus.CREATED).body(u);}@GetMapping("/{id}")public ResponseEntity<User> get(@PathVariable Long id){return ResponseEntity.of(Optional.ofNullable(userService.getUser(id)));}
}