在一次开发中,偶然间发现使用@Autowired注入有多个实现的对象时,并未通过@Qualifier定义想要的实现,代码也成功启动并且注入了自己想要的具体实现类。按照以往对@Autowired的理解:@Autowired是按照类型来注入bean的,如果有多个实现需要通过@Qualifier指定要注入的具体的bean的名称。
为了弄明白Spring具体做了什么,于是一步一步的去调试了整个bean的创建过程。
先创建一个父类 Car,其中定义了一个方法name
public interface Car {
String name();
}
然后创建它的两个子类 BMW 和 BYD
@Component("byd")
public class BYD implements Car{
@Override
public String name() {
System.out.println("I'm BYD");
return "BYD";
}
}
@Component("bmw")
public class BMW implements Car{
@Override
public String name() {
System.out.println("I'm BMW");
return "BMW";
}
}
最后在要使用的地方通过@Autowired注入 Car。
首先我们通过如下方式注入:
Car car;
@Autowired
public void setCar(@Qualifier("bmw") Car car) {
this.car = car;
}
会发现 idea 会有提示报错无法自动装配。存在多个‘Car’类型的Bean。Beans:bmw,byd。
根据提示加上@Qualifier("bmw")或者@Qualifier("byd")自然能解决这个问题,但是我们尝试使用如下方式:
Car bmw;
@Autowired
public void setCar(Car bmw) {
this.bmw = bmw;
}
此时发现报错没有了,启动程序运行bmw.name()方法,发现返回为 "BMW",属性名称和构造器传入参数更换为 "byd",重新启动程序运行 byd.name()方法,发现返回 "BYD"。 那具体是怎么做到的呢?
我们点开@Autowired注解发现它让我们去看AutowiredAnnotationBeanPostProcessor这个类,看名称知道这是Spring中bean的生命周期中的bean后置处理器(spring生命周期相关此处不赘述)且是用于处理@Autowired注解的一个后置处理器。
点开 AutowiredAnnotationBeanPostProcessor 源码,找到,其中的代码主要是获取了类中的@Autowired数据。找到postProcessProperties()方法中findAutowiringMetadata方法中代码如下:
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
...
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
// 寻找有@Autowired的field
ReflectionUtils.doWithLocalFields(targetClass, field -> {
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
boolean required = determineRequiredStatus(ann);
// 找到的field封装成AutowiredFieldElement放入currElements
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required));
}
});
// 寻找有@Autowired的method
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
// 找到的field封装成AutowiredMethodElement放入currElements
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement(method, required, pd));
}
});
// 将currElements全部添加到elements中
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
// 调用InjectionMetadata的forElements方法传入elements封装成InjectionMetadata对象
return InjectionMetadata.forElements(elements, clazz);
}
其中主要的代码加了注释,包括doWithLocalFields()方法获取有@Autowired属性(属性注入)和doWithLocalMethods()方法获取有@Autowired的方法(构造方法注入和setter方法注入)。最后全部封装成一个InjectionMetadata对象。
接着分析 AutowiredAnnotationBeanPostProcessor 源码中postProcessProperties()方法中metadata.inject(bean, beanName, pvs)方法:
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectedElement> checkedElements = this.checkedElements;
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
for (InjectedElement element : elementsToIterate) {
if (logger.isTraceEnabled()) {
logger.trace("Processing injected element of bean '" + beanName + "': " + element);
}
// 遍历每个元素来注入
element.inject(target, beanName, pvs);
}
}
}
其中最为关键的方法是element.inject(target, beanName, pvs),它把在findAutowiringMetadata中生成的InjectionMetadata对象中所有的InjectedElement进行遍历,对每个需要注入的元素调用他自身的element.inject(target, beanName, pvs)方法。上边我们说到了,InjectedElement分别有AutowiredFieldElement(属性)和AutowiredMethodElement(setter方法,构造器方法)两种,先追踪AutowiredFieldElement。点开AutowiredFieldElement的inject方法:
@Override
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 {
...
try {
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
...
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
}
其中有行代码为beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter),看名字应该是我们需要追踪的代码(因为最后把该方法的value值设置给了field的,也可以自己去追踪确定,这里直接得结论),它的实现是在DefaultListableBeanFactory,点进去查看:
@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
}
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DependencyObjectProvider(descriptor, requestingBeanName);
}
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
}
else {
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
了解过Spring源码的码友可能都会发现Spring中的do开头的方法一般就是实际执行操作逻辑的部分,于是我们找到doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter)方法,继续深入:
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
...
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
if (matchingBeans.size() > 1) {
// 获取确切的注入bean
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
}
else {
// In case of an optional Collection/Map, silently ignore a non-unique case:
// possibly it was meant to be an empty collection of multiple regular beans
// (before 4.3 in particular when we didn't even look for collection beans).
return null;
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
else {
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
Object result = instanceCandidate;
if (result instanceof NullBean) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
result = null;
}
if (!ClassUtils.isAssignableValue(type, result)) {
throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
}
return result;
}
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}
其中关于getAutowireCandidateResolver().getSuggestedValue(descriptor)和resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter)笔者没有去深入研究过,想来是一些其他的获取注入值。我们主要关注findAutowireCandidates(beanName, type, descriptor)方法,它返回了需要注入的属性的所有匹配的bean,此处返回的是BMW和BYD两个子类。接着通过后边的determineAutowireCandidate(matchingBeans, descriptor)获取到确切的需要注入的beanName,最后通过matchingBeans.get(autowiredBeanName)获取到注入的bean。继续深入:
@Nullable
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
Class<?> requiredType = descriptor.getDependencyType();
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
}
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
}
// Fallback
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateName = entry.getKey();
Object beanInstance = entry.getValue();
if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
matchesBeanName(candidateName, descriptor.getDependencyName())) {
return candidateName;
}
}
return null;
}
可以看到在这个方法中先进行了Primary判断和Highest判断(具体逻辑读者自己深入阅读),之后会对所有匹配的遍历获取到它的beanName和具体实现,在if里边判断了matchesBeanName(candidateName, descriptor.getDependencyName()),其中执行了一段代码为:
return (candidateName != null &&
(candidateName.equals(beanName) || ObjectUtils.containsElement(getAliases(beanName), candidateName)));
可以看出如果匹配的bean的beanName和属性名一致,或者是别名一致就会匹配成功。至此,Spring如何在没使用@Qualifier的情况下,只是通过吧field的名称与需要注入的bean的beanName一致也成功注入了想要的bean。在构造器注入和setter注入下,则需要在构造函数的参数名称与beanName一致才会注入想要的bean,具体代码请读者自行阅读。