在 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 的注册,无需手动配置,具体流程如下:
graph TD
A("SpringApplication.run() 启动")-->|初始化应用上下文| B("createApplicationContext()")
B-->|"默认创建注解驱动的 Web 上下文"| C("AnnotationConfigServletWebServerApplicationContext.class")
C -->|"构造方法中初始化 Bean 定义读取器"| D("AnnotatedBeanDefinitionReader.class")
D -->|构造方法调用注册工具类| E("AnnotationConfigUtils.registerAnnotationConfigProcessors()")
E-->|"向容器中注册处理器 Bean"|F(AutowiredAnnotationBeanPostProcessor)
F-->|"容器启动完成后,处理器就绪"|G("等待拦截 Bean 创建流程")
关键说明:
AnnotationConfigServletWebServerApplicationContext是 Spring Boot Web 应用的默认上下文,专门支持注解驱动的配置。AnnotatedBeanDefinitionReader负责读取注解式的 Bean 定义(如@Component、@Configuration),其构造时会调用AnnotationConfigUtils注册一系列 “注解处理处理器”,AutowiredAnnotationBeanPostProcessor就是其中之一。
2. 注解识别与注入:核心方法与流程
AutowiredAnnotationBeanPostProcessor 通过两个核心方法完成注入:postProcessMergedBeanDefinition()(解析注入元数据)和 postProcessProperties()(执行注入),具体流程如下:
graph TD
A("Spring 容器创建 Bean 流程")-->|"1. Bean 实例化完成(new 出对象)"| B("调用 AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition()")
B-->|"解析 Bean 的类结构,查找 @Autowired/@Value 注解"| C("findAutowiringMetadata(beanName, beanClass, null)")
C -->|"封装注入点信息(字段/方法 + 注解属性)"| D("InjectionMetadata.checkConfigMembers()")
D-->|"将注入元数据缓存到 BeanDefinition 中,避免重复解析"| E("元数据缓存完成")
A -->|"2. 进入属性填充阶段"| F("调用 AutowiredAnnotationBeanPostProcessor.postProcessProperties()")
F-->|从缓存中获取注入元数据| G("findAutowiringMetadata(beanName, beanClass, pvs)")
G-->|执行注入逻辑| H("InjectionMetadata.inject(bean, beanName, pvs)")
H-->|遍历所有注入点元素| I("AutowiredFieldElement.inject(bean, beanName, pvs)")
I-->|"委托 BeanFactory 解析依赖"| J("DefaultListableBeanFactory.resolveDependency()")
J-->|进一步处理依赖查找| K("DefaultListableBeanFactory.doResolveDependency()")
K-->|"获取注解指定的建议值(如 @Value 的表达式)"| L("getAutowireCandidateResolver().getSuggestedValue(descriptor)")
L-->|判断建议值类型| M{"是否为字符串(配置占位符/SpEL)"}
M-->|"是:处理 @Value 逻辑"| N("resolveEmbeddedValue() 解析 ${} 和 #{}")
N-->|"转换为目标类型(如 Integer)"| O("注入到字段/方法")
M-->|"否:处理 @Autowired 逻辑"| P("findAutowireCandidates() 按类型查找 Bean")
P-->|"存在多个时按名称匹配(配合 @Qualifier)"| Q("筛选出唯一匹配的 Bean")
Q-->|注入到字段/方法| O
关键细节解析
- 元数据缓存:
postProcessMergedBeanDefinition()会在 Bean 实例化后立即解析注入注解,并将元数据缓存到BeanDefinition中。这样做是为了避免每次 Bean 初始化都重复解析类结构,提升性能。 - InjectionMetadata 与 AutowiredFieldElement:
InjectionMetadata是注入元数据的容器,包含一个 Bean 所有需要注入的 “注入点元素”(字段或方法)。AutowiredFieldElement是字段注入的具体实现类,封装了字段的反射信息(Field对象)和注入规则。
- @Value 的特殊处理:
resolveEmbeddedValue()是 Spring 提供的字符串解析方法,专门处理${}配置占位符(如读取application.yml中的属性)和#{}SpEL 表达式(如计算表达式、调用 Bean 方法)。- 解析后的数据会通过类型转换器(
ConversionService)转换为目标字段的类型(如将字符串 “8080” 转换为Integer)。
- @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 共享部分逻辑)
graph TD
A("SpringApplication.run() 启动")-->|初始化应用上下文| B("createApplicationContext()")
B-->|创建注解驱动上下文| C("AnnotationConfigServletWebServerApplicationContext.class")
C -->|"构造方法初始化 Bean 定义读取器"| D("AnnotatedBeanDefinitionReader.class")
D -->|调用注册工具类| E("AnnotationConfigUtils.registerAnnotationConfigProcessors()")
E-->|"向容器中注册处理器 Bean"|F(CommonAnnotationBeanPostProcessor)
F-->|"容器启动完成后,处理器就绪"|G("等待拦截 Bean 创建流程")
关键说明:
CommonAnnotationBeanPostProcessor与AutowiredAnnotationBeanPostProcessor由同一个AnnotationConfigUtils工具类注册,是 Spring 注解驱动的核心处理器组合。- 若项目中未引入 JDK 的
javax.annotation相关依赖(如 JDK 9+ 需手动引入javax.annotation-api),@Resource注解将无法被识别。
2. 注解识别与注入:核心方法与流程
CommonAnnotationBeanPostProcessor 的注入流程与 AutowiredAnnotationBeanPostProcessor 类似,但核心差异在于 “注入点封装” 和 “资源查找规则”:
graph TD
A("Spring 容器创建 Bean 流程")-->|"1. Bean 实例化完成"| B("调用 CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition()")
B-->|"解析 Bean 的类结构,查找 @Resource 注解"| C("findAutowiringMetadata(beanName, beanClass, null)")
C -->|封装注入点信息| D("InjectionMetadata.checkConfigMembers()")
D-->|"缓存元数据到 BeanDefinition"| E("元数据缓存完成")
A -->|"2. 进入属性填充阶段"| F("调用 CommonAnnotationBeanPostProcessor.postProcessProperties()")
F-->|获取缓存的注入元数据| G("findAutowiringMetadata(beanName, beanClass, pvs)")
G-->|执行注入| H("InjectionMetadata.inject(bean, beanName, pvs)")
H-->|遍历注入点元素| I("ResourceElement.inject(bean, beanName, pvs)")
I-->|获取要注入的资源| J("ResourceElement.getResourceToInject(bean, beanName)")
J-->|委托处理器查找资源| K("CommonAnnotationBeanPostProcessor.getResource(descriptor, beanName)")
K-->|自动装配资源| L("CommonAnnotationBeanPostProcessor.autowireResource(beanFactory, descriptor, beanName)")
L-->|"核心:按名称查找 Bean"| M("DefaultListableBeanFactory.resolveBeanByName(descriptor.getName())")
M-->|判断是否找到匹配名称的 Bean| N{Bean 是否存在}
N-->|是| O("返回找到的 Bean 实例")
N-->|否| P("按类型查找 Bean(名称匹配失败后的降级策略)")
P-->|找到唯一匹配的 Bean| O
P-->|未找到或多个匹配| Q("抛出 NoSuchBeanDefinitionException 或 NoUniqueBeanDefinitionException")
O-->|通过反射注入到字段/方法| R("注入完成")
关键细节解析
- ResourceElement 封装:
@Resource对应的注入点元素是ResourceElement(继承自InjectionElement),它封装了@Resource注解的属性(如name、type)和字段 / 方法的反射信息。 - 注入规则:名称优先:
@Resource的核心注入规则是 “按名称匹配”:首先根据@Resource(name="xxx")指定的名称查找 Bean,若未指定name,则默认使用 “字段名称” 或 “方法参数名称” 作为查找键。- 若按名称未找到 Bean,会降级为 “按类型匹配”,此时逻辑与
@Autowired类似,但不支持@Qualifier注解。
- 与 @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)提供理论支撑。