在 Spring 框架中,依赖注入(Dependency Injection, DI)是核心特性之一,它通过注解化配置简化了组件间的依赖管理,避免了手动创建对象和组装依赖的繁琐操作。其中,@Autowired、@Value、@Resource 是日常开发中最常用的三个依赖注入注解,本文将从基本使用、核心实现步骤、底层原理三个维度,深入剖析这三个注解的工作机制。
一、基本使用:注解功能与场景区分
在了解底层实现前,先明确三个注解的核心用途和使用差异,这是理解实则现逻辑的基础:
|
注解 |
核心功能 |
注入依据 |
适用场景 |
关键特性 |
|
@Autowired |
自动装配 Spring 容器中的 Bean |
类型(Type)优先,支持按名称(需配合 @Qualifier) |
注入自定义组件(如 Service、Dao) |
支持构造器、字段、setter 注入;默认必须存在(required=true) |
|
@Value |
注入配置属性、字面量或 SpEL 表达式结果 |
配置键名 / 表达式 |
注入配置参数(如 application.yml 中的值) |
支持 ${} 配置占位符、#{} SpEL 表达式;可指定默认值 |
|
@Resource |
自动装配 Bean(JDK 原生注解,非 Spring 定义) |
名称(Name)优先,其次类型 |
注入自定义组件或 JDK 原生组件 |
支持字段、setter 注入;可通过 name 属性指定 Bean 名称 |
典型使用示例
- @Autowired 注入:
@Service
public class UserService {
// 字段注入:按类型匹配 UserDao,若存在多个同类型 Bean 需配合 @Qualifier 指定名称
@Autowired
@Qualifier("userDaoImpl")
private UserDao userDao;
// 构造器注入(Spring 4.3+ 无需显式 @Autowired)
private OrderService orderService;
@Autowired
public UserService(OrderService orderService) {
this.orderService = orderService;
}
}
- @Value 注入:
@Component
public class AppConfig {
// 注入配置文件中的属性,默认值为 "dev"
@Value("${spring.profiles.active:dev}")
private String activeProfile;
// 注入 SpEL 表达式结果(计算 10+20)
@Value("#{10 + 20}")
private Integer calculateResult;
// 注入字面量
@Value("Hello Spring")
private String greeting;
}
- @Resource 注入:
@Component
public class PaymentService {
// 按名称 "alipayClient" 注入,若不存在则按类型匹配
@Resource(name = "alipayClient")
private PaymentClient paymentClient;
}
二、核心实现步骤:依赖注入的通用逻辑
无论哪个注入注解,Spring 实现依赖注入的核心思路一致,本质是拦截 Bean 创建过程,识别注解标记的依赖,从容器中获取对应资源并注入,具体分为两步:
1. 拦截 Bean 的创建:生命周期扩展点介入
Spring 容器创建 Bean 时,会遵循固定的生命周期流程(如实例化、属性填充、初始化、销毁)。依赖注入的关键是在属性填充阶段(即 Bean 实例化后、初始化前)介入,而介入的核心是
InstantiationAwareBeanPostProcessor 接口 —— 这是 Spring 提供的 Bean 生命周期扩展接口,专门用于在 Bean 实例化后、属性设置前执行自定义逻辑,是依赖注入的 “入口”。
InstantiationAwareBeanPostProcessor 继承自 BeanPostProcessor,但新增了与 “实例化后、属性填充前” 相关的方法,其中 postProcessProperties() 是依赖注入的核心方法,负责处理字段或方法上的注入注解。
2. 识别注解与注入值:元数据解析 + 资源查找
Spring 会通过以下子步骤完成注入:
- 扫描注解:对目标 Bean 的类结构进行解析,查找标记了 @Autowired、@Value、@Resource 的字段或方法,收集这些需要注入的 “元数据”(如字段类型、注解属性、目标名称等)。
- 封装元数据:将收集到的注入信息封装为 InjectionMetadata 对象,每个具体的注入点(如一个字段)会被封装为对应的元素对象(如 AutowiredFieldElement、ResourceElement),便于统一处理。
- 查找资源:根据注入元数据的规则,从 Spring 容器(BeanFactory)中查找对应的资源 —— 可能是 Bean 实例(@Autowired、@Resource)、配置属性(@Value)或 SpEL 表达式结果。
- 执行注入:通过反射机制,将找到的资源赋值给目标 Bean 的字段或通过方法参数传入,完成依赖注入。
三、@Autowired 与 @Value 的实现原理
@Autowired 和 @Value 由同一个核心处理器
AutowiredAnnotationBeanPostProcessor 负责处理,二者共享 “拦截 Bean 创建” 的流程,但在 “资源查找” 阶段有不同逻辑。
1. 拦截 Bean 创建:AutowiredAnnotationBeanPostProcessor 的注册
@Autowired 和 @Value 能生效的前提是,Spring 容器中存在
AutowiredAnnotationBeanPostProcessor 实例 —— 它是
InstantiationAwareBeanPostProcessor 接口的实现类,专门处理 @Autowired、@Value 以及 JSR-330 规范的 @Inject 注解。
注册流程
Spring Boot 启动时,会自动完成
AutowiredAnnotationBeanPostProcessor 的注册,无需手动配置,具体流程如下:

关键说明:
- AnnotationConfigServletWebServerApplicationContext 是 Spring Boot Web 应用的默认上下文,专门支持注解驱动的配置。
- AnnotatedBeanDefinitionReader 负责读取注解式的 Bean 定义(如 @Component、@Configuration),其构造时会调用 AnnotationConfigUtils 注册一系列 “注解处理处理器”,AutowiredAnnotationBeanPostProcessor 就是其中之一。
2. 注解识别与注入:核心方法与流程
AutowiredAnnotationBeanPostProcessor 通过两个核心方法完成注入:
postProcessMergedBeanDefinition()(解析注入元数据)和 postProcessProperties()(执行注入),具体流程如下:

关键细节解析
- 元数据缓存:postProcessMergedBeanDefinition() 会在 Bean 实例化后立即解析注入注解,并将元数据缓存到 BeanDefinition 中。这样做是为了避免每次 Bean 初始化都重复解析类结构,提升性能。
- InjectionMetadata 与 AutowiredFieldElement:
- InjectionMetadata 是注入元数据的容器,包含一个 Bean 所有需要注入的 “注入点元素”(字段或方法)。
- AutowiredFieldElement 是字段注入的具体实现类,封装了字段的反射信息(Field 对象)和注入规则。
- 3. @Value 的特殊处理:
- resolveEmbeddedValue() 是 Spring 提供的字符串解析方法,专门处理 ${} 配置占位符(如读取 application.yml 中的属性)和 #{} SpEL 表达式(如计算表达式、调用 Bean 方法)。
- 解析后的数据会通过类型转换器(ConversionService)转换为目标字段的类型(如将字符串 “8080” 转换为 Integer)。
- 4. @Autowired 的自动装配规则:
- 优先按 “类型” 查找容器中的 Bean,若存在唯一匹配则直接注入。
- 若存在多个同类型 Bean,会按 “字段名称” 或 @Qualifier 注解指定的名称筛选。
- 若未找到匹配的 Bean,且 @Autowired(required=true)(默认),则抛出 NoSuchBeanDefinitionException;若 required=false,则注入 null。
四、@Resource 的实现原理
@Resource 是 JDK 原生注解(位于 javax.annotation 包),并非 Spring 定义,但 Spring 对其提供了完美支持。实则现逻辑与 @Autowired 类似,但核心处理器和注入规则不同。
1. 拦截 Bean 创建:CommonAnnotationBeanPostProcessor 的注册
@Resource 由
CommonAnnotationBeanPostProcessor 负责处理,该处理器同样实现了
InstantiationAwareBeanPostProcessor 接口,同时还处理 @PostConstruct(初始化回调)、@PreDestroy(销毁回调)等 JDK 原生注解。
注册流程(与 @Autowired 共享部分逻辑)

关键说明:
- CommonAnnotationBeanPostProcessor 与 AutowiredAnnotationBeanPostProcessor 由同一个 AnnotationConfigUtils 工具类注册,是 Spring 注解驱动的核心处理器组合。
- 若项目中未引入 JDK 的 javax.annotation 相关依赖(如 JDK 9+ 需手动引入 javax.annotation-api),@Resource 注解将无法被识别。
2. 注解识别与注入:核心方法与流程
CommonAnnotationBeanPostProcessor 的注入流程与
AutowiredAnnotationBeanPostProcessor 类似,但核心差异在于 “注入点封装” 和 “资源查找规则”:

关键细节解析
- ResourceElement 封装:@Resource 对应的注入点元素是 ResourceElement(继承自 InjectionElement),它封装了 @Resource 注解的属性(如 name、type)和字段 / 方法的反射信息。
- 注入规则:名称优先:
- @Resource 的核心注入规则是 “按名称匹配”:第一根据 @Resource(name=”xxx”) 指定的名称查找 Bean,若未指定 name,则默认使用 “字段名称” 或 “方法参数名称” 作为查找键。
- 若按名称未找到 Bean,会降级为 “按类型匹配”,此时逻辑与 @Autowired 类似,但不支持 @Qualifier 注解。
- 3. 与 @Autowired 的核心差异:
- 注解来源:@Resource 是 JDK 原生注解,@Autowired 是 Spring 注解。
- 匹配优先级:@Resource 名称优先,@Autowired 类型优先。
- 支持的注入方式:@Resource 不支持构造器注入,@Autowired 支持。
- 依赖查找范围:@Resource 可通过 lookup 属性指定查找范围,@Autowired 依赖 Spring 容器的 Bean 查找机制。
五、总结:三大注解实现逻辑对比
通过以上分析,可将三大注解的实现逻辑归纳为 “同一框架,不同处理器,不同规则”:
|
维度 |
@Autowired + @Value |
@Resource |
|
核心处理器 |
AutowiredAnnotationBeanPostProcessor |
CommonAnnotationBeanPostProcessor |
|
依赖查找规则 |
@Autowired:类型优先,支持 @Qualifier;@Value:配置 / SpEL |
名称优先,降级类型匹配,不支持 @Qualifier |
|
注入点封装元素 |
AutowiredFieldElement(字段)、AutowiredMethodElement(方法) |
ResourceElement(字段 / 方法) |
|
核心依赖查找方法 |
DefaultListableBeanFactory.resolveDependency() |
DefaultListableBeanFactory.resolveBeanByName() |
|
特殊功能 |
@Value 支持配置占位符、SpEL |
支持 JDK 原生规范,兼容非 Spring 环境 |
核心底层共性
- 都依赖 InstantiationAwareBeanPostProcessor 接口拦截 Bean 生命周期,在属性填充阶段执行注入。
- 都通过 “元数据解析 + 缓存” 提升性能,避免重复解析类结构。
- 都通过反射机制完成最终的属性赋值,依赖 Spring 的 BeanFactory 查找资源。
理解这些底层原理,不仅能协助我们更灵活地使用三大注解(如解决注入冲突、自定义注入规则),还能深入理解 Spring 容器的工作机制,为排查依赖注入相关问题(如
NoSuchBeanDefinitionException、
NoUniqueBeanDefinitionException)提供理论支撑。
收藏了,感谢分享