本篇目标:把全书正文里散落的注解收拢成按用途分类的速查表,让你在写代码时能一眼查到「这个注解属于哪一层、在哪个包、对应哪一节」,并附一张 3.x 到 4.x 的变更对照表供迁移时核对。
适用版本:Spring Boot 4.1.x(Java 21)
附录 A 常用注解速查
正文 18 章把「为什么这样设计」讲透了,这一页只做一件事:把写代码时真正会去翻的注解压成表。建议第一次通读建立索引感,之后把它当案头卡片——写的时候不必回忆章节号,先在这里对号入座,再回正文看完整推导。
表里的「所在包」一列均按 4.x 口径给出。 4.0 起 Spring Boot 做了模块化重构,少数注解的包位置发生了变化(例如 @EntityScan 与 @PropertyMapping),这类差异集中列在本页末尾的「3.x → 4.x 注解与包名变更对照表」里,正文其余部分不再重复。
怎么用这张表
这张表按「注解在应用里扮演什么角色」分类,而不是按字母顺序。使用它的方式有三种:
- 写代码时查包名:不记得某个注解该
import哪个包,先在对应分类里找到它,再复制包名。 - 读代码时反查:看到一个陌生注解,先在表里确认它属于哪一层,再翻到「详见章节」看它的完整行为。
- 迁移时对照:从 3.x 项目迁到 4.x,先看末尾的变更对照表,把已移除或改名的注解一次性替换掉。
需要提醒的是,速查表只能帮你「定位」,不能帮你「理解」。一个注解为什么存在、什么时候该用、什么时候不该用,仍然要看正文。尤其是 @Transactional、@ConditionalOnMissingBean、@Validated 这几个,写法只有一行,但行为受很多条件影响,值得回到对应章节读透。
① 启动与配置类
这一组注解决定「应用从哪启动、哪些类算配置、自动配置从哪来」。
| 注解 | 作用 | 所在包 | 详见章节 |
|---|---|---|---|
@SpringBootApplication | 组合注解:@SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan | org.springframework.boot.autoconfigure | 4.1 |
@Configuration | 声明一个配置类,其中的 @Bean 方法会被容器处理 | org.springframework.context.annotation | 5.1 |
@AutoConfiguration | 声明一个自动配置类,4.x 编写自定义自动配置的推荐写法 | org.springframework.boot.autoconfigure | 4.2、7.1 |
@ComponentScan | 指定组件扫描的包与过滤规则 | org.springframework.context.annotation | 4.1 |
@Import | 导入普通配置类、ImportSelector 或 ImportBeanDefinitionRegistrar | org.springframework.context.annotation | 5.1 |
@EnableConfigurationProperties | 让指定的 @ConfigurationProperties 类生效并被注册为 Bean | org.springframework.boot.context.properties | 6.2 |
@ConfigurationPropertiesScan | 扫描并注册指定包下的全部 @ConfigurationProperties 类 | org.springframework.boot.context.properties | 6.2 |
@SpringBootApplication 是入口注解,但它本身几乎不做事,真正的启动逻辑由它引入的 @EnableAutoConfiguration 触发——这一层在 4.2 节会拆开讲。写自定义自动配置时,优先用 @AutoConfiguration 而不是裸的 @Configuration,因为前者额外携带了「什么时候该加载」的元数据约定。
② 组件与依赖注入
这一组注解决定「哪些对象交给容器、它们之间怎么互相拿到」。
| 注解 | 作用 | 所在包 | 详见章节 |
|---|---|---|---|
@Component | 通用组件,交给容器管理 | org.springframework.stereotype | 5.1 |
@Service | 语义化组件,标注服务层 | org.springframework.stereotype | 5.1 |
@Repository | 语义化组件,标注数据访问层,并触发持久化异常转换 | org.springframework.stereotype | 5.1、12.2 |
@Controller | 语义化组件,标注 MVC 控制器 | org.springframework.stereotype | 8.1 |
@RestController | @Controller 加 @ResponseBody,返回值直接作为响应体 | org.springframework.web.bind.annotation | 8.1 |
@Bean | 在配置类中声明一个由方法返回的 Bean | org.springframework.context.annotation | 5.1 |
@Autowired | 按类型注入依赖,可用在构造器、字段或方法上 | org.springframework.beans.factory.annotation | 5.2 |
@Qualifier | 在多个同类型 Bean 中按名字限定 | org.springframework.beans.factory.annotation | 5.2 |
@Primary | 多个候选时优先选择该 Bean | org.springframework.context.annotation | 5.2 |
@Value | 注入单个配置值(${...} 或 SpEL #{...}) | org.springframework.beans.factory.annotation | 6.2 |
@Scope | 指定 Bean 作用域(singleton / prototype 等) | org.springframework.context.annotation | 5.2 |
@Lazy | 延迟初始化,直到第一次被使用才创建 | org.springframework.context.annotation | 5.2 |
一个常见困惑是「@Component、@Service、@Repository 到底有什么区别」。答案是:在容器眼里它们几乎等价,差别只在语义与少数副作用——@Repository 会额外把底层异常翻译成 Spring 的数据访问异常体系。选哪个,看这个类在架构里扮演什么角色,而不是看功能。
③ 生命周期
| 注解 | 作用 | 所在包 | 详见章节 |
|---|---|---|---|
@PostConstruct | 依赖注入完成后执行一次,用于初始化 | jakarta.annotation | 5.3 |
@PreDestroy | 容器销毁 Bean 之前执行一次,用于释放资源 | jakarta.annotation | 5.3 |
这两个注解来自 Jakarta 规范而非 Spring 自身,因此包里是 jakarta.annotation。它们的执行时机与构造器不同:构造器执行时依赖还没注入完,@PostConstruct 执行时依赖已经就绪。要真正搞清它们的顺序,需要理解 Bean 的生命周期回调,见 5.3 节。
④ 条件装配
这一组是自动配置的核心,决定「一个配置类到底要不要生效」。
| 注解 | 作用 | 所在包 | 详见章节 |
|---|---|---|---|
@ConditionalOnClass | classpath 上存在指定类时生效 | org.springframework.boot.autoconfigure.condition | 4.3、7.1 |
@ConditionalOnMissingBean | 容器中不存在指定 Bean 时生效 | org.springframework.boot.autoconfigure.condition | 4.3、7.1 |
@ConditionalOnProperty | 指定配置属性满足条件时生效 | org.springframework.boot.autoconfigure.condition | 4.3 |
@ConditionalOnWebApplication | 是 Web 应用时生效,可限定 servlet 或 reactive | org.springframework.boot.autoconfigure.condition | 4.3 |
@ConditionalOnBean | 容器中存在指定 Bean 时生效 | org.springframework.boot.autoconfigure.condition | 4.3 |
其中 @ConditionalOnMissingBean 是「可被用户覆盖的默认值」这一设计的关键:框架先声明一个默认实现,但只要你自己的配置里提供了同类型 Bean,默认实现就让位。理解这一点,就能明白为什么「我写了一个 ObjectMapper,为什么自动配置的没生效」——答案往往就在这里。
⑤ Web
| 注解 | 作用 | 所在包 | 详见章节 |
|---|---|---|---|
@RequestMapping | 映射请求路径与方法,类级与方法级均可用 | org.springframework.web.bind.annotation | 8.1 |
@GetMapping / @PostMapping / @PutMapping / @DeleteMapping / @PatchMapping | @RequestMapping 的 HTTP 方法快捷注解 | org.springframework.web.bind.annotation | 8.1 |
@PathVariable | 绑定 URI 模板变量 | org.springframework.web.bind.annotation | 8.2 |
@RequestParam | 绑定查询参数或表单字段 | org.springframework.web.bind.annotation | 8.2 |
@RequestBody | 把请求体反序列化为对象 | org.springframework.web.bind.annotation | 8.2 |
@RequestHeader | 绑定请求头 | org.springframework.web.bind.annotation | 8.2 |
@ResponseStatus | 指定方法或异常处理返回的 HTTP 状态码 | org.springframework.web.bind.annotation | 10.1 |
@ControllerAdvice | 全局增强所有控制器,处理异常、数据绑定与模型 | org.springframework.web.bind.annotation | 10.2 |
@RestControllerAdvice | @ControllerAdvice 加 @ResponseBody,直接返回 JSON | org.springframework.web.bind.annotation | 10.2 |
@ExceptionHandler | 在增强类或控制器内处理指定异常 | org.springframework.web.bind.annotation | 10.1 |
@CrossOrigin | 允许跨域请求 | org.springframework.web.bind.annotation | 11.2 |
这组注解有一个共同点:它们大多只是「声明」,真正让它们生效的是 DispatcherServlet 启动时建立的那张映射表。所以当某个注解「没起作用」时,排查思路通常是「这个处理器有没有被注册到映射表里」,而不是「注解拼错了没有」。
⑥ 校验
| 注解 | 作用 | 所在包 | 详见章节 |
|---|---|---|---|
@Valid | 触发对参数或字段的级联校验(Jakarta Bean Validation) | jakarta.validation | 9.1 |
@Validated | Spring 变体,支持分组校验,可用于类级 | org.springframework.validation.annotation | 9.1、9.3 |
@NotNull | 不能为 null | jakarta.validation.constraints | 9.1 |
@NotBlank | 字符串不能为 null 且去除空白后非空 | jakarta.validation.constraints | 9.1 |
@Size | 集合或字符串长度在指定范围内 | jakarta.validation.constraints | 9.1 |
@Pattern | 字符串必须匹配指定正则 | jakarta.validation.constraints | 9.1 |
@Email | 必须是合法邮箱格式 | jakarta.validation.constraints | 9.1 |
@Min / @Max | 数值下界 / 上界 | jakarta.validation.constraints | 9.1 |
@Valid 与 @Validated 的区别值得记住:前者是标准注解,负责「触发校验」;后者是 Spring 的扩展,额外支持「分组」与「方法级校验」。需要按场景分组校验时用 @Validated,见 9.3 节。
⑦ 数据与事务
| 注解 | 作用 | 所在包 | 详见章节 |
|---|---|---|---|
@Entity | 声明一个 JPA 实体 | jakarta.persistence | 12.2 |
@Table | 指定映射的表名与约束 | jakarta.persistence | 12.2 |
@Id | 声明主键 | jakarta.persistence | 12.2 |
@GeneratedValue | 指定主键生成策略 | jakarta.persistence | 12.2 |
@Column | 指定列名、可空性、长度等映射细节 | jakarta.persistence | 12.2 |
@OneToMany | 一对多关联 | jakarta.persistence | 13.1 |
@ManyToOne | 多对一关联 | jakarta.persistence | 13.1 |
@JoinColumn | 指定关联使用的外键列 | jakarta.persistence | 13.1 |
@Query | 声明 JPQL 或原生 SQL 查询 | org.springframework.data.jpa.repository | 13.2 |
@Modifying | 标记 @Query 为更新或删除语句 | org.springframework.data.jpa.repository | 13.2 |
@Transactional | 声明事务边界与传播行为 | org.springframework.transaction.annotation | 14.1 |
JPA 注解来自 jakarta.persistence,与 Spring 无关;@Query 与 @Modifying 是 Spring Data JPA 的扩展;@Transactional 则来自 Spring 的事务模块。三者分属不同层次,混在一起用时要清楚每个注解「归谁管」——尤其 @Transactional 是基于代理生效的,同类内部调用不会触发它,这是 14.3 节要讲的经典坑。
⑧ 测试
| 注解 | 作用 | 所在包 | 详见章节 |
|---|---|---|---|
@SpringBootTest | 加载完整应用上下文 | org.springframework.boot.test.context | 17.3 |
@WebMvcTest | 只加载 MVC 切片 | org.springframework.boot.test.autoconfigure.web.servlet | 17.2 |
@DataJpaTest | 只加载 JPA 与数据源切片 | org.springframework.boot.test.autoconfigure.orm.jpa | 17.2 |
@AutoConfigureMockMvc | 为测试装配 MockMvc,4.x 需显式添加 | org.springframework.boot.test.autoconfigure.web.servlet | 17.2 |
@MockitoBean | 用 Mockito 替身替换容器中的 Bean | org.springframework.test.context.bean.override.mockito | 17.1、17.2 |
@AutoConfigureTestRestTemplate | 为测试装配 TestRestTemplate,4.x 需显式添加 | org.springframework.boot.test.autoconfigure.web.client | 17.3 |
这里最容易踩坑的是 4.x 的一条行为变更:@SpringBootTest 不再自动附带 MockMvc 与 TestRestTemplate,需要 @AutoConfigureMockMvc、@AutoConfigureTestRestTemplate 显式声明。如果你从 3.x 迁移测试代码,测试类会因为找不到这些组件而报错,补上对应的 @AutoConfigure* 注解即可。
3.x → 4.x 注解与包名变更对照表
从 3.x 迁移到 4.x 时,下面这些注解要么被移除,要么改了名,要么挪了包。先按这张表把代码过一遍,再启动项目,能省下大量「编译不过却看不出原因」的时间。
| 3.x 写法 | 4.x 写法 | 说明 |
|---|---|---|
@MockBean | @MockitoBean | @MockBean 已在 4.x 移除,替换为 @MockitoBean |
@SpyBean | @MockitoSpyBean | @SpyBean 已在 4.x 移除,替换为 @MockitoSpyBean |
@JsonComponent | @JacksonComponent | 随 Jackson 3 改名 |
@JsonMixin | @JacksonMixin | 随 Jackson 3 改名 |
org.springframework.boot.autoconfigure.domain.EntityScan | org.springframework.boot.persistence.autoconfigure.EntityScan | @EntityScan 包位置变化 |
org.springframework.boot.test.autoconfigure.properties.PropertyMapping | org.springframework.boot.test.context.PropertyMapping | @PropertyMapping 包位置变化 |
org.springframework.lang.Nullable | org.jspecify.annotations.Nullable | 空安全注解迁移到 JSpecify |
几条配套说明:
@MockitoBean/@MockitoSpyBean有一条硬约束:它们只能用在测试类上,不能用在@Configuration类里。3.x 时代有人把@MockBean塞进配置类做「测试专用装配」,4.x 起这条路径被明确堵死。@MockitoBean的包路径也变了,从 Spring 自己的包迁到了org.springframework.test.context.bean.override.mockito,迁移时留意 import。@SpringBootTest不再自带MockMvc与TestRestTemplate:这虽然不算注解改名,但同样是「按旧写法照抄会失效」的典型,故一并提醒。- 空安全注解的迁移影响面很广:
org.springframework.lang.Nullable被org.jspecify.annotations.Nullable取代后,凡是显式 import 旧包的位置都要替换。如果只是用注解而不 import,通常不受影响。
下面是一段符合 4.x 口径的最小组合示例,把上面几类注解放在同一个文件里,供你对照包名:
package com.example.book;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/books")
public class BookController {
private final BookService service;
public BookController(BookService service) {
this.service = service;
}
@GetMapping("/{id}")
public Book find(@PathVariable Long id) {
return service.findById(id);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Book create(@Valid @RequestBody BookForm form) {
return service.create(form);
}
public record BookForm(@NotBlank String title) {
}
}
这段代码没有写 import 之外的任何 Spring 配置,但它已经覆盖了「控制器、路由、路径变量、请求体、校验、状态码、依赖注入」七件事——这正是 Spring Boot「约定优于配置」的直观体现。
注解的层次关系
Spring Boot 的注解可以粗略分成三层,理解层次有助于判断一个注解「归谁管」:
- Spring 核心层(
org.springframework.context/org.springframework.beans):@Configuration、@Bean、@Autowired、@Component系列。它们管的是「对象如何被创建和装配」。 - Spring Boot 层(
org.springframework.boot):@SpringBootApplication、@AutoConfiguration、@ConditionalOn*、@ConfigurationProperties。它们管的是「在什么条件下自动装配什么」。 - 规范层(
jakarta.*):@PostConstruct、@Valid、@Entity等。它们由 Jakarta 规范定义,Spring 只是支持它们。
分清这三层,你就能在遇到问题时快速判断「该去查 Spring 的文档,还是查 Jakarta 的文档」,也能理解为什么有些注解换一个框架依然可用。
几个容易混淆的注解
| 容易混淆的一对 | 区别 |
|---|---|
@Component 与 @Bean | 前者标在类上,靠组件扫描发现;后者标在方法上,由配置类显式声明 |
@Controller 与 @RestController | 前者返回值默认当作视图名;后者当作响应体直接写出 |
@Valid 与 @Validated | 前者是标准触发注解;后者是 Spring 扩展,支持分组校验 |
@RequestParam 与 @PathVariable | 前者取查询串中的参数;后者取路径模板中的占位符 |
@Configuration 与 @AutoConfiguration | 后者是 4.x 编写自动配置的推荐注解,额外携带加载元数据 |
@Transactional 与 @Modifying | 前者管事务边界;后者只标记某个查询是写操作 |
一个配置类示例
注解速查之外,一个常见的困惑是「配置类该怎么写」。下面这段示例把启动与配置类那一组注解放在一起:
@AutoConfiguration
@EnableConfigurationProperties(BookProperties.class)
public class BookAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public BookFormatter bookFormatter(BookProperties properties) {
return new BookFormatter(properties.getPrefix());
}
}
配合下面的属性类:
@ConfigurationProperties(prefix = "app.book")
public class BookProperties {
private String prefix = "book";
// getter / setter 省略
}
@AutoConfiguration 让这个类被识别为自动配置;@EnableConfigurationProperties 让属性类生效;@ConditionalOnMissingBean 保证用户自定义的 BookFormatter 能覆盖这个默认值。这三者组合起来,就是一个最小可用的「可被覆盖的自动配置」。更完整的自定义 starter 流程见 7.2 节。
按场景查注解
比起按分类逐个回忆,从「我要做的事」出发反查往往更快:
| 我想做的事 | 会用到的注解 |
|---|---|
| 暴露一个 GET 接口 | @RestController + @GetMapping + @PathVariable / @RequestParam |
| 接收 JSON 请求体 | @PostMapping + @RequestBody |
| 校验入参 | @Valid(或 @Validated)+ jakarta.validation.constraints.* |
| 统一返回错误格式 | @RestControllerAdvice + @ExceptionHandler |
| 允许前端跨域 | @CrossOrigin,或全局 CORS 配置 |
| 映射一张表 | @Entity + @Table + @Id + @GeneratedValue |
| 写一个自定义查询 | @Query,更新语句再加 @Modifying |
| 保证一组操作原子 | @Transactional |
| 绑定一组配置 | @ConfigurationProperties + @EnableConfigurationProperties |
| 提供可被覆盖的默认 Bean | @Bean + @ConditionalOnMissingBean |
| 写一个只测 Controller 的测试 | @WebMvcTest + @AutoConfigureMockMvc + @MockitoBean |
这张表里的每一项,都能在正文里找到对应的完整示例与解释。速查表的作用是让你「想起还有这么一个注解」,至于它怎么用、什么时候会失效,仍要回到正文。
小结
- 速查表的价值在于「定位」而非「理解」:先用表找到注解属于哪一层、在哪个包,再回正文看它的完整行为。
- 八类里最需要理解而非记忆的是条件装配:
@ConditionalOnMissingBean决定了「默认值可被覆盖」这一核心设计。 - 校验里分清
@Valid(触发)与@Validated(分组),Web 里分清@Controller(视图)与@RestController(响应体)。 - 数据层要记住注解分属三套体系:
jakarta.persistence管映射,Spring Data 管查询,Spring 管事务。 - 迁移到 4.x 时,先按末尾的变更对照表把
@MockBean/@SpyBean等替换掉,再启动项目——@MockBean与@SpyBean在 4.x 已被移除。 - 需要查配置项而不是注解时,请看附录 B 的配置速查;需要查构建脚本时,看附录 C 的 Maven 与 Gradle 对照。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。