网上经常说 @Autowired 是先 byType,再 byName,这到底什么意思?我们来一探究竟!
先来看一个例子
public interface MyService {
}
@Service
public class MyServiceImpl implements MyService {
}
@RestController
public class MyController {
@Autowired
private MyService myService;
}
这种情况是我们常用的方式,一个 Service 接口只有一个实现,一点问题没有。
但是如果此时又有一个实现类实现了 Service 接口后,会怎样呢?
@Service
public class MyServiceImpl2 implements MyService {
}
此时 MyController 如果还是原来的,那么就会报错了。
Field myService in MyController required a single bean, but 2 were found.
错误显示有两个 myService 导致 MyController 不知道该使用哪一个。
我先给出解决方案,只需要将属性名和 Bean 名称匹配即可。
@RestController
public class MyController {
@Autowired
private MyService myServiceImpl;
@Autowired
private MyService myServiceImpl2;
}
由于实现类的 Bean 名称就是它们的类名首字母小写,所以将属性名改成类名首字母小写形式,明确指定我要使用哪一个实现类。
接下来,我们来分析下源码,看看这里面的前因后果。
首先,一个 MyController 这个 Bean 的创建是由 AbstractAutowireCapableBeanFactory#createBeanInstance
来完成的,构造出了 MyController 实例后,会调用 AbstractAutowireCapableBeanFactory#populate
来填充属性,这里填充的就是 myService。
填充过程的关键就是执行各种 BeanPostProcessor 处理器,当这个 bp 表示 AutowiredAnnotationBeanPostProcessor
时,就表示进行 @Autowired 属性注入了。
我们从调用栈可以看出,是调用了 AutowiredAnnotationBeanPostProcessor#postProcessProperties
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 寻找要注入的字段和方法
InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 注入
metadata.inject(bean, beanName, pvs);
return pvs;
} catch (BeanCreationException var6) {
throw var6;
} catch (Throwable var7) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", var7);
}
}
inject 方法最终调用的是 AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field)this.member;
Object value;
if (this.cached) {
try {
value = AutowiredAnnotationBeanPostProcessor.this.resolvedCachedArgument(beanName, this.cachedFieldValue);
} catch (NoSuchBeanDefinitionException var7) {
value = this.resolveFieldValue(field, bean, beanName);
}
} else {
// 寻找依赖
value = this.resolveFieldValue(field, bean, beanName);
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
我们看第 12 行的 resolveFieldValue 方法
@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
//...
try {
// 解析依赖
value = AutowiredAnnotationBeanPostProcessor.this.beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
} catch (BeansException var12) {
throw new UnsatisfiedDependencyException((String)null, beanName, new InjectionPoint(field), var12);
}
//...
}
第 6 行我们继续跟进去
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
//...
if (result == null) {
// 跟进去
result = this.doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
} else {
return new DefaultListableBeanFactory.DependencyObjectProvider(descriptor, requestingBeanName);
}
}
在 doResolveDependency 方法内我们注意到这行代码
Map<String, Object> matchingBeans = this.findAutowireCandidates(beanName, type, descriptor);
这行代码是用来找到 MyService 接口的所有实现类,跟进去看下
通过 BeanFactoryUtils.beanNamesForTypeIncludingAncestors
找到所有的实现类的 beanName,这里我们发现有两个。
好,现在我们知道了 @Autowired 是根据类型来获取所有 bean 的,获取完后接下来就是匹配了,那之前发生的错误必然就出现在了匹配阶段。没错,我们继续往下看。
我们找到如下代码片段
matchingBeans 的值为 2 我们已经知道了,那么该值如果大于 1,这时候就需要通过 determineAutowireCandidate 方法来判断到底选择哪一个了。
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
Class<?> requiredType = descriptor.getDependencyType();
// 1.@Primary 优先
String primaryCandidate = this.determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
} else {
// 2.@Priority 其次
String priorityCandidate = this.determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
} else {
// 3. 根据 beanName 匹配
Iterator var6 = candidates.entrySet().iterator();
String candidateName;
Object beanInstance;
do {
if (!var6.hasNext()) {
return null;
}
Entry<String, Object> entry = (Entry)var6.next();
candidateName = (String)entry.getKey();
beanInstance = entry.getValue();
} while((beanInstance == null || !this.resolvableDependencies.containsValue(beanInstance)) && !this.matchesBeanName(candidateName, descriptor.getDependencyName()));
// 4.都没有返回 null
return candidateName;
}
}
}
我们从 determineAutowireCandidate 方法中可以看出,确定使用哪个 bean 的判断标准有三个:
- 标注了 @Primary 注解的就是被 Spring 选中的幸运儿
- 如果没有标注 @Primary,但是标注了 @Priority,会获取优先级最高的 bean 作为幸运儿
- 如果 @Primary 和 @Priority 都没有标注,会匹配属性名和 beanName 相同的 bean 作为幸运儿
- 要是以上三个条件,一个都不满足,返回 null
我们这里就比较悲催了,一个都不满足,返回了 null,紧接着就返回错误异常信息了。
以上就是 @Autowired 的注入过程分析了,网上说的不清不楚,不如自己调试一下就明白了。
总结一下:
AbstractAutowireCapableBeanFactory#createBeanInstance
AbstractAutowireCapableBeanFactory#populate
AutowiredAnnotationBeanPostProcessor#postProcessProperties
AutowiredFieldElement#inject
AutowiredFieldElement#resolveFieldValue
DefaultListableBeanFactory#resolveDependency
DefaultListableBeanFactory#doResolveDependency
DefaultListableBeanFactory#findAutowireCandidates
BeanFactoryUtils.beanNamesForTypeIncludingAncestors
DefaultListableBeanFactory#determineAutowireCandidate
先按类型获取所有bean
再根据如下匹配方式进行匹配:
@Primiary
@Priority
beanName
null -》报错
我是知识不加糖,关注我,后续还会分享更多有意思的文章!