Spring 注解使用详解
Spring 的注解体系覆盖了从 Bean 注册、依赖注入、AOP、事务到 Web 开发的方方面面。注解把配置信息直接写在代码上,让开发和维护都更直观。本文按功能分类,逐一说明常用注解的用法、适用场景和底层机制。
一、注解生效的底层机制
Spring 处理注解主要依赖两个扩展点:
BeanPostProcessor:在 Bean 初始化前后介入,处理@Autowired、@Value、@PostConstruct等注解。AutowiredAnnotationBeanPostProcessor负责依赖注入,CommonAnnotationBeanPostProcessor负责@Resource、@PostConstruct、@PreDestroy。BeanFactoryPostProcessor:在 Bean 实例化之前介入,处理@ComponentScan、@Configuration、@Bean等注解。ConfigurationClassPostProcessor负责解析配置类,生成 BeanDefinition。
注解本身不会"自动生效",是这些后置处理器在容器启动时扫描并执行对应逻辑。
二、声明 Bean 的注解
1. @Component 及其派生注解
| 注解 | 语义 | 特殊功能 |
|---|---|---|
@Component |
通用组件 | 无 |
@Service |
业务层 | 无(语义清晰) |
@Repository |
数据访问层 | 异常转换 |
@Controller |
Web 控制层 | 视图解析 |
@RestController |
REST 控制器 | 直接返回 JSON |
@Configuration |
配置类 | CGLIB 代理 |
java
@Service
public class UserService {
@Autowired
private UserDao userDao;
}
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
}
Bean 名称默认是类名首字母小写,也可以显式指定:@Service("userService")。
2. @Bean
标注在 @Configuration 类的方法上,把返回值注册为 Bean。适合第三方类或需要复杂初始化逻辑的对象。
java
@Configuration
public class AppConfig {
@Bean
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
return ds;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
@Bean 方法参数会自动从容器注入。@Bean(initMethod = "init", destroyMethod = "cleanup") 可以指定初始化和销毁方法。
三、依赖注入注解
1. @Autowired
按类型自动注入,可以标注在字段、构造方法、Setter 方法上。
java
// 构造器注入(推荐)
@Service
public class UserService {
private final UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}
// 字段注入
@Service
public class UserService {
@Autowired
private UserDao userDao;
}
required = false 允许依赖为 null。构造器注入是 Spring 官方推荐的方式,保证依赖不可变且完全就绪。
2. @Qualifier
同类型有多个 Bean 时,按名称指定:
java
@Service
public class OrderService {
@Autowired
@Qualifier("aliPayService")
private PaymentService paymentService;
}
3. @Primary
同类型多 Bean 时,标记首选:
java
@Service
@Primary
public class AliPayService implements PaymentService { }
@Qualifier 优先级高于 @Primary。
4. @Value
注入简单类型和配置值:
java
@Component
public class AppConfig {
@Value("${app.name}")
private String appName;
@Value("${app.timeout:30}")
private int timeout;
}
5. @Resource
JSR-250 标准,默认按名称注入:
java
@Service
public class UserService {
@Resource(name = "userDaoImpl")
private UserDao userDao;
}
与 @Autowired 的区别:@Autowired 默认按类型,@Resource 默认按名称;@Autowired 支持 required = false 和构造器注入,@Resource 不支持。
四、配置与扫描注解
1. @ComponentScan
指定扫描包路径:
java
@Configuration
@ComponentScan("com.example")
public class AppConfig { }
2. @Import
导入其他配置类:
java
@Configuration
@Import({DatabaseConfig.class, RedisConfig.class})
public class AppConfig { }
3. @PropertySource
加载自定义 properties 文件:
java
@Configuration
@PropertySource("classpath:custom.properties")
public class AppConfig {
@Value("${custom.value}")
private String value;
}
4. @ConfigurationProperties
批量绑定配置到对象:
java
@Component
@ConfigurationProperties(prefix = "app")
@Getter
@Setter
public class AppProperties {
private String name;
private int timeout;
private List<String> servers;
}
五、条件化与 Profile 注解
1. @Conditional
根据条件决定是否注册 Bean:
java
@Bean
@Conditional(WindowsCondition.class)
public FileService windowsFileService() {
return new WindowsFileService();
}
2. Spring Boot 条件注解
| 注解 | 条件 |
|---|---|
@ConditionalOnClass |
classpath 存在指定类 |
@ConditionalOnMissingBean |
容器中不存在指定 Bean |
@ConditionalOnProperty |
配置属性满足条件 |
@ConditionalOnWebApplication |
是 Web 应用 |
java
@Bean
@ConditionalOnMissingBean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
3. @Profile
按环境激活:
java
@Configuration
@Profile("dev")
public class DevConfig { }
@Configuration
@Profile("prod")
public class ProdConfig { }
六、AOP 注解
1. 开启 AOP
java
@Configuration
@EnableAspectJAutoProxy
public class AopConfig { }
Spring Boot 中引入 spring-boot-starter-aop 后自动生效。
2. 定义切面
java
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
@Before("serviceLayer()")
public void before(JoinPoint jp) {
System.out.println("执行前:" + jp.getSignature().getName());
}
@AfterReturning(pointcut = "serviceLayer()", returning = "result")
public void afterReturning(JoinPoint jp, Object result) {
System.out.println("返回:" + result);
}
@AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
public void afterThrowing(JoinPoint jp, Exception ex) {
System.out.println("异常:" + ex.getMessage());
}
@Around("serviceLayer()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed();
System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");
return result;
}
}
七、事务注解
java
@Service
public class OrderService {
@Transactional(rollbackFor = Exception.class,
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 30)
public void createOrder(Order order) {
orderDao.save(order);
inventoryDao.decrease(order.getProductId(), order.getQuantity());
}
}
需要 @EnableTransactionManagement(Spring Boot 自动配置)。注意 @Transactional 在同一个类内部调用不生效,必须通过代理调用。
八、Web 注解
1. 请求映射
java
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.getById(id);
}
@PostMapping
public User create(@RequestBody @Valid User user) {
return userService.save(user);
}
@GetMapping
public List<User> list(@RequestParam(required = false) String name) {
return userService.findByName(name);
}
}
2. 全局异常处理
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusiness(BusinessException e) {
return ResponseEntity.badRequest()
.body(new ErrorResponse(e.getMessage()));
}
}
九、测试注解
java
@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@MockBean
private UserDao userDao;
@Test
void testGetUser() {
when(userDao.findById(1L)).thenReturn(new User(1L, "张三"));
User user = userService.getById(1L);
assertEquals("张三", user.getName());
}
}
常用测试注解:@SpringBootTest、@WebMvcTest、@DataJpaTest、@MybatisTest、@MockBean、@SpyBean、@TestPropertySource。
十、其他常用注解
| 注解 | 作用 |
|---|---|
@Async |
异步执行方法,需 @EnableAsync |
@Scheduled |
定时任务,需 @EnableScheduling |
@Cacheable |
缓存方法结果,需 @EnableCaching |
@CacheEvict |
清除缓存 |
@EventListener |
事件监听 |
@Order |
控制 Bean 加载顺序 |
@Lazy |
延迟初始化 |
@Scope |
指定作用域 |
java
@Async
public CompletableFuture<String> sendEmail(String to) { ... }
@Scheduled(cron = "0 0 2 * * ?")
public void dailyReport() { ... }
@Cacheable(value = "users", key = "#id")
public User getUser(Long id) { ... }
十一、注解使用最佳实践
- 按层选择语义注解:Controller 用
@RestController,Service 用@Service,Dao 用@Repository。 - 优先使用构造器注入,保证依赖不可变和完整。
- 第三方类无法加注解时,用
@Configuration+@Bean注册。 - 多环境配置用
@Profile,避免运行时 if-else。 - 提供默认实现时用
@ConditionalOnMissingBean,允许用户覆盖。 - 事务注解放在 Service 层,注意内部调用失效问题。
- AOP 切面只处理真正的横切关注点,避免滥用导致调试困难。
- 测试中使用
@MockBean隔离外部依赖,用切片测试代替完整@SpringBootTest提升速度。
十二、总结
| 分类 | 核心注解 |
|---|---|
| 声明 Bean | @Component、@Service、@Repository、@Controller、@RestController、@Configuration、@Bean |
| 依赖注入 | @Autowired、@Qualifier、@Primary、@Value、@Resource |
| 配置与扫描 | @ComponentScan、@Import、@PropertySource、@ConfigurationProperties |
| 条件与 Profile | @Conditional、@ConditionalOnXxx、@Profile |
| AOP | @Aspect、@Pointcut、@Before、@After、@Around |
| 事务 | @Transactional |
| Web | @RequestMapping、@GetMapping、@PostMapping、@RequestParam、@PathVariable、@RequestBody、@RestControllerAdvice |
| 测试 | @SpringBootTest、@MockBean、@WebMvcTest |
| 其他 | @Async、@Scheduled、@Cacheable、@EventListener |
Spring 注解体系庞大但逻辑清晰:声明 Bean 靠 @Component 系列和 @Bean,注入依赖靠 @Autowired 系列,扩展功能靠 @EnableXxx 和条件注解。掌握这些注解的语义、生效机制和适用场景,就能在开发中灵活组合,写出简洁且易维护的 Spring 应用。