Spring Boot 常用注解:功能与示例

注:@Mapper@MapperScan 属于 MyBatis@FeignClient 属于 Spring Cloud OpenFeign;其余主要来自 Spring / Spring Boot。


Web 接口

@RestController

声明一个 REST Controller。等价于 @Controller + @ResponseBody,方法返回值会自动序列化为 JSON。

@RestController
public class UserController {

    @GetMapping("/users/{id}")
    public UserDTO getUser(@PathVariable Long id) {
        return new UserDTO(id, "Alice");
    }
}

适用场景:JSON API 服务。


@RequestMapping

定义请求路径、HTTP 方法、请求头或参数匹配规则。可标在类或方法上。

@RestController
@RequestMapping("/api/users")
public class UserController {

    @RequestMapping(
        value = "/{id}",
        method = RequestMethod.GET
    )
    public UserDTO getUser(@PathVariable Long id) {
        return userService.getUser(id);
    }
}

通常更推荐语义更清晰的快捷注解:

@GetMapping("/{id}")
@PostMapping
@PutMapping("/{id}")
@DeleteMapping("/{id}")

Bean 注册与业务分层

@Service

将类注册为 Spring Bean,语义上表示业务服务层。

@Service
@RequiredArgsConstructor
public class UserService {

    private final UserRepository userRepository;

    public UserDTO getUser(Long id) {
        User user = userRepository.findById(id);
        return UserDTO.from(user);
    }
}

适用场景:业务规则、用例编排、事务边界、调用多个 Repository 或外部服务。


@Component

通用的 Bean 注册注解。没有明确层次语义时使用。

@Component
public class PasswordEncoder {

    public String encode(String password) {
        return DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8));
    }
}

常用于:工具类、策略实现、事件处理器、任务执行器、非 Web/Service/Repository 的组件。


@Repository

将类注册为 Bean,语义上表示数据访问层。Spring 还会将底层持久化异常转换为统一的 DataAccessException 体系。

@Repository
@RequiredArgsConstructor
public class UserRepository {

    private final JdbcTemplate jdbcTemplate;

    public User findById(Long id) {
        return jdbcTemplate.queryForObject(
            "SELECT id, name FROM users WHERE id = ?",
            (resultSet, rowNum) -> new User(
                resultSet.getLong("id"),
                resultSet.getString("name")
            ),
            id
        );
    }
}

如果使用 Spring Data JPA,通常写接口即可:

@Repository
public interface UserJpaRepository extends JpaRepository<UserEntity, Long> {
}

MyBatis 数据访问

@Mapper

标记 MyBatis Mapper 接口,让 MyBatis 在运行时生成代理实现。

@Mapper
public interface UserMapper {

    @Select("""
        SELECT id, name
        FROM users
        WHERE id = #{id}
        """)
    UserEntity selectById(@Param("id") Long id);
}

更常见的 XML 写法:

@Mapper
public interface UserMapper {
    UserEntity selectById(Long id);
}
<mapper namespace="com.example.user.UserMapper">
    <select id="selectById" resultType="com.example.user.UserEntity">
        SELECT id, name
        FROM users
        WHERE id = #{id}
    </select>
</mapper>

UserMapper 没有手写 Impl 是正常的:MyBatis 会动态生成代理对象。


@MapperScan

批量扫描指定包下的 MyBatis Mapper,通常放在启动类或配置类上。

@SpringBootApplication
@MapperScan("com.example.user.mapper")
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

使用了 @MapperScan 后,Mapper 接口一般无需再逐个写 @Mapper


事务、异步与任务

@Transactional

声明式事务。方法正常结束时提交;出现符合回滚规则的异常时回滚。

@Service
@RequiredArgsConstructor
public class OrderService {

    private final OrderMapper orderMapper;
    private final InventoryService inventoryService;

    @Transactional
    public void createOrder(CreateOrderCommand command) {
        inventoryService.deduct(command.productId(), command.quantity());

        orderMapper.insert(new OrderEntity(
            command.userId(),
            command.productId(),
            command.quantity()
        ));
    }
}

常用属性:

@Transactional(
    rollbackFor = Exception.class,
    readOnly = true,
    timeout = 5
)
public UserDTO getUser(Long id) {
    // 只读查询
}

注意事项:

  • 注解依赖 Spring AOP 代理生效。
  • 同一个类内 this.someTransactionalMethod() 调用,可能绕过事务代理。
  • 默认主要对 RuntimeExceptionError 回滚;业务检查异常常需显式写 rollbackFor
  • 事务通常放在 Service 层,不建议放在 Controller。

@Async

让方法异步在线程池中执行,调用方不会等待其完成。

先开启异步能力:

@Configuration
@EnableAsync
public class AsyncConfig {
}

再标注方法:

@Service
public class EmailService {

    @Async
    public CompletableFuture<Void> sendWelcomeEmail(String email) {
        // 调用邮件服务
        return CompletableFuture.completedFuture(null);
    }
}

调用:

emailService.sendWelcomeEmail("user@example.com");
// 这里会继续执行,不等待邮件发送完成

注意:

  • 通常返回 voidFutureCompletableFuture
  • 同类内部调用也可能绕过代理。
  • 生产环境应配置专用线程池,避免使用默认线程池。

@Scheduled

声明定时任务。

先开启调度能力:

@Configuration
@EnableScheduling
public class SchedulerConfig {
}
@Component
public class ReportTask {

    @Scheduled(cron = "0 0 2 * * ?")
    public void generateDailyReport() {
        // 每天凌晨 2 点执行
    }

    @Scheduled(fixedDelay = 60_000)
    public void refreshCache() {
        // 上一次执行完成后,间隔 60 秒再执行
    }
}

常用参数:

@Scheduled(fixedRate = 10_000)    // 每 10 秒触发一次
@Scheduled(fixedDelay = 10_000)  // 上次执行完后等 10 秒
@Scheduled(cron = "0 */5 * * * ?") // 每 5 分钟

注意:多实例部署时,每个实例都会执行任务。需要分布式锁或任务调度平台避免重复执行。


事件机制

@EventListener

监听应用事件,实现发布者和消费者解耦。

定义事件:

public record UserRegisteredEvent(Long userId, String email) {
}

发布事件:

@Service
@RequiredArgsConstructor
public class UserService {

    private final ApplicationEventPublisher eventPublisher;

    public void register(RegisterUserCommand command) {
        Long userId = 1001L;

        eventPublisher.publishEvent(
            new UserRegisteredEvent(userId, command.email())
        );
    }
}

监听事件:

@Component
public class UserRegisteredListener {

    @EventListener
    public void onUserRegistered(UserRegisteredEvent event) {
        // 例如:初始化用户资料、发送欢迎邮件
    }
}

若希望在事务提交后才处理,可使用:

@Component
public class UserRegisteredListener {

    @TransactionalEventListener(
        phase = TransactionPhase.AFTER_COMMIT
    )
    public void onUserRegistered(UserRegisteredEvent event) {
        // 只有事务成功提交后执行
    }
}

远程 HTTP 调用

@FeignClient

声明远程 HTTP 服务客户端接口,由 OpenFeign 生成实现。

先开启:

@SpringBootApplication
@EnableFeignClients
public class Application {
}

定义客户端:

@FeignClient(
    name = "user-service",
    url = "${clients.user-service.base-url}"
)
public interface UserClient {

    @GetMapping("/api/users/{id}")
    UserDTO getUser(@PathVariable Long id);
}

像调用本地接口一样使用:

@Service
@RequiredArgsConstructor
public class OrderService {

    private final UserClient userClient;

    public OrderView getOrder(Long orderId) {
        UserDTO user = userClient.getUser(1001L);
        return new OrderView(orderId, user.name());
    }
}

注意:@FeignClient 接口通常没有本地实现类;框架会在启动时生成代理。应配置超时、重试、错误处理和熔断策略。


Java 配置与 Bean 装配

@Configuration

声明配置类,其中通常包含 @Bean 方法。

@Configuration
public class AppConfig {

    @Bean
    public Clock clock() {
        return Clock.systemUTC();
    }
}

@Configuration 常用于:第三方 SDK 初始化、线程池、HTTP Client、序列化器、条件化装配。


@Bean

将方法返回值注册到 Spring 容器。适用于无法直接修改源码、不能加 @Component 的第三方对象。

@Configuration
public class HttpClientConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

注入使用:

@Service
@RequiredArgsConstructor
public class RemoteService {

    private final RestTemplate restTemplate;
}

@Component 是“扫描类并创建对象”;@Bean 是“调用指定方法,把返回对象注册进容器”。


@Qualifier

当同一接口有多个 Bean 实现时,明确指定要注入哪一个。

public interface PaymentService {
    void pay(BigDecimal amount);
}
@Service("alipayService")
public class AlipayService implements PaymentService {
    public void pay(BigDecimal amount) {
    }
}

@Service("wechatPayService")
public class WechatPayService implements PaymentService {
    public void pay(BigDecimal amount) {
    }
}

注入指定实现:

@Service
public class CheckoutService {

    private final PaymentService paymentService;

    public CheckoutService(
            @Qualifier("alipayService") PaymentService paymentService
    ) {
        this.paymentService = paymentService;
    }
}

适用场景:支付渠道、多存储实现、多第三方客户端、不同策略实现。


@Primary

当同一类型有多个 Bean 时,标记默认优先注入的实现。

@Service
@Primary
public class DefaultPaymentService implements PaymentService {
    public void pay(BigDecimal amount) {
    }
}

@Service
public class BackupPaymentService implements PaymentService {
    public void pay(BigDecimal amount) {
    }
}
@Service
@RequiredArgsConstructor
public class CheckoutService {

    private final PaymentService paymentService;
    // 注入 DefaultPaymentService
}

规则:

@Qualifier 优先级高于 @Primary

环境与条件装配

@Profile

仅在特定环境 Profile 激活时注册 Bean。

@Service
@Profile("dev")
public class MockSmsService implements SmsService {

    public void send(String phone, String content) {
        System.out.println("Mock SMS: " + content);
    }
}
@Service
@Profile("prod")
public class RealSmsService implements SmsService {

    public void send(String phone, String content) {
        // 调用真实短信平台
    }
}

激活 Profile:

spring:
  profiles:
    active: dev

或通过环境变量:

SPRING_PROFILES_ACTIVE=prod

@Conditional

按条件决定是否注册 Bean。它是底层通用注解,实际开发更常见 Spring Boot 提供的派生注解。

@Configuration
public class CacheConfig {

    @Bean
    @ConditionalOnProperty(
        prefix = "app.cache",
        name = "enabled",
        havingValue = "true"
    )
    public CacheService cacheService() {
        return new RedisCacheService();
    }
}

配置:

app:
  cache:
    enabled: true

常用条件注解:

@ConditionalOnProperty        // 配置项满足条件
@ConditionalOnClass           // classpath 存在某个类
@ConditionalOnMissingBean     // 容器中尚不存在某 Bean
@ConditionalOnBean            // 容器中存在某 Bean
@ConditionalOnWebApplication  // 当前是 Web 应用

典型用途:自动配置、可选能力、开发环境 Mock 实现、组件开关。


AOP 切面

@Aspect

声明切面,用于把日志、监控、鉴权、审计、统一异常处理等横切逻辑从业务代码中抽离。

@Aspect
@Component
@Slf4j
public class MethodLogAspect {

    @Around("execution(* com.example.service..*(..))")
    public Object logMethod(
            ProceedingJoinPoint joinPoint
    ) throws Throwable {
        long startTime = System.currentTimeMillis();

        try {
            return joinPoint.proceed();
        } finally {
            long duration = System.currentTimeMillis() - startTime;
            log.info(
                "method={}, duration={}ms",
                joinPoint.getSignature(),
                duration
            );
        }
    }
}

常用通知类型:

@Before   // 方法执行前
@After    // 方法结束后,无论成功失败
@AfterReturning // 正常返回后
@AfterThrowing  // 抛异常后
@Around   // 完全包裹方法调用,最灵活

常见切点表达式:

execution(* com.example.service..*(..))

含义:

匹配 com.example.service 及其子包下,
所有类的所有方法,任意参数。

注意:@Transactional@Async 本质上也主要依赖 AOP 代理机制。因此要警惕同类内部调用绕过代理的问题。