前言

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 都依赖 C

3. 事务边界合理

// 错误:事务范围过大
@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

总结:注解是工具,不是银弹。理解每个注解背后的机制,比死记硬背更重要。代码是写给人看的,注解也是——让读代码的人一眼就能明白你的意图,这才是好注解的用法。