全面解析 Spring 依赖注入:@Autowired、@Inject 与 @Resource 深度剖析
在 Spring 框架中,依赖注入(Dependency Injection,简称 DI)是核心功能之一。通过不同的注解(如 @Autowired
、@Inject
、@Resource
),开发者可以以声明式方式将所需组件引入到业务代码中,解耦模块间的依赖关系。本文将从注解来源、默认注入行为,到底层处理流程以及多候选 Bean 的决策机制,全面剖析 Spring 容器如何解析并注入这些注解。
使用场景
假设我们有一个 Vehicle
接口,以及它的两个实现类 Car
和 Bus
:
public interface Vehicle {}
@Component
public class Car implements Vehicle {}
@Component
public class Bus implements Vehicle {}
另有一个 VehicleService
类,需要注入一个 Vehicle
类型的 Bean。下面展示三种常用的注解方式:
1. @Autowired + @Qualifier
@Component
public class VehicleService {@Autowired@Qualifier("car") // 指定注入名称为 car 的 Beanprivate Vehicle vehicle;
}
2. @Inject + @Qualifier/@Named
@Component
public class VehicleService {@Inject@Qualifier("car")private Vehicle vehicle;@Inject@Named("bus") // 等同于 @Qualifierprivate Vehicle anotherVehicle;
}
3. @Resource
@Component
public class VehicleService {@Resource(name = "car")private Vehicle vehicle;
}
尽管三者都能完成注入,但它们在规范归属与底层实现上各有差异。
注解来源与默认行为
-
JSR 250(
jakarta.annotation-api
)- 包含:
@Resource
、@PostConstruct
、@PreDestroy
等。 - 默认按 名称 注入。
- 包含:
-
JSR 330(
jakarta.inject-api
)- 包含:
@Inject
、@Qualifier
、@Named
。 - 默认按 类型 注入,可配合
@Qualifier
/@Named
实现名称注入。
- 包含:
-
Spring 自有注解
- 包含:
@Autowired
、@Qualifier
。 - 默认按 类型 注入,可与
@Qualifier
搭配,指定名称。
- 包含:
注入流程一览
Spring 在创建 Bean 时,会依次执行:
- 实例化 (
createBeanInstance
) - 属性填充(
populateBean
) - 初始化(
initializeBean
)
属性填充阶段会触发所有已注册的 InstantiationAwareBeanPostProcessor
,其中:
CommonAnnotationBeanPostProcessor
处理@Resource
AutowiredAnnotationBeanPostProcessor
处理@Autowired
和@Inject
在 populateBean(...)
中,Spring 遍历这些后置处理器并调用其 postProcessProperties(...)
方法,为被注解的字段或 setter 提供目标依赖。
后置处理器注册时机
在 AbstractApplicationContext#refresh()
中,会先执行:
registerBeanPostProcessors(beanFactory)
finishBeanFactoryInitialization(beanFactory)
registerBeanPostProcessors
通过PostProcessorRegistrationDelegate
收集并实例化所有BeanPostProcessor
。- 这样,处理自动注入的后置处理器就已就绪,后续创建业务 Bean 时可正常进行注入。
深入剖析:@Resource 注入机制
- 扫描注解:
CommonAnnotationBeanPostProcessor
在静态块中加载@Resource
类型。 - 定位字段:
buildResourceMetadata(Class<?>)
通过反射遍历所有字段,收集带注解的成员。 - 执行注入:
postProcessProperties(...)
调用InjectionMetadata.inject(...)
,依次为每个ResourceElement
设置字段值。 - 匹配逻辑:
- 优先按 name 查找 Bean
- 若未找到且允许回退,则按 type 匹配(走 Spring 的类型解析机制)
深入剖析:@Autowired / @Inject 注入机制
- 初始化注解类型:
AutowiredAnnotationBeanPostProcessor
构造函数中加载@Autowired
、@Value
、@Inject
。 - 定位字段:
buildAutowiringMetadata(Class<?>)
反射扫描所有字段,收集AutowiredFieldElement
。 - 执行注入:在
postProcessProperties(...)
中,调用InjectionMetadata.inject(...)
,反射赋值。 - 优先匹配:
- 若
descriptor.usesStandardBeanLookup()
,会先通过名称获取(若存在) - 否则再按类型查找(调用
DefaultListableBeanFactory.doResolveDependency
)
- 若
多候选 Bean 的决策策略
当按类型匹配到多个 Bean 时,Spring 会依次考虑:
- @Primary 标注的 Bean
- 与 依赖名称 相同的 Bean
- 与 @Qualifier/@Named 指定名称一致的 Bean
- @Priority(值越小优先级越高)
- 自定义注册(
resolvableDependencies
)
若都无法唯一识别,则抛出 NoUniqueBeanDefinitionException
(或对非必须注入返回空)。
小结
- 三类注解来源不同,默认注入行为也有细微差异。
- Spring 通过两大后置处理器对注解进行解析和注入。
- 注入前后处理器在容器刷新时完成注册,确保业务 Bean 创建时即可使用。
- 多 Bean 场景下,Spring 提供完善的优先级策略,保证依赖可预测地注入。