likes
comments
collection
share

Spring源码分析之依赖注入(二)

作者站长头像
站长
· 阅读数 30

一、前言

二、方法的注入

我们上一篇文章最后讲到了方法的注入, 保留了一个重要方法没有进行讲解, 具体如下图所示

Spring源码分析之依赖注入(二) 我们点进这个方法, 具体如下图所示, 接下来我就开始为大家讲解这个方法具体的内容

Spring源码分析之依赖注入(二) 下图红框部分是获取当前注入方法的入参, 因为一个方法可能有多个入参, 所以用数组接收,

Spring源码分析之依赖注入(二) 然后去遍历这个数组, 分别的根据这几个参数的信息分别去 BeanFactory中找到对应的 bean

Spring源码分析之依赖注入(二) 在这个遍历循环的过程当中有一个方法是很重要的, 接下来我带大家看一下这个方法的内部实现

Spring源码分析之依赖注入(二)

Spring源码分析之依赖注入(二) 在上图这个方法中, 入参的 DependencyDescriptor descriptor需要注意一下, 因为这个参数可以是字段或者方法参数, 但是我们去获取这个参数的信息, 只会获取其名字和类型

该方法简单流程说明:

  • 获取方法入参名字
    • 通过java反射获取的, 该方法只做了这一件事
  • 判断参数的类型(大多数情况下直接进入 else中)
  • else
    • 判断方法或属性上有没有使用 @Lazy注解

三、如果有 @Lazy注解

如果有 @Lazy注解则会进入以下方法

Spring源码分析之依赖注入(二)

Spring源码分析之依赖注入(二)

Spring源码分析之依赖注入(二) 在 IsLazy方法中, 他会先去判断你的属性有没有 @Lazy注解, 再去判断方法的参数有没有使用 @Lazy注解

假设存在 @Lazy注解, 那么通过三元运算符会进入到 buildLazyResolutionProxy方法中, 这个方法内部主要是和 AOP相关, 后续我们会出专门的文章进行讲解, 目前暂时知道它会产生一个代理对象就可以了

Spring源码分析之依赖注入(二)

简单流程说明:

  • 最开始为属性赋值, 但是因为属性是被 @Lazy注解修饰的, 所以被赋的值是一个 代理对象
  • 当真正的去执行这个 代理对象的某个方法的时候
  • 才会根据 这个代理对象字段的信息去 BeanFactory中找对应的 bean对象
  • 再去执行 bean对象对应的方法

四、如果没有 @Lazy注解

我们回到之前的方法中, 如果没有 @Lazy注解, 那么就会进入到下面的这个方法中

Spring源码分析之依赖注入(二)

doResolveDependency方法是非常核心的

@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

   InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
   try {
      // 如果当前descriptor之前做过依赖注入了,则可以直接取shortcut了,相当于缓存
      Object shortcut = descriptor.resolveShortcut(this);
      if (shortcut != null) {
         return shortcut;
      }

       
      Class<?> type = descriptor.getDependencyType();
      // 获取@Value所指定的值
      Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
      if (value != null) {
         if (value instanceof String) {
            // 占位符填充(${})
            String strVal = resolveEmbeddedValue((String) value);
            BeanDefinition bd = (beanName != null && containsBean(beanName) ?
                  getMergedBeanDefinition(beanName) : null);
            // 解析Spring表达式(#{})
            value = evaluateBeanDefinitionString(strVal, bd);
         }
         // 将value转化为descriptor所对应的类型
         TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
         try {
            return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
         }
         catch (UnsupportedOperationException ex) {
            // A custom TypeConverter which does not support TypeDescriptor resolution...
            return (descriptor.getField() != null ?
                  converter.convertIfNecessary(value, type, descriptor.getField()) :
                  converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
         }
      }

      // 如果descriptor所对应的类型是数组、Map这些,就将descriptor对应的类型所匹配的所有bean方法,不用进一步做筛选了
      Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
      if (multipleBeans != null) {
         return multipleBeans;
      }

      // 找到所有Bean,key是beanName, value有可能是bean对象,有可能是beanClass
      Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
      if (matchingBeans.isEmpty()) {
         // required为true,抛异常
         if (isRequired(descriptor)) {
            raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
         }
         return null;
      }

      String autowiredBeanName;
      Object instanceCandidate;

      if (matchingBeans.size() > 1) {
         // 根据类型找到了多个Bean,进一步筛选出某一个, @Primary-->优先级最高--->name
         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();
      }

      // 记录匹配过的beanName
      if (autowiredBeanNames != null) {
         autowiredBeanNames.add(autowiredBeanName);
      }
      // 有可能筛选出来的是某个bean的类型,此处就进行实例化,调用getBean
      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);
   }
}

resolveMultipleBeans方法

我们看一下 resolveMultipleBeans详情 Spring源码分析之依赖注入(二)

可以看到, 他会根据你属性注入的类型不同来进行不同的操作, 会去直接匹配对应的 bean方法,不在进行进一步的筛选了

Spring源码分析之依赖注入(二) 例如当我们属性注入的类型为 Map的时候, 它会强制要求key为String类型, 并去寻找相应的 bean

Spring源码分析之依赖注入(二) 具体的如下所示, 他会根据 bean的类型去找 bean的名字

Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType,
      new MultiElementDescriptor(descriptor));

然后去判断 如果没有找到 Bean则抛出异常

核心的findAutowireCandidates方法

流程信息

  • 找出BeanFactory中类型为type的所有的Bean的名字,注意是名字,而不是Bean对象,因为我们可以根据BeanDefinition就能判断和当前type是不是匹配,不用生成Bean对象
  • 把resolvableDependencies中key为type的对象找出来并添加到result中
  • 遍历根据type找出的beanName,判断当前beanName对应的Bean是不是能够被自动注入
  • 先判断beanName对应的BeanDefinition中的autowireCandidate属性,如果为false,表示不能用来进行自动注入,如果为true则继续进行判断
  • 判断当前type是不是泛型,如果是泛型是会把容器中所有的beanName找出来的,如果是这种情况,那么在这一步中就要获取到泛型的真正类型,然后进行匹配,如果当前beanName和当前泛型对应的真实类型匹配,那么则继续判断
  • 如果当前DependencyDescriptor上存在@Qualifier注解,那么则要判断当前beanName上是否定义了Qualifier,并且是否和当前DependencyDescriptor上的Qualifier相等,相等则匹配
  • 经过上述验证之后,当前beanName才能成为一个可注入的,添加到result中

源码讲解

我们进入这个方法, 里面的注释是比较清楚的, 接下来主要是针对一些方法的讲解

Spring源码分析之依赖注入(二)

Spring源码分析之依赖注入(二)

beanNamesForTypeIncludingAncestors方法

  • 先根据自己的类型去 BeanFactory中找 bean的名字
  • 如果有 父BeanFactory, 就去 父BeanFactory中找
  • 然后将两个集合合并起来返回

Spring源码分析之依赖注入(二)

怎么根据 Bean的类型去找

我们进入 getBeanNamesForType方法, 具体如下图所示

Spring源码分析之依赖注入(二)

doGetBeanNamesForType方法

这个方法太长了, 而且我看着真的蒙, 简单讲一下流程, 感兴趣的话大家自己看一下:

  • 遍历 BeanDefinition
  • 判断bean名字前有没有&符号, 有的话去处理掉
  • 拿取 BeanDefinition的属性判断当前的是否匹配

Spring源码分析之依赖注入(二)

返回

我们回到本方法的开头, 只要知道这个方法是从 BeanFactory中找出和 requiredType所匹配的 beanName, 仅仅是 beanName, 这些 bean不一定经过了实例化, 只有到最终确定某个 Bean了, 如果这个Bean还没有实例化才会进行实例化

Spring源码分析之依赖注入(二)

resolvableDependencies

在 Spring启动的时候就会忘这个 map中存东西,

Spring源码分析之依赖注入(二) 这个 map具体的存储过程如下图所示: 后续讲Spring启动源码的时候会讲

Spring源码分析之依赖注入(二)

Bean对象加入到集合

然后去遍历这个 map, 获取到 key也就是 Bean类型, 判断有没有相同的 Bean类型, 如果有就把对应的对象加入到我们的返回数组中

Spring源码分析之依赖注入(二)

遍历判断是不是自己注入自己

  • 如果不是自己注入自己,则判断该candidate到底能不能用来进行自动注入
  • 注入非自己
  • 如果有多个candidate 最终将匹配成功 candidate加入到 result
  • 因为有多个, 且result不为空
  • 所以直接返回 result

Spring源码分析之依赖注入(二)

result.isEmpty()

  • 假设有多个 candidate, 且都不匹配, result == null
  • 则会去判断需要的类型是不是 Map, 数组之类的
  • 中间那个for循环用的很少 , 就先不看了
  • 如果匹配的是自己, 就将自己添加到result中

Spring源码分析之依赖注入(二)

返回

然后去判断 如果没有找到 Bean则抛出异常

Spring源码分析之依赖注入(二)

如果找到了多个 Bean

如图所示, 如果根据 beanName找到了多个 Bean那么该 if判断则为 true

Spring源码分析之依赖注入(二)

determineAutowireCandidate方法

Spring源码分析之依赖注入(二) 在这个方法中, 我们先去获取他的类型, 获取类型之后执行 determinePrimaryCandidate方法, 下图是这个方法的详情

Spring源码分析之依赖注入(二) 上图中的方法经历了以下几个步骤:

  • 遍历 map获取对应的 beanName和 ( bean对象或 Class类)
  • isPrimary方法, 判断这多个 Bean有没有否实现了 @isPrimary注解
  • 如果都实现了 @isPrimary注解则抛出异常
  • 如果只有一个实现了 @isPrimary注解则使用这个 Bean

方法结束, 返回上一层 determineAutowireCandidate方法

Spring源码分析之依赖注入(二) 途中红框部分的方法讲解完毕, 接下来它去寻找了我们的返回值, 如果都没有实现 @Primary注解, 那么继续往下走

determineHighestPriorityCandidate方法

方法流程如下:

  • 遍历map
  • 取当前某个Bean的优先级是什么
  • 获取 @Priority注解, 方法在下图红框位置

Spring源码分析之依赖注入(二)

@Priority 该注解不能使用在方法上, 只能使用在类上面

@Order在依赖注入中是没有去使用判断的

方法结束, 返回上一层 determineAutowireCandidate方法

Spring源码分析之依赖注入(二) 如上图所示, 我们继续往下走, 最后才是根据名字去判断 bean对象的

通过源码可以看到 @Resource 和 @Autowired的区别 如果有多个同名beanName, 那么@Autowired 会先去寻找有没有哪个 bean是有 @Primary注解的, 如果都没有, 则去寻找有没有哪个类上是有 @Priority优先级注解的, 如果都没有则会去按照名称查找 这个面试题在刚毕业的时候也是经常被问到的, 以前只知道答案, 这次通过源码对其的解答也更加清晰明了了

返回

接下来返回到下面红框的位置, 继续玩下走

如果没有找到对象, required == true抛出异常, 如果不为 true则返回 null, 然后赋值

Spring源码分析之依赖注入(二)

如果只找到了一个 Bean

那么直接为之前定义的 autowiredBeanName 和 instanceCandidate进行赋值

如果找到了Bean

如果找到了Bean , 则进行记录

Spring源码分析之依赖注入(二)

判断 instanceCandidate是不是一个类

如果是一个类, 那么获取这个类的 bean

Spring源码分析之依赖注入(二)

去判断这个类型是不是 NullBean

NullBean的定义代码如下所示, 当以下面的方式定义一个 bean的时候, 这个bean存在 map中的 key为 JuejinService, value为 NullBean

相关判断代码如下所示

Spring源码分析之依赖注入(二)

最后返回 over

最后红框判断的是期望类型和实际获取到的类型是否一致问题, 就不详细解释了

Spring源码分析之依赖注入(二)

Spring专栏历史文章

本文内容到此结束了

如有收获欢迎点赞👍收藏💖关注✔️,您的鼓励是我最大的动力。

如有错误❌疑问💬欢迎各位大佬指出。

我是 宁轩 , 我们下次再见