Spring / Spring Boot 常用注解

核心原则(先看这段)

  • 把行为(业务)放在 Service 层,事务/缓存/异步等注解一般放在 Service 层,不要把复杂逻辑放到 Controller。

  • 首选构造器注入(constructor injection),比字段注入更利于测试与不可变性。

  • 注解很多都是"语义化的标记 + 框架在运行时的处理",理解背后的代理/生命周期很重要(例如 @Transactional 基于 AOP 代理,自调用不会触发代理逻辑)。

核心/启动与配置注解

复制代码
@SpringBootApplication // 等价于 @Configuration + @EnableAutoConfiguration + @ComponentScan(默认扫描启动类包及子包)
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
  • @Configuration:Java 配置类(替代 XML)。

  • @Bean:方法级别把返回对象注册为 Spring Bean(可设置 init/destroy)。

  • @ComponentScan:定制扫描包路径(启动类包位置很关键,建议把主类放在根包)。

  • @EnableConfigurationProperties:启用 @ConfigurationProperties 绑定(在 Spring Boot 中,直接将 @ConfigurationProperties 标注成 @Component 也可被扫描到)。

  • @ConfigurationProperties(prefix="..."):批量强类型注入配置(推荐用于复杂/层级配置,支持校验 @Validated)。

示例(YAML + 配置类):

复制代码
# application.yml
app:
  name: demo
  timeout: 30
  nested:
    enabled: true

@Component
@ConfigurationProperties(prefix = "app")
@Validated
public class AppProperties {
    @NotBlank
    private String name;
    private int timeout;
    private Nested nested = new Nested();
    // getters/setters
    public static class Nested { private boolean enabled; /* getter/setter */ }
}

组件(stereotype)注解与区别

  • @Component:通用组件(任何层通用)。

  • @Service:业务层语义化(仅为语义,便于识别)。

  • @Repository:持久层语义化,且启用 异常翻译(把 JPA/Hibernate 的异常翻译为 Spring 的 DataAccessException)。

  • @Controller:MVC 控制器,通常返回视图(与 @ResponseBody 组合可返回 JSON)。

  • @RestController:@Controller + @ResponseBody,更常用于 REST API。

何时用?按层次用对应注解(可读性和工具/监控的好处)。

依赖注入 / Bean 选择 / Scope / 生命周期

  • 注入方式对比(推荐):

    • 构造器注入(推荐):支持 final,利于单元测试。

    • Setter 注入:有状态或可选依赖时可用。

    • 字段注入(@Autowired 在字段上):不推荐(难测、不可 final)。

      @Service
      public class MyService {
      private final UserRepository repo;
      public MyService(UserRepository repo) { this.repo = repo; } // 自动注入(Spring 4.3+)
      }

  • @Autowired(按类型注入),@Qualifier("name") 或 @Primary(用于同类型多个 Bean);

  • @Resource(JSR-250,按名称优先);

  • @Value("${xxx}")(注入单个配置值);

  • @ConfigurationProperties(批量注入复杂配置);

Bean scope:

  • @Scope("singleton")(默认)

  • @Scope("prototype")(每次请求新实例)

  • 还有 request、session(web 场景)等

生命周期:

  • @PostConstruct、@PreDestroy(JSR)

  • 或在 @Bean(initMethod="...", destroyMethod="...") 指定

  • InitializingBean / DisposableBean(接口)

    示例:

    @PostConstruct
    public void init(){ /* init work / }
    @PreDestroy
    public void cleanup(){ /
    release */ }

Web / Controller 相关注解(常用)

  • 路由映射:@RequestMapping、@GetMapping、@PostMapping、@PutMapping、@DeleteMapping。

  • 参数绑定:@PathVariable、@RequestParam、@RequestBody、@RequestHeader、@CookieValue。

  • 返回:@ResponseBody(将返回对象序列化为 JSON) → @RestController 更常用。

  • 状态码:@ResponseStatus(HttpStatus.CREATED)。

  • 验证:@Valid(与 Bean Validation 联动) + DTO 上加注解 @NotNull/@Size...。

  • 异常处理:@ControllerAdvice + @ExceptionHandler(全局异常/统一返回)或 @RestControllerAdvice。

示例:

复制代码
@RestController
@RequestMapping("/users")
public class UserController {
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public UserDto create(@Valid @RequestBody CreateUserDto dto) { ... }
}

数据访问与事务(重要)

  • @Repository(DAO层) + JPA @Entity / MyBatis 接口等。

  • @Transactional(关键点):

    • 放在 Service 层方法上最合适(控制事务边界)。

    • 常用属性:propagation、isolation、readOnly、rollbackFor。

    • 注意:@Transactional 基于代理(AOP),内部自调用不会触发事务(self-invocation)

      @Service
      public class OrderService {
      @Transactional(rollbackFor = Exception.class)
      public void placeOrder(OrderDto dto){ ... }
      }

常见坑:

  • 在同一个类里面 public methodA() 调 private methodB() 并给 methodB 标注 @Transactional,事务不会生效(因为代理不会拦截内部调用)。解决:把方法拆到另一个 Bean,或使用 AopContext.currentProxy() (不常用)。

AOP(切面)相关注解

  • @Aspect(切面类) + @Before / @After / @AfterReturning / @Around / @Pointcut。

  • 典型用途:日志、监控、权限校验、限流、重试等。

    @Aspect
    @Component
    public class LoggingAspect {
    @Pointcut("execution(* com.example.service..*(..))")
    public void serviceMethods() {}

    复制代码
      @Around("serviceMethods()")
      public Object log(ProceedingJoinPoint pjp) throws Throwable {
          long t = System.currentTimeMillis();
          Object ret = pjp.proceed();
          System.out.println("took " + (System.currentTimeMillis() - t));
          return ret;
      }

    }

异步 / 调度 / 缓存 / 安全(常见功能注解)

  • 异步:@EnableAsync(配置类) + @Async(方法) → 返回 void / Future / CompletableFuture。注意自调用失效

  • 调度:@EnableScheduling + @Scheduled(cron = "...")。

  • 缓存:@EnableCaching + @Cacheable / @CacheEvict / @CachePut。

  • 方法级安全:@PreAuthorize("hasRole('ADMIN')") / @Secured,配合 @EnableMethodSecurity(或旧版 @EnableGlobalMethodSecurity)。

示例(异步):

复制代码
@Configuration
@EnableAsync
public class AsyncConfig {}
@Service
public class MailService {
    @Async
    public CompletableFuture<Void> sendAsync(...) { ... }
}

条件化 & 自动配置注解(Spring Boot 自动化)

  • @ConditionalOnProperty(name="...", havingValue="..."):基于配置决定是否装配某 Bean。

  • @ConditionalOnClass:当类在 classpath 上时才生效(常用在 starter/自动配置中)。

  • @ConditionalOnMissingBean:容器中没有指定类型的 Bean 时才创建默认 bean。

  • @Profile("dev"):按 profile 加载(spring.profiles.active=dev)。

示例(有默认实现但允许覆盖):

复制代码
@Configuration
@ConditionalOnClass(SomeClient.class)
public class AutoConfig {
    @Bean
    @ConditionalOnMissingBean
    public MyClient myClient(){ return new DefaultMyClient(); }
}

测试相关注解(常见)

  • @SpringBootTest:集成测试,启动整个上下文(慢)。

  • @WebMvcTest(controllers = ...):只加载 Web 层(适合 Controller 测试)。

  • @DataJpaTest:只加载 JPA 相关组件(内存数据库)。

  • @MockBean:在 Spring 容器中替换某个 Bean 为 mock。

  • @TestConfiguration:测试专用配置类。

常见坑、注意事项与最佳实践(实战派)

  1. 优先构造器注入(可用 final,更安全)。

  2. @Transactional 放在 public 方法上且不要依赖自调用来触发事务。

  3. Controller 层只做参数校验/返回,不做事务处理和复杂业务(业务放 Service)。

  4. 使用 @ConfigurationProperties 管理复杂配置,@Value 仅用于单值注入。

  5. @Repository 会做异常翻译(推荐持久层使用)。

  6. @RestControllerAdvice + @ExceptionHandler 做统一异常处理和统一响应结构。

  7. 生产环境建议对定时任务、异步线程池、缓存策略做合理配置与监控。

  8. 用 @Profile 管理 dev/test/prod 配置差异。

  9. 注意 @Async / @Transactional/@Cacheable 等基于代理的注解,均受自调用影响。

速查小表(快速记忆)

  • 启动:@SpringBootApplication

  • 配置:@Configuration / @Bean / @ConfigurationProperties

  • 组件:@Component / @Service / @Repository / @Controller / @RestController

  • 注入:@Autowired / @Qualifier / @Value / 构造器注入

  • Web:@GetMapping / @PostMapping / @RequestBody / @PathVariable

  • 事务:@Transactional(放 Service)

  • AOP:@Aspect / @Around / @Pointcut

  • 异步:@EnableAsync + @Async

  • 调度:@EnableScheduling + @Scheduled

  • 缓存:@EnableCaching + @Cacheable

  • 条件/自动配置:@ConditionalOnProperty / @ConditionalOnClass / @ConditionalOnMissingBean

  • 测试:@SpringBootTest / @WebMvcTest / @MockBean

代码示例:Controller → Service → Repository + 配置属性(可拷贝运行)

复制代码
// Application.java
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

// AppProperties.java
@Component
@ConfigurationProperties(prefix = "app")
@Validated
public class AppProperties {
    @NotBlank private String name;
    private int timeout;
    // getters/setters
}

// UserEntity.java
@Entity
@Table(name = "users")
public class User {
    @Id @GeneratedValue private Long id;
    private String username;
    // getters/setters
}

// UserRepository.java (Spring Data JPA example)
@Repository
public interface UserRepository extends JpaRepository<User, Long> {}

// UserService.java
@Service
public class UserService {
    private final UserRepository userRepository;
    public UserService(UserRepository userRepository) { this.userRepository = userRepository; }

    @Transactional
    public User createUser(String username) {
        User u = new User(); u.setUsername(username);
        return userRepository.save(u);
    }

    @Transactional(readOnly = true)
    public User getUser(Long id){ return userRepository.findById(id).orElse(null); }
}

// UserController.java
@RestController
@RequestMapping("/users")
public class UserController {
    private final UserService userService;
    private final AppProperties props;
    public UserController(UserService userService, AppProperties props){
        this.userService = userService; this.props = props;
    }

    @PostMapping
    public ResponseEntity<User> create(@RequestParam String name){
        User u = userService.createUser(name);
        return ResponseEntity.status(HttpStatus.CREATED).body(u);
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> get(@PathVariable Long id){
        return ResponseEntity.of(Optional.ofNullable(userService.getUser(id)));
    }
}
相关推荐
Chen-Edward5 分钟前
有了Spring为什么还有要Spring Boot?
java·spring boot·spring
magic3341656324 分钟前
Springboot整合MinIO文件服务(windows版本)
windows·spring boot·后端·minio·文件对象存储
开心-开心急了34 分钟前
Flask入门教程——李辉 第一、二章关键知识梳理(更新一次)
后端·python·flask
掘金码甲哥1 小时前
调试grpc的哼哈二将,你值得拥有
后端
陈小桔1 小时前
idea中重新加载所有maven项目失败,但maven compile成功
java·maven
小学鸡!1 小时前
Spring Boot实现日志链路追踪
java·spring boot·后端
xiaogg36781 小时前
阿里云k8s1.33部署yaml和dockerfile配置文件
java·linux·kubernetes
逆光的July1 小时前
Hikari连接池
java
微风粼粼2 小时前
eclipse 导入javaweb项目,以及配置教程(傻瓜式教学)
java·ide·eclipse
番茄Salad2 小时前
Spring Boot临时解决循环依赖注入问题
java·spring boot·spring cloud