Spring Boot Common Annotations
前言
SpringBoot 的注解系统是其"约定优于配置"哲学的核心体现。这篇文章不讲虚的,只讲实际开发中真正会用到的注解。我会按照功能分类,每个注解都配上最简示例。
一、核心启动注解
@SpringBootApplication
这是 SpringBoot 应用的入口注解,一个组合注解,等价于三个注解的叠加:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}它的三个组成部分:
@SpringBootConfiguration- 标记为配置类(本质是@Configuration的特化)@EnableAutoConfiguration- 启用自动配置机制@ComponentScan- 自动扫描当前包及子包下的组件
实用主义提示:别自己拆这三个注解,除非你知道自己在做什么。
二、Bean 定义注解
@Component
最基础的组件注解,告诉 Spring"这是个 Bean,帮我管理"。
@Component
public class EmailService {
public void send(String to, String message) {
// 发送邮件
}
}它的三个特化版本(语义更清晰):
@Service // 服务层
@Repository // 数据访问层,自带异常转换
@Controller // 控制层,配合视图解析器好品味提示:@Repository 不只是标记,它会自动将 JDBC 异常转换为 Spring 的 DataAccessException 层次结构。
@Bean
用在配置类的方法上,声明方法返回值为 Bean。
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
}使用场景:当你需要控制 Bean 的创建逻辑,或者第三方库的类无法添加 @Component 时使用。
@Configuration
标记类为配置类,相当于 XML 时代的 applicationContext.xml。
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");
return new HikariDataSource(config);
}
}三、依赖注入注解
@Autowired
Spring 提供的自动注入注解,按类型注入。
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private EmailService emailService;
}实用主义提示:字段注入虽然被某些"原教旨主义者"鄙视,但在实际开发中是最简洁的方案。构造器注入适合强制依赖,字段注入适合可选依赖。
@Qualifier
配合 @Autowired 使用,当有多个同类型 Bean 时指定具体使用哪个。
@Service
public class PaymentService {
@Autowired
@Qualifier("alipayStrategy")
private PaymentStrategy paymentStrategy;
}@Resource
JSR-250 标准注解,按名称注入(默认)。
@Service
public class OrderService {
@Resource(name = "userRepository")
private UserRepository userRepository;
}对比:@Autowired 是 Spring 原生,@Resource 是 Java 标准。团队如果用 Spring 全家桶,用 @Autowired 没问题;如果要保持框架无关性,用 @Resource。
四、Web 相关注解
@RestController
组合注解,@Controller + @ResponseBody,用于构建 REST API。
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public ResponseEntity<ApiResponse<List<UserDTO>>> getAll() {
List<UserDTO> users = userService.findAll();
return ResponseEntity.ok(new ApiResponse<>("SUCCESS", "获取成功", users));
}
}@RequestMapping 及其变体
@RestController
@RequestMapping("/api/users") // 类级别路径
public class UserController {
@GetMapping // GET /api/users
public List<User> list() { ... }
@GetMapping("/{id}") // GET /api/users/{id}
public User get(@PathVariable Long id) { ... }
@PostMapping // POST /api/users
public User create(@RequestBody UserCreateRequest req) { ... }
@PutMapping("/{id}") // PUT /api/users/{id}
public User update(@PathVariable Long id, @RequestBody UserUpdateRequest req) { ... }
@DeleteMapping("/{id}") // DELETE /api/users/{id}
public void delete(@PathVariable Long id) { ... }
}@RequestBody / @ResponseBody
@PostMapping
public User create(@RequestBody UserCreateRequest request) {
// request 自动从 JSON 反序列化
return userService.create(request);
}
@GetMapping("/{id}")
@ResponseBody // @RestController 中可省略
public UserDTO get(@PathVariable Long id) {
return userService.findById(id);
}@PathVariable / @RequestParam / @RequestHeader
@GetMapping("/{id}/posts")
public List<Post> getUserPosts(
@PathVariable Long id, // 路径参数
@RequestParam(defaultValue = "0") int page, // 查询参数 ?page=0
@RequestParam(required = false) String keyword, // 可选查询参数
@RequestHeader("Authorization") String token // 请求头
) {
return postRepository.findByUserId(id, page, keyword);
}@RestControllerAdvice
全局异常处理和模型增强的利器。
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ApiResponse<?>> handleIllegalArgument(IllegalArgumentException ex) {
ApiResponse<?> response = new ApiResponse<>("ERROR", ex.getMessage(), null);
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiResponse<?>> handleGeneric(Exception ex) {
ApiResponse<?> response = new ApiResponse<>("ERROR", "服务器内部错误", null);
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
}五、配置相关注解
@Value
注入配置文件中的值。
@Service
public class EmailService {
@Value("${email.smtp.host}")
private String smtpHost;
@Value("${email.smtp.port:587}") // 带默认值
private int smtpPort;
@Value("${app.feature.new-ui:false}") // 带默认值
private boolean enableNewUi;
}@ConfigurationProperties
类型安全地绑定配置属性。
@ConfigurationProperties(prefix = "app.email")
@Data
public class EmailProperties {
private String smtpHost;
private int smtpPort;
private String username;
private String password;
}
// 使用方式
@Service
public class EmailService {
private final EmailProperties properties;
public EmailService(EmailProperties properties) {
this.properties = properties;
}
public void send() {
// 使用 properties.getSmtpHost() 等
}
}好品味提示:当配置项超过 3 个且有关联性时,用 @ConfigurationProperties 替代一堆 @Value,代码更清晰。
@PropertySource
指定额外的配置文件。
@Configuration
@PropertySource("classpath:custom.properties")
public class CustomConfig {
// ...
}六、数据访问注解
@Entity
JPA 实体类标记。
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String email;
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Order> orders;
}@Repository
数据访问层注解。
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
List<User> findByEmailContaining(String keyword);
@Query("SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :id")
Optional<User> findByIdWithOrders(@Param("id") Long id);
}@Transactional
声明式事务管理。
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private InventoryRepository inventoryRepository;
@Transactional // 方法内所有数据库操作在同一事务中
public Order createOrder(OrderCreateRequest request) {
// 扣减库存
inventoryRepository.decrease(request.getProductId(), request.getQuantity());
// 创建订单
Order order = new Order();
order.setProductId(request.getProductId());
order.setQuantity(request.getQuantity());
return orderRepository.save(order);
}
@Transactional(readOnly = true) // 只读事务,性能优化
public List<Order> findAll() {
return orderRepository.findAll();
}
}铁律提示:@Transactional 只能用于 public 方法,自调用不会触发事务。
@EntityGraph
解决 N+1 查询问题。
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
@EntityGraph(attributePaths = {"user", "items"})
List<Order> findAll();
@EntityGraph(attributePaths = "items.product")
Optional<Order> findById(Long id);
}七、条件注解
SpringBoot 自动配置的核心机制。
@ConditionalOnClass
当类路径存在指定类时生效。
@Configuration
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {
// 只有存在 DataSource 类时才配置数据源
}@ConditionalOnProperty
当配置属性满足条件时生效。
@Configuration
@ConditionalOnProperty(name = "app.cache.enabled", havingValue = "true")
public class CacheConfig {
// 只有 app.cache.enabled=true 时才启用缓存配置
}@ConditionalOnBean / @ConditionalOnMissingBean
@Configuration
public class CustomConfig {
@Bean
@ConditionalOnMissingBean // 当容器中没有该类型的 Bean 时才创建
public RestTemplate restTemplate() {
return new RestTemplate();
}
}八、其他实用注解
@Profile
环境隔离。
@Configuration
@Profile("dev")
public class DevConfig {
// 仅 dev 环境生效
}
@Service
@Profile("!prod") // 非生产环境
public class MockEmailService implements EmailService {
// 开发环境使用的模拟邮件服务
}@Scheduled
定时任务。
@Service
public class CleanupService {
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨 2 点
public void cleanupTempFiles() {
// 清理临时文件
}
@Scheduled(fixedRate = 60000) // 每 60 秒
public void checkHealth() {
// 健康检查
}
}@Async
异步执行。
@Service
public class NotificationService {
@Async // 异步执行,不阻塞调用线程
public void sendNotification(User user, String message) {
// 发送邮件/短信
}
}
// 需要在配置类上添加 @EnableAsync
@Configuration
@EnableAsync
public class AsyncConfig {
// 异步配置
}@Validated / @Valid
参数校验。
@RestController
@RequestMapping("/api/users")
@Validated
public class UserController {
@PostMapping
public User create(@Valid @RequestBody UserCreateRequest request) {
return userService.create(request);
}
}
// DTO 中的校验注解
public record UserCreateRequest(
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度 3-20 字符")
String username,
@NotBlank
@Email(message = "邮箱格式不正确")
String email,
@NotNull
@Min(value = 1, message = "年龄最小为 1")
Integer age
) {}九、注解使用最佳实践
1. 分层清晰
@RestController → 处理 HTTP 请求/响应
↓
@Service → 业务逻辑
↓
@Repository → 数据访问2. 避免循环依赖
// 错误示例:循环依赖
@Service A { @Autowired B b; }
@Service B { @Autowired A a; }
// 解决方案:抽取共同逻辑到 C,A 和 B 都依赖 C3. 事务边界合理
// 错误:事务范围过大
@Transactional
public void processBatch(List<Order> orders) {
for (Order order : orders) {
// 处理每个订单,可能调用外部 API
}
}
// 正确:事务范围最小化
public void processBatch(List<Order> orders) {
for (Order order : orders) {
processSingle(order); // 这个方法加 @Transactional
}
}总结
SpringBoot 的注解系统看似繁杂,实则有其内在逻辑:
| 分类 | 核心注解 |
|---|---|
| 启动 | @SpringBootApplication |
| Bean 定义 | @Component, @Service, @Repository, @Bean |
| 依赖注入 | @Autowired, @Qualifier, @Resource |
| Web | @RestController, @RequestMapping, @RequestBody |
| 配置 | @Value, @ConfigurationProperties |
| 数据 | @Entity, @Transactional, @EntityGraph |
| 条件 | @ConditionalOnClass, @ConditionalOnProperty |
| 其他 | @Profile, @Scheduled, @Async, @Validated |
总结:注解是工具,不是银弹。理解每个注解背后的机制,比死记硬背更重要。代码是写给人看的,注解也是——让读代码的人一眼就能明白你的意图,这才是好注解的用法。
