SpringBoot 的注解系统是其"约定优于配置"哲学的核心体现。这篇文章不讲虚的,只讲实际开发中真正会用到的注解。我会按照功能分类,每个注解都配上最简示例。
核心启动注解
@SpringBootApplication
这是 SpringBoot 应用的入口注解,一个组合注解,等价于三个注解的叠加:
1 2 3 4 5 6
| @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
|
它的三个组成部分:
@SpringBootConfiguration - 标记为配置类(本质是 @Configuration 的特化)@EnableAutoConfiguration - 启用自动配置机制@ComponentScan - 自动扫描当前包及子包下的组件
实用主义提示:别自己拆这三个注解,除非你知道自己在做什么。
Bean 定义注解
@Component
最基础的组件注解,告诉 Spring"这是个 Bean,帮我管理"。
1 2 3 4 5 6
| @Component public class EmailService { public void send(String to, String message) { } }
|
它的三个特化版本(语义更清晰):
1 2 3
| @Service @Repository @Controller
|
好品味提示:@Repository 不只是标记,它会自动将 JDBC 异常转换为 Spring 的 DataAccessException 层次结构。
@Bean
用在配置类的方法上,声明方法返回值为 Bean。
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Configuration public class AppConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } @Bean public ObjectMapper objectMapper() { return new ObjectMapper(); } }
|
使用场景:当你需要控制 Bean 的创建逻辑,或者第三方库的类无法添加 @Component 时使用。
@Configuration
标记类为配置类,相当于 XML 时代的 applicationContext.xml。
1 2 3 4 5 6 7 8 9 10
| @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 提供的自动注入注解,按类型注入。
1 2 3 4 5 6 7 8 9
| @Service public class UserService { @Autowired private UserRepository userRepository; @Autowired private EmailService emailService; }
|
实用主义提示:字段注入虽然被某些"原教旨主义者"鄙视,但在实际开发中是最简洁的方案。构造器注入适合强制依赖,字段注入适合可选依赖。
@Qualifier
配合 @Autowired 使用,当有多个同类型 Bean 时指定具体使用哪个。
1 2 3 4 5 6 7
| @Service public class PaymentService { @Autowired @Qualifier("alipayStrategy") private PaymentStrategy paymentStrategy; }
|
@Resource
JSR-250 标准注解,按名称注入(默认)。
1 2 3 4 5 6
| @Service public class OrderService { @Resource(name = "userRepository") private UserRepository userRepository; }
|
对比:@Autowired 是 Spring 原生,@Resource 是 Java 标准。团队如果用 Spring 全家桶,用 @Autowired 没问题;如果要保持框架无关性,用 @Resource。
Web 相关注解
@RestController
组合注解,@Controller + @ResponseBody,用于构建 REST API。
1 2 3 4 5 6 7 8 9 10 11 12 13
| @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 及其变体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @RestController @RequestMapping("/api/users") public class UserController { @GetMapping public List<User> list() { ... } @GetMapping("/{id}") public User get(@PathVariable Long id) { ... } @PostMapping public User create(@RequestBody UserCreateRequest req) { ... } @PutMapping("/{id}") public User update(@PathVariable Long id, @RequestBody UserUpdateRequest req) { ... } @DeleteMapping("/{id}") public void delete(@PathVariable Long id) { ... } }
|
@RequestBody/@ResponseBody
1 2 3 4 5 6 7 8 9 10 11
| @PostMapping public User create(@RequestBody UserCreateRequest request) { return userService.create(request); }
@GetMapping("/{id}") @ResponseBody public UserDTO get(@PathVariable Long id) { return userService.findById(id); }
|
1 2 3 4 5 6 7 8 9
| @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
全局异常处理和模型增强的利器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @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
注入配置文件中的值。
1 2 3 4 5 6 7 8 9 10 11 12
| @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
类型安全地绑定配置属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @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() { } }
|
好品味提示:当配置项超过 3 个且有关联性时,用 @ConfigurationProperties 替代一堆 @Value,代码更清晰。
@PropertySource
指定额外的配置文件。
1 2 3 4 5
| @Configuration @PropertySource("classpath:custom.properties") public class CustomConfig { }
|
数据访问注解
@Entity
JPA 实体类标记。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @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
数据访问层注解。
1 2 3 4 5 6 7 8 9 10
| @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
声明式事务管理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @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 查询问题。
1 2 3 4 5 6 7 8 9
| @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
当类路径存在指定类时生效。
1 2 3 4 5
| @Configuration @ConditionalOnClass(DataSource.class) public class DataSourceAutoConfiguration { }
|
@ConditionalOnProperty
当配置属性满足条件时生效。
1 2 3 4 5
| @Configuration @ConditionalOnProperty(name = "app.cache.enabled", havingValue = "true") public class CacheConfig { }
|
@ConditionalOnBean / @ConditionalOnMissingBean
1 2 3 4 5 6 7 8 9
| @Configuration public class CustomConfig { @Bean @ConditionalOnMissingBean public RestTemplate restTemplate() { return new RestTemplate(); } }
|
其他实用注解
@Profile
环境隔离。
1 2 3 4 5 6 7 8 9 10 11
| @Configuration @Profile("dev") public class DevConfig { }
@Service @Profile("!prod") public class MockEmailService implements EmailService { }
|
@Scheduled
定时任务。
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Service public class CleanupService { @Scheduled(cron = "0 0 2 * * ?") public void cleanupTempFiles() { } @Scheduled(fixedRate = 60000) public void checkHealth() { } }
|
@Async
异步执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Service public class NotificationService { @Async public void sendNotification(User user, String message) { } }
@Configuration @EnableAsync public class AsyncConfig { }
|
@Validated / @Valid
参数校验。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @RestController @RequestMapping("/api/users") @Validated public class UserController { @PostMapping public User create(@Valid @RequestBody UserCreateRequest request) { return userService.create(request); } }
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 2 3 4 5
| @RestController → 处理 HTTP 请求/响应 ↓ @Service → 业务逻辑 ↓ @Repository → 数据访问
|
避免循环依赖
1 2 3 4 5
| @Service A { @Autowired B b; } @Service B { @Autowired A a; }
|
事务边界合理
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Transactional public void processBatch(List<Order> orders) { for (Order order : orders) { } }
public void processBatch(List<Order> orders) { for (Order order : orders) { processSingle(order); } }
|
总结
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 |
总结:注解是工具,不是银弹。理解每个注解背后的机制,比死记硬背更重要。代码是写给人看的,注解也是——让读代码的人一眼就能明白你的意图,这才是好注解的用法。