目录
大家好,我是一名Java开发工程师。今天我们要聊的话题是Spring中那个我们每天都在使用,却可能从未深入了解的魔法——@Autowired依赖注入。当你在字段上写下这个简单的注解时,Spring背后究竟施展了什么样的魔法?
一、从日常开发中的@Autowired说起
相信每位Spring开发者都写过这样的代码:
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
// 或者构造器注入
private final UserService userService;
public OrderService(UserService userService) {
this.userService = userService;
}
}
这两种注入方式看似结果相同,但底层实现却大不相同。让我们从Spring处理@Autowired的核心处理器开始探索。
二、核心魔法师:AutowiredAnnotationBeanPostProcessor
AutowiredAnnotationBeanPostProcessor是Spring处理@Autowired注解的核心类,它是一个BeanPostProcessor,专门负责处理自动装配逻辑。
public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
// 支持的注解类型,默认包含@Autowired和@Value
private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
// 如果JSR-330的@Inject存在,也支持
try {
this.autowiredAnnotationTypes.add(ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
} catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
// ...其他核心方法
}
三、依赖注入处理流程全景图
Spring处理依赖注入的主要流程可以分为以下几个阶段:
- 元数据收集阶段:找出所有需要注入的点(字段、方法等)
- 依赖解析阶段:确定每个注入点需要注入的Bean
- 依赖注入阶段:实际进行注入操作
1. 元数据收集:postProcessMergedBeanDefinition()
在Bean实例化后、初始化前,Spring会调用此方法收集注入元数据:
// AutowiredAnnotationBeanPostProcessor类
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
// 查找所有需要自动装配的元数据
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// 使用缓存提高性能
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName();
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (metadata == null) {
// 实际解析注解的代码
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
return metadata;
}
2. 依赖解析与注入:postProcessProperties()
这是实际进行依赖注入的核心方法:
// AutowiredAnnotationBeanPostProcessor类
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 获取之前收集的注入元数据
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 实际注入操作
metadata.inject(bean, beanName, pvs);
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
四、字段注入 vs 构造器注入:源码层面的差异
虽然最终效果相同,但字段注入和构造器注入在Spring内部的实现路径却大不相同。
1. 字段注入实现细节
对于字段注入,Spring会创建AutowiredFieldElement:
// AutowiredAnnotationBeanPostProcessor内部类
private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {
private final boolean required;
private volatile boolean cached = false;
public AutowiredFieldElement(Field field, boolean required) {
super(field, null);
this.required = required;
}
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
} else {
// 创建依赖描述符
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
// 解析依赖
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
synchronized (this) {
if (!this.cached) {
if (value != null || this.required) {
this.cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
this.cached = true;
}
}
}
}
if (value != null) {
// 通过反射设置字段值
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
}
2. 构造器注入实现细节
构造器注入的处理则发生在Bean实例化阶段,由ConstructorResolver负责:
// ConstructorResolver类
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
// 确定使用的构造函数和参数
Constructor<?> constructorToUse = null;
Object[] argsToUse = null;
// 遍历构造函数,寻找最匹配的
for (Constructor<?> candidate : candidates) {
Class<?>[] paramTypes = candidate.getParameterTypes();
Object[] args = new Object[paramTypes.length];
// 解析每个参数
for (int i = 0; i < args.length; i++) {
ConstructorArgumentValues.ValueHolder valueHolder = mbd.getConstructorArgumentValues().getArgumentValue(i, paramTypes[i]);
if (valueHolder != null) {
args[i] = valueHolder.getValue();
} else {
// 解析自动装配的依赖
DependencyDescriptor desc = new DependencyDescriptor(new MethodParameter(candidate, i), true);
args[i] = beanFactory.resolveDependency(desc, beanName, null, null);
}
}
// 选择最匹配的构造函数
if (constructorToUse == null || argsToUse.length > args.length) {
constructorToUse = candidate;
argsToUse = args;
}
}
// 使用选定的构造函数实例化Bean
return instantiate(beanName, mbd, constructorToUse, argsToUse);
}
关键差异:
- 时机不同:构造器注入发生在实例化阶段,字段注入发生在实例化后
- 处理类不同:构造器由ConstructorResolver处理,字段由AutowiredAnnotationBeanPostProcessor处理
- 灵活性不同:构造器注入必须满足所有依赖,字段注入可以部分满足
五、依赖解析的核心:resolveDependency()
无论是字段注入还是构造器注入,最终都会调用DefaultListableBeanFactory.resolveDependency():
// DefaultListableBeanFactory类
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// 处理@Value注解
if (descriptor.getDependencyType() == ObjectFactory.class) {
return new DependencyObjectFactory(descriptor, requestingBeanName);
}
// 处理集合类型注入
if (descriptor.getDependencyType().isArray()) {
return resolveArray(descriptor.getDependencyType().getComponentType(), requestingBeanName, descriptor);
} else if (Collection.class.isAssignableFrom(descriptor.getDependencyType())) {
return resolveCollection(descriptor.getDependencyType(), descriptor.getElementTypeDescriptor(), requestingBeanName, descriptor);
}
// 处理Map类型注入
else if (Map.class.isAssignableFrom(descriptor.getDependencyType())) {
return resolveMap(descriptor.getDependencyType(), descriptor.getMapKeyType(), descriptor.getElementTypeDescriptor(), requestingBeanName, descriptor);
}
// 查找匹配的Bean
Map<String, Object> matchingBeans = findAutowireCandidates(requestingBeanName, descriptor.getDependencyType(), descriptor);
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoMatchingBeanFound(descriptor.getDependencyType(), descriptor.getAnnotations(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
if (matchingBeans.size() > 1) {
// 处理多个候选Bean的情况
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
instanceCandidate = matchingBeans.get(autowiredBeanName);
} else {
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
return instanceCandidate;
}
六、实战:自定义自动装配逻辑
理解了原理后,我们可以扩展自动装配的行为。例如,实现一个根据自定义注解注入的处理器:
public class MyCustomAutowiredProcessor extends AutowiredAnnotationBeanPostProcessor {
public MyCustomAutowiredProcessor() {
super();
// 添加我们自定义的注解
this.autowiredAnnotationTypes.add(MyCustomInject.class);
}
@Override
protected InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
// 先处理标准的@Autowired注解
InjectionMetadata metadata = super.buildAutowiringMetadata(clazz);
// 处理我们自定义的注解
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
ReflectionUtils.doWithLocalFields(clazz, field -> {
if (field.isAnnotationPresent(MyCustomInject.class)) {
elements.add(new MyCustomFieldElement(field));
}
});
return new InjectionMetadata(clazz, elements) {
@Override
public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
// 先执行父类的注入
super.inject(target, beanName, pvs);
// 再执行我们的自定义注入
for (InjectedElement element : elements) {
element.inject(target, beanName, pvs);
}
}
};
}
private static class MyCustomFieldElement extends InjectionMetadata.InjectedElement {
protected MyCustomFieldElement(Field field) {
super(field, null);
}
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value = // 自定义解析逻辑
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
}
七、常见问题与最佳实践
1.@Autowired vs @Resource vs @Inject:
- @Autowired:Spring原生,支持required属性
- @Resource:JSR-250标准,按名称注入
- @Inject:JSR-330标准,功能类似@Autowired
2.循环依赖问题:
- 字段注入可以解决大多数循环依赖
- 构造器注入无法解决循环依赖
- 最佳实践是尽量避免循环依赖
3.测试友好性:
- 构造器注入更利于单元测试
- 字段注入需要依赖Spring容器
4.不可变对象:
- 构造器注入支持创建不可变对象(final字段)
- 字段注入无法用于final字段
八、总结:@Autowired的魔法本质
通过今天的分析,我们可以看到@Autowired背后的"魔法"实际上是Spring精心设计的一系列处理流程:
- 注解解析:通过后处理器识别需要注入的点
- 依赖查找:根据类型或名称在容器中查找匹配的Bean
- 依赖注入:通过反射或构造器参数完成实际注入
思考题:在你的项目中,更喜欢使用构造器注入还是字段注入?为什么?欢迎在评论区分享你的观点!

36

被折叠的 条评论
为什么被折叠?



