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

本文深入探讨Spring框架中依赖注入的实现,特别是@Autowired注解用于方法注入时的源码分析。文章从有无@Lazy注解的角度出发,详细解释了在依赖查找、Bean的创建和选择过程,涉及到BeanFactory中的核心方法,如resolveMultipleBeans和findAutowireCandidates等。通过对源码的解读,阐述了如何处理多个候选Bean、@Primary和@Priority注解的优先级判断,以及懒加载机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

主要讲解了在依赖注入过程中, @Autowired等注解方法注入的源码解析

二、方法的注入

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

我们点进这个方法, 具体如下图所示, 接下来我就开始为大家讲解这个方法具体的内容

下图红框部分是获取当前注入方法的入参, 因为一个方法可能有多个入参, 所以用数组接收,

然后去遍历这个数组, 分别的根据这几个参数的信息分别去 BeanFactory中找到对应的 bean

在这个遍历循环的过程当中有一个方法是很重要的, 接下来我带大家看一下这个方法的内部实现

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

该方法简单流程说明:

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

三、如果有 @Lazy注解

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

在 IsLazy方法中, 他会先去判断你的属性有没有 @Lazy注解, 再去判断方法的参数有没有使用 @Lazy注解

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

简单流程说明:

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

四、如果没有 @Lazy注解

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

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详情

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

例如当我们属性注入的类型为 Map的时候, 它会强制要求key为String类型, 并去寻找相应的 bean

具体的如下所示, 他会根据 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中

源码讲解

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

beanNamesForTypeIncludingAncestors方法

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

怎么根据 Bean的类型去找

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

doGetBeanNamesForType方法

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

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

返回

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

resolvableDependencies

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

这个 map具体的存储过程如下图所示: 后续讲Spring启动源码的时候会讲

Bean对象加入到集合

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

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

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

result.isEmpty()

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

返回

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

如果找到了多个 Bean

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

determineAutowireCandidate方法

在这个方法中, 我们先去获取他的类型, 获取类型之后执行 determinePrimaryCandidate方法, 下图是这个方法的详情

上图中的方法经历了以下几个步骤:

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

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

途中红框部分的方法讲解完毕, 接下来它去寻找了我们的返回值, 如果都没有实现 @Primary注解, 那么继续往下走

determineHighestPriorityCandidate方法

方法流程如下:

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

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

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

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

如上图所示, 我们继续往下走, 最后才是根据名字去判断 bean对象的

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

返回

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

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

如果只找到了一个 Bean

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

如果找到了Bean

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

判断 instanceCandidate是不是一个类

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

去判断这个类型是不是 NullBean

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

@Bean
public JuejinService juejinService(){
   return null;
}

相关判断代码如下所示

最后返回 over

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值