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

相关推荐
未秃头的程序猿5 分钟前
Spring Cloud Gateway 做统一加解密,我在请求体上栽了三天
java·后端·架构
Rain的Java大神之路5 分钟前
别再上来就堆 Redis!点赞系统从打挂数据库到 18w 写峰值,我把这套方案讲透了
java·后端·面试
SimonKing9 分钟前
开源文档解析天花板 MinerU 4.0:PDF / 图片 / Office 等任意文档都支持
java·后端·程序员
咖啡八杯14 分钟前
Spring MessageSource 国际化消息解析流程
java·后端·spring·架构·框架·若依
博.闻广见30 分钟前
第10篇:Agent系统的性能优化 —— 缓存、压缩与动态模型路由
java·人工智能·后端·spring·agent
Irene19911 小时前
Flink 学习日志 01:前端转大数据的实战避坑指南
java·jvm·flink
Thomas.Sir1 小时前
第1课:SpringBoot 4.x 快速上手:新特性、环境准备与第一个HelloWorld程序
java·spring boot
devilnumber1 小时前
Java 内部迭代 vs 外部迭代:从原理到实战避坑
java·开发语言
杨运交1 小时前
080-缓存模块-Spring Cache缓存键前缀设计与租户隔离实践
java·spring·缓存
\光辉岁月/1 小时前
5.math类
java·开发语言