Spring @Autowired 属性注入过程分析

网上经常说 @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。

image-20211216093941521

填充过程的关键就是执行各种 BeanPostProcessor 处理器,当这个 bp 表示 AutowiredAnnotationBeanPostProcessor 时,就表示进行 @Autowired 属性注入了。

image-20211216094201223

我们从调用栈可以看出,是调用了 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 接口的所有实现类,跟进去看下

image-20211216112244297

通过 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 找到所有的实现类的 beanName,这里我们发现有两个。

好,现在我们知道了 @Autowired 是根据类型来获取所有 bean 的,获取完后接下来就是匹配了,那之前发生的错误必然就出现在了匹配阶段。没错,我们继续往下看。

我们找到如下代码片段

image-20211216100427815

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,紧接着就返回错误异常信息了。

image-20211216100824879

以上就是 @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 -》报错

我是知识不加糖,关注我,后续还会分享更多有意思的文章!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值