Spring 注解使用详解

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 应用。

相关推荐
一条小小yu2 小时前
Spring IoC的理解
java·后端·spring
落魄实习生2 小时前
Agent Scope Java 2.x 系列【7】工具使用
java·开发语言·ai
旺仔学长 哈哈2 小时前
springboot钓鱼爱好者交流平台APP设计与实现
java·spring boot·mysql·充电桩管理系统
自强的小白4 小时前
核心功能(Service接口)
java·mybatis
乌暮5 小时前
深入理解 Java 泛型:把「万能盒子」用对、用稳
java·开发语言·后端·学习
大猫和小黄5 小时前
深入理解 Java Optional:从设计哲学到工程实战
java
数据库百宝箱5 小时前
rum&gin索引对比
java·数据库·gin
yychen_java5 小时前
第六篇:Spring AI 实战:将 Java 业务接口封装成企业级 MCP Server
java·人工智能·spring