Spring @Autowired 源码解析 – 为什么是ByType注入

前言

之前博主已经通过例子还有部分源码阐述了Spring自动装配的原理,模型以及自动装配的技术。简单来说就是四个模型:AUTOWIRE_NO,AUTOWIRE_BY_TYPE,AUTOWIRE_BY_NAME,和AUTOWIRE_CONSTRUCTOR。两种实现技术:@Autowired和@Resource。这些已经在笔者的【Spring解读系列目录】的IOC章节做了很详细的解析,有兴趣的同学可以进入。【Spring框架的自动装配官网解析】这一系列第一篇简单回顾下。回归正题,本片博客的内容将会从源码角度解析,为什么很多人都说@Autowired是ByType自动装配。

自动装配方式的误区

博主已经在之前的帖子里详细的说过无论是@Autowired,还是@Resource其实都不能简单说是byType还是byName注入的。因为Spring在这两个注解的实现上都做了双保险:同时支持byType和byName。严格来说@Autowried默认的装配是通过byType注入,如果是失败了再通过byName进行注入。而@Resource则是首先会通过byName的方式进行注入,如果失败了则进行byType的方式进行注入。具体的例证可以连接到这篇文章【什么是@Autowired和@Reource以及其机制】

构建Sample

既然要读源码,就必须有一个相应的例子去跟随源码的调试过程查看调用链是怎么走的。首先要有一个接口Demo,然后有两个实现类DemoImplADemoImplB,以及一个DemoService去依赖Demo

@Service
public class DemoService {
    @Autowired
    Demo demoImplA;
}

Spring启动调用链

构建完毕以后,还要说明一点:@Autowired是Spring Bean生命周期的一环,在处理依赖的时候Spring会使用populateBean ()方法调用相应的后置处理器去处理注入,这个后置处理器叫做AutowiredAnnotationBeanPostProcessor。由于Bean生命周期是一个很大的环节,因此这里就简单的用调用链表示了,主要介绍AutowiredAnnotationBeanPostProcessor后置处理器里面做的事情。根据下面的调用链,可以看到从最初的main()方法进入以后,经过populateBean()方法进行赋值,最后会调用AutowiredAnnotationBeanPostProcessor.postProcessProperties()方法进行处理,因此后续的源码就将会从这个方法开始说起。

postProcessProperties:399, AutowiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
populateBean:1420, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:593, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:516, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:324, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 854587510 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$32)
getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:322, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:202, AbstractBeanFactory (org.springframework.beans.factory.support)
preInstantiateSingletons:897, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:879, AbstractApplicationContext (org.springframework.context.support)
refresh:551, AbstractApplicationContext (org.springframework.context.support)
<init>:89, AnnotationConfigApplicationContext (org.springframework.context.annotation)
main:9, Test (com.demo.test)

源码探究

首先进入postProcessProperties()。为了追踪注入的过程,这里的bean就是DemoService对象,此时DemoService刚刚被实例化出来,正要被Spring容器进行依赖注入。其中的beanName就是根据Spring默认的生成规则生成的"demoService"。

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
   InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
   try {
	  //参数bean就是DemoService,beanName则是demoService
      metadata.inject(bean, beanName, pvs);
   }
   catch (BeanCreationException ex) {
      throw ex;
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
   }
   return pvs;
}

进入以后,发现里面当获取到bean的元数据metadata之后,将会调用metadata.inject()方法进行注入,进入方法。

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);
      }
   }
}

进入以后发现,这里又调用了另外一个inject()方法处理,这里传入的target就是上面的DemoService,beanName依然是demoService。继续进入查看下一层源码写了什么。进入下一层以后有一点要注意,这个进入的是AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject()这个内部类的inject()方法,而不是InjectionMetadata.inject()方法。这两个类是继承关系AutowiredFieldElement extends InjectionMetadata.InjectedElement,一定不要走错了方法。

  @Override
  protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
     Field field = (Field) this.member; //依赖就是Demo demoImplA
     Object value;  //赋值变量
     if (this.cached) {
        value = resolvedCachedArgument(beanName, this.cachedFieldValue);
     }
     else {
        DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
        desc.setContainingClass(bean.getClass());
        Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
        Assert.state(beanFactory != null, "No BeanFactory available");
        TypeConverter typeConverter = beanFactory.getTypeConverter();
        try {//value获取到值
           value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
        }
        catch (BeansException ex) {
           throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
        }
      synchronized (this) {   /**无关项,略去**/   }
      if (value != null) {
         ReflectionUtils.makeAccessible(field);
         field.set(bean, value);   //反射注入字段值value给bean
      }
   }
}

进入第一个变量Field field就是拿到了在DemoService里面声明的依赖信息,然后在最下面赋值。那么重点就在于value何时被赋值的。往下经过if (this.cached),由于是第一次进入,不可能缓存到内存里,因为一定走到else里面。唯一给value赋值的地方就是value = beanFactory.resolveDependency()这里了,直接进入这个方法。

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 { //result就是要找的值
      Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
            descriptor, requestingBeanName);
      if (result == null) {
         result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
      }
      return result;
   }
}

进入方法,首先会判断依赖的类型是不是系统或者容器提供的类型,由于依赖是我们自己创建的接口,因此这些都不会进入直接到else。最终返回的是resultresult声明的时候首先判断是不是lazy的,肯定不是就被赋值为null。走到if (result == null)那么对象的实例化就到了doResolveDependency()方法里面了。方法很长,我们挑关键行看,怎么区分关键行呢?就是根据我们传入的是什么,以及返回的是什么来看的。

@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
   try {
      /**忽略**/
	// matchingBeans非常重要的一个变量
      Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
      if (matchingBeans.isEmpty()) {
         if (isRequired(descriptor)) {
            raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
         }
         return null;
      }
	  //要创建的bean名字
      String autowiredBeanName;
	  //创建返回对象变量
      Object instanceCandidate;
	  //关键的判断
      if (matchingBeans.size() > 1) {
	     //获取要注入的变量名字
         autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
         if (autowiredBeanName == null) {
            if (isRequired(descriptor)|| !indicatesMultipleBeans(type)) {	//如果没有找到报错
            	return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
            }
            else {
            return null;
            }
         }
         instanceCandidate = matchingBeans.get(autowiredBeanName);
      }
      else {
         // 如果只匹配到一个,赋值autowiredBeanName和instanceCandidate
         Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
         autowiredBeanName = entry.getKey();
         instanceCandidate = entry.getValue();
      }
      /**忽略**/
      Object result = instanceCandidate;  //传递给result
      /**忽略**/
      return result;  //返回出去
   }
   finally {
      ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
   }
}

把该忽略的代码忽略掉,就直接能看到一个很重要的Map<String, Object> matchingBeans,这个变量将会把所有依赖的对象拿出来,然后在进行判断,里面的代码后面分析。执行了findAutowireCandidates()方法以后,matchingBeans就会被赋值成为{<demoImplA: object>,<demoImplB: object>},里面将会有实现Demo接口的全部类的示例对象。拿到这个map以后,紧接着是两个临时变量,再往下的判断就关键了。

注意这里判断如果if (matchingBeans.size() > 1)发现要注入的接口有多个实例,首先就把变量名字拿出来,如果名字可以直接拿到,那么就从matchingBeans中直接拿出来,返回出去。如果matchingBeans只有一个直接拿出来返回出去。然后交给上层的field.set(bean, value)把依赖的值设置进去,完成注入。

总结

通过上面的源码分析,当使用@Autowired的时候,首先会从已经实例化的单例对象池中拿出已经实例化好的bean出来。如果发现不止一个就找到能够根据命名规则匹配到的bean对象,完成注入。如果根据名字找不到则报错。但是如果发现拿到的bean对象只有一个,那么就直接返回出去赋值,完成注入。这也就是为什么多数人认为@Autowired是根据byType进行注入的,因为会先根据type进行检索注入,如果失败了才会根据名字检索注入,但是从源码来看@Autowired同样也会根据byName注入,做了一个双保险。本篇博客分析了@Autowired在源码中如何做到首先byType进行注入而后根据byName注入的。而@Resource默认根据byName,然后byType,其源码流程则和@Autowired完全不同。因此【Spring @Resource 源码解析 – 为什么是ByName注入】会针对@Resource的源码分析下,@Resource的源码又是怎么做的。

附:findAutowireCandidates()方法源码

protected Map<String, Object> findAutowireCandidates(
      @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
   //构造候选对象名字
   String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
         this, requiredType, true, descriptor.isEager());
   //构建返回map
   Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
   for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
      /**略**/
   }
   for (String candidate : candidateNames) {
      if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
         //给候选对象赋值,源码在下面
         addCandidateEntry(result, candidate, descriptor, requiredType);
      }
   }
   if (result.isEmpty()) {
      /**略**/
   }
   //把赋值过的map返回出去
   return result;
}

findAutowireCandidates()方法源码

private void addCandidateEntry(Map<String, Object> candidates, String candidateName,
      DependencyDescriptor descriptor, Class<?> requiredType) {
   if (descriptor instanceof MultiElementDescriptor) {
      /**略**/
   } //如果是单例的就直接拿出来赋值candidates.put()
   else if (containsSingleton(candidateName) || (descriptor instanceof StreamDependencyDescriptor &&
         ((StreamDependencyDescriptor) descriptor).isOrdered())) {
      Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
      candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
   }
   else {
      candidates.put(candidateName, getType(candidateName));
   }
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值