【小家Spring】细说Spring IOC容器的自动装配(@Autowired),以及Spring4.0新特性之【泛型依赖注入】的源码级解析(上)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 【小家Spring】细说Spring IOC容器的自动装配(@Autowired),以及Spring4.0新特性之【泛型依赖注入】的源码级解析(上)

前言


前面我发布了Spring IOC容器的刷新(初始化)过程,以及Spring 容器的Bean的实例化、初始化过程。其中有一个步骤小伙伴们比较关心,也提问的比较多,那就是泛型依赖注入。鉴于之前对这一块描述得也不是很详细,鉴于此处还是比较重要的,因此本文专门用篇幅聊聊这个事


看本篇文章之前,建议至少已经了解Spring容器的一个大致过程,最好已经看过博文(或者知道):

【小家Spring】AbstractAutowireCapableBeanFactory#populateBean实现Bean的属性赋值和initializeBean对Bean的初始化


因此本文将直接从依赖注入这一步,分析Spring是怎么样实现控制反转、依赖注入(DI)的~

Demo Show(自动装配的)


在讲解之前,先构造一个例子看看效果(效果很像RedisTemplate):


// 准备一个带泛型的Bean
@Getter
@Setter
@NoArgsConstructor  
@AllArgsConstructor
@ToString
public class GenericBean<T, W> {
    private T t;
    private W w;
}
// config配置文件中注入两个泛型Bean
    @Bean
    public Parent parentOne() {
        return new Parent();
    }
    @Bean
    public Parent parentTwo() {
        return new Parent();
    }
    @Bean
    public GenericBean<String, String> stringGeneric() {
        return new GenericBean<String, String>("str1", "str2");
    }
    @Bean
    public GenericBean<Object, Object> objectGeneric() {
        return new GenericBean<Object, Object>("obj1", 2);
    }
// 使用@Autowired注入,测试一下:
    @Autowired
    private GenericBean<Object, Object> objectGenericBean; //GenericBean(t=obj1, w=2)
    @Autowired
    private GenericBean<String, String> stringGenericBean; //GenericBean(t=st   r1, w=str2)
    // 注意,容器里虽然有两个Parent,这里即使不使用@Qualifier也不会报错。
    // 但是需要注意字段名parentOne,必须是容器里存在的,否则就报错了。
    @Autowired
    private Parent parentOne; //com.fsx.bean.Parent@23c98163
    //Spring4.0后的新特性,这样会注入所有类型为(包括子类)GenericBean的Bean(但是顺序是不确定的,可通过Order接口控制顺序)
    @Autowired
    private List<GenericBean> genericBeans; //[GenericBean(t=st   r1, w=str2), GenericBean(t=obj1, w=2)]
    // 这里的key必须是String类型,把GenericBean类型的都拿出来了,beanName->Bean
    @Autowired
    private Map<String, GenericBean> genericBeanMap; //{stringGenericBean=GenericBean(t=st   r1, w=str2), objectGenericBean=GenericBean(t=obj1, w=2)}
    // 这里面,用上泛型也是好使的,就只会拿指定泛型的了
    @Autowired
    private Map<String, GenericBean<Object, Object>> genericBeanObjMap; //{objectGenericBean=GenericBean(t=obj1, w=2)}
    // 普通类型,容器里面没有的Bean类型,注入是会报错的
    //@Autowired
    //private Integer normerValue;


如果你还不知道泛型依赖注入的话,就应该有这样一个疑问(应该有哈,若没有,看来你对@Autowired还没有个概性的认识):

@Autowired明明是根据类型进行注入的,那我们往容器里放置了两个GenericBean类型的Bean,为何启动没有报错呢???

(最直观的感受:若我们的service有两个serviceImpl,直接仅使用@Autowired注入是会报错的)


那么接下来,我们通过跟踪源码的方式,一层一层剥开内部的缘由,看看葫芦里到底是什么药~

依赖注入源码分析


在上面推荐的博文里已经讲到了,Spring在populateBean这一步为属性赋值的时候,会执行InstantiationAwareBeanPostProcessor处理器的postProcessPropertyValues方法。


这里AutowiredAnnotationBeanPostProcessor该处理器的postProcessPropertyValues方法就是来处理该注解的。(因此,我们的源码解析从此处开始吧~~~~)


  @Override
  public PropertyValues postProcessPropertyValues(
      PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
    // 找到所有的@Autowired元数据-------当前值可以见下图
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
      // 所以我们看InjectionMetadata 的inject即可,见下
      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;
  }


metadata值如下:


image.png


InjectionMetadata#inject


  public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Collection<InjectedElement> checkedElements = this.checkedElements;
    // 这里是待遍历、待处理的属性Element们(备注,本处的InjectedElement实现类为:AutowiredFieldElement 因为我们是Field注入嘛)
    // 所以从下可知,我们直接看AutowiredFieldElement#inject方法吧
    Collection<InjectedElement> elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements);
    if (!elementsToIterate.isEmpty()) {
      boolean debug = logger.isDebugEnabled();
      for (InjectedElement element : elementsToIterate) {
        if (debug) {
          logger.debug("Processing injected element of bean '" + beanName + "': " + element);
        }
        element.inject(target, beanName, pvs);
      }
    }
  }


AutowiredFieldElement#inject:根据字段注入

AutowiredFieldElement是AutowiredAnnotationBeanPostProcessor的一个私有普

通内部类,高内聚~

    // 这段代码虽然长,其实核心逻辑还并不在这里,而是在beanFactory Bean工厂的resolveDependency处理依赖实现里
    @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 {
        // 把field和required属性,包装成desc描述类
        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 {
          // 把desc传进去,里面还有注解信息、元信息等等,这个方法是根据注解信息寻找到依赖的Bean的核心逻辑
          // 备注:此部分处理依赖逻辑交给Bean工厂,其实也没毛病。毕竟它处理的相当于是Field
          // 那么接下里,重点分析resolveDependency这个方法
          value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
        } catch (BeansException ex) {
          throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
        }
        synchronized (this) {
          if (!this.cached) {
            if (value != null || this.required) {
              this.cachedFieldValue = desc;
              registerDependentBeans(beanName, autowiredBeanNames);
              if (autowiredBeanNames.size() == 1) {
                String autowiredBeanName = autowiredBeanNames.iterator().next();
                if (beanFactory.containsBean(autowiredBeanName) &&
                    beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                  this.cachedFieldValue = new ShortcutDependencyDescriptor(
                      desc, autowiredBeanName, field.getType());
                }
              }
            }
            else {
              this.cachedFieldValue = null;
            }
            this.cached = true;
          }
        }
      }
      if (value != null) {
        ReflectionUtils.makeAccessible(field);
        field.set(bean, value);
      }
    }
  }


DefaultListableBeanFactory#resolveDependency:解决依赖(根据依赖关系找到值)

  @Override
  @Nullable
  public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    // 把当前Bean工厂的名字发现器赋值给传进来DependencyDescriptor 类
    // 这里面注意了:有必要说说名字发现器这个东西,具体看下面吧==========还是比较重要的
    // Bean工厂的默认值为:private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    // 支持到Optional类型的注入,比如我们这样注入:private Optional<GenericBean<Object, Object>> objectGenericBean;
    // 也是能够注入进来的,只是类型变为,Optional[GenericBean(t=obj1, w=2)]
    // 对于Java8中Optional类的处理
    if (Optional.class == descriptor.getDependencyType()) {
      return createOptionalDependency(descriptor, requestingBeanName);
    }
    // 兼容ObjectFactory和ObjectProvider(Spring4.3提供的接口)
    // 关于ObjectFactory和ObjectProvider在依赖注入中的大作用,我觉得是非常有必要再撰文讲解的
    //对于前面讲到的提早曝光的ObjectFactory的特殊处理
    else if (ObjectFactory.class == descriptor.getDependencyType() ||
        ObjectProvider.class == descriptor.getDependencyType()) {
      return new DependencyObjectProvider(descriptor, requestingBeanName);
    }
    // 支持到了javax.inject.Provider这个类的实现
    else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
      return new Jsr330ProviderFactory().createDependencyProvider(descriptor, requestingBeanName);
    }
    // 这个应该是我们觉得部分触及到的,其实不管何种方式,最终都是交给doResolveDependency方法去处理了
    else {
      //getAutowireCandidateResolver()得到ContextAnnotationAutowireCandidateResolver 根据依赖注解信息,找到对应的Bean值信息
      //getLazyResolutionProxyIfNecessary方法,它也是唯一实现。
      //如果字段上带有@Lazy注解,表示进行懒加载 Spring不会立即创建注入属性的实例,而是生成代理对象,来代替实例
      Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
          descriptor, requestingBeanName);
      // 如果在@Autowired上面还有个注解@Lazy,那就是懒加载的,是另外一种处理方式(是一门学问)
      // 这里如果不是懒加载的(绝大部分情况都走这里) 就进入核心方法doResolveDependency 下面有分解
      if (result == null) {
        result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
      }
      return result;
    }
  }


DefaultListableBeanFactory#doResolveDependency 处理属性依赖关系的核心方法:通用的处理逻辑


  @Nullable
  public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    // 相当于打个点,记录下当前的步骤位置  返回值为当前的InjectionPoint 
    InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    try {
      // 简单的说就是去Bean工厂的缓存里去看看,有没有名称为此的Bean,有就直接返回,没必要继续往下走了
      // 比如此处的beanName为:objectGenericBean等等
      Object shortcut = descriptor.resolveShortcut(this);
      if (shortcut != null) {
        return shortcut;
      }
      // 此处为:class com.fsx.bean.GenericBean
      Class<?> type = descriptor.getDependencyType();
      // 看看ContextAnnotationAutowireCandidateResolver的getSuggestedValue方法,具体实现在父类 QualifierAnnotationAutowireCandidateResolver中
      //处理@Value注解-------------------------------------
      //获取@Value中的value属性
      Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
      // 若存在value值,那就去解析它。使用到了AbstractBeanFactory#resolveEmbeddedValue
      // 也就是使用StringValueResolver处理器去处理一些表达式~~
      if (value != null) {
        if (value instanceof String) {
          String strVal = resolveEmbeddedValue((String) value);
          BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
          value = evaluateBeanDefinitionString(strVal, bd);
        }
        //如果需要会进行类型转换后返回结果
        TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
        return (descriptor.getField() != null ?
            converter.convertIfNecessary(value, type, descriptor.getField()) :
            converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
      }
      //对数组、Collection、Map等类型进行处理,也是支持自动注入的。
      //因为是数组或容器,Sprng可以直接把符合类型的bean都注入到数组或容器中,处理逻辑是:
      //1.确定容器或数组的组件类型 if else 分别对待,分别处理
      //2.调用findAutowireCandidates(核心方法)方法,获取与组件类型匹配的Map(beanName -> bean实例)
      //3.将符合beanNames添加到autowiredBeanNames中
      Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
      if (multipleBeans != null) {
        return multipleBeans;
      }
      // 获取所有【类型】匹配的Beans,形成一个Map(此处用Map装,是因为可能不止一个符合条件)
      // 该方法就特别重要了,对泛型类型的匹配、对@Qualifierd的解析都在这里面,下面详情分解
      Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
      // 若没有符合条件的Bean。。。
      if (matchingBeans.isEmpty()) {
          // 并且是必须的,那就抛出没有找到合适的Bean的异常吧
          // 我们非常熟悉的异常信息:expected at least 1 bean which qualifies as autowire candidate...
        if (isRequired(descriptor)) {
          raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
        }
        return null;
      }
      String autowiredBeanName;
      Object instanceCandidate;
      //如果类型匹配的bean不止一个,Spring需要进行筛选,筛选失败的话继续抛出异常
      // 如果只找到一个该类型的,就不用进这里面来帮忙筛选了~~~~~~~~~
      if (matchingBeans.size() > 1) {
        // 该方法作用:从给定的beans里面筛选出一个符合条件的bean,此筛选步骤还是比较重要的,因此看看可以看看下文解释吧
        autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
        if (autowiredBeanName == null) {
          // 如果此Bean是要求的,或者 不是Array、Collection、Map等类型,那就抛出异常NoUniqueBeanDefinitionException
          if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
            // 抛出此异常
            return descriptor.resolveNotUnique(type, matchingBeans);
          }
          // Spring4.3之后才有:表示如果是required=false,或者就是List Map类型之类的,即使没有找到Bean,也让它不抱错,因为最多注入的是空集合嘛
          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.
        // 仅仅只匹配上一个,走这里 很简单  直接拿出来即可
        // 注意这里直接拿出来的技巧:不用遍历,直接用iterator.next()即可
        Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
        autowiredBeanName = entry.getKey();
        instanceCandidate = entry.getValue();
      }
      // 把找到的autowiredBeanName 放进去
      if (autowiredBeanNames != null) {
        autowiredBeanNames.add(autowiredBeanName);
      }
      // 底层就是调用了beanFactory.getBean(beanName);  确保该实例肯定已经被实例化了的
      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;
      }
      // 再一次校验,type和result的type类型是否吻合=====
      if (!ClassUtils.isAssignableValue(type, result)) {
        throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
      }
      return result;
    }
    // 最终把节点归还回来
    finally {
      ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
    }
  }


若出现多个Bean,将由下面方法去匹配和决定~


//determineAutowireCandidate 从多个Bean中,筛选出一个符合条件的Bean
  @Nullable
  protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
    Class<?> requiredType = descriptor.getDependencyType();
    // 看看传入的Bean中有没有标注了@Primary注解的
    String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
    // 如果找到了 就直接返回
    // 由此可见,@Primary的优先级还是非常的高的
    if (primaryCandidate != null) {
      return primaryCandidate;
    }
    //找到一个标注了javax.annotation.Priority注解的。(备注:优先级的值不能有相同的,否则报错)
    String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
    if (priorityCandidate != null) { 
      return priorityCandidate;
    }
    // Fallback
    // 这里是最终的处理(相信绝大部分情况下,都会走这里~~~~~~~~~~~~~~~~~~~~)
    // 此处就能看出resolvableDependencies它的效能了,他会把解析过的依赖们缓存起来,不用再重复解析了
    for (Map.Entry<String, Object> entry : candidates.entrySet()) {
      String candidateName = entry.getKey();
      Object beanInstance = entry.getValue();
      // 到这一步就比较简单了,matchesBeanName匹配上Map的key就行。
      // 需要注意的是,bean可能存在很多别名,所以只要有一个别名相同,就认为是能够匹配上的  具体参考AbstractBeanFactory#getAliases方法
      //descriptor.getDependencyName() 这个特别需要注意的是:如果是字段,这里调用的this.field.getName() 直接用的是字段的名称
      // 因此此处我们看到的情况是,我们采用@Autowired虽然匹配到两个类型的Bean了,即使我们没有使用@Qualifier注解,也会根据字段名找到一个合适的(若没找到,就抱错了)
      if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
          matchesBeanName(candidateName, descriptor.getDependencyName())) {
        return candidateName;
      }
    }
    return null;
  }
//determinePrimaryCandidate:顾名思义。它是从给定的Bean中看有木有标注了@Primary注解的Bean,有限选择它
  @Nullable
  protected String determinePrimaryCandidate(Map<String, Object> candidates, Class<?> requiredType) {
    String primaryBeanName = null;
    for (Map.Entry<String, Object> entry : candidates.entrySet()) {
      String candidateBeanName = entry.getKey();
      Object beanInstance = entry.getValue();
      // isPrimary就是去看看容器里(包含父容器)对应的Bean定义信息是否有@Primary标注
      if (isPrimary(candidateBeanName, beanInstance)) {
        if (primaryBeanName != null) {
          boolean candidateLocal = containsBeanDefinition(candidateBeanName);
          boolean primaryLocal = containsBeanDefinition(primaryBeanName);
          // 这个相当于如果已经找到了一个@Primary的,然后又找到了一个 那就抛出异常
          // @Primary只能标注到一个同类型的Bean上
          if (candidateLocal && primaryLocal) {
            throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
                "more than one 'primary' bean found among candidates: " + candidates.keySet());
          }
          else if (candidateLocal) {
            primaryBeanName = candidateBeanName;
          }
        }
        // 把找出来的标注了@Primary的Bean的名称返回出去
        else {
          primaryBeanName = candidateBeanName;
        }
      }
    }
    return primaryBeanName;
  } 
// determineHighestPriorityCandidate:从给定的Bean里面筛选出一个优先级最高的
// 什么叫优先级最高呢?主要为了兼容JDK6提供的注解javax.annotation.Priority
  @Nullable
  protected String determineHighestPriorityCandidate(Map<String, Object> candidates, Class<?> requiredType) {
    String highestPriorityBeanName = null;
    Integer highestPriority = null;
    for (Map.Entry<String, Object> entry : candida         tes.entrySet()) {
      String candidateBeanName = entry.getKey();
      Object beanInstance = entry.getValue();
      if (beanInstance != null) {
        //AnnotationAwareOrderComparator#getPriority
        // 这里就是为了兼容JDK6提供的javax.annotation.Priority这个注解,然后做一个优先级排序
        // 注意注意注意:这里并不是@Order,和它木有任何关系~~~
        // 它有的作用像Spring提供的@Primary注解
        Integer candidatePriority = getPriority(beanInstance);
        // 大部分情况下,我们这里都是null,但是需要注意的是,@Primary只能标注一个,这个虽然可以标注多个,但是里面的优先级值,不能出现相同的(强烈建议不要使用~~~~而使用@Primary)
        if (candidatePriority != null) {
          if (highestPriorityBeanName != null) {
            // 如果优先级的值相等,是不允许的,这里需要引起注意,个人建议一般还是使用@Primary吧
            if (candidatePriority.equals(highestPriority)) {
              throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
                  "Multiple beans found with the same priority ('" + highestPriority +
                  "') among candidates: " + candidates.keySet());
            }
            else if (candidatePriority < highestPriority) {
              highestPriorityBeanName = candidateBeanName;
              highestPriority = candidatePriority;
            }
          }
          else {
            highestPriorityBeanName = candidateBeanName;
            highestPriority = candidatePriority;
          }
        }
      }
    }
    return highestPriorityBeanName;
  }





相关文章
|
7天前
|
Java 开发者 Spring
Spring Framework 中的 @Autowired 注解:概念与使用方法
【4月更文挑战第20天】在Spring Framework中,@Autowired 注解是实现依赖注入(Dependency Injection, DI)的一种非常强大的工具。通过使用 @Autowired,开发者可以减少代码中的引用绑定,提高模块间的解耦能力
29 6
|
2天前
|
开发框架 Dart Java
Flutter的核心:Dart语言基础——语法与特性深度解析
【4月更文挑战第26天】Flutter框架背后的Dart语言,以其简洁的语法和独特特性深受开发者喜爱。本文深入解析Dart的语法与特性,如类型推导、动态静态类型系统、统一的类接口、访问权限控制以及并发编程支持。了解并掌握Dart,能助开发者更高效地利用Flutter构建高性能移动应用。
|
2天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
4天前
|
Rust 安全 程序员
Rust vs Go:解析两者的独特特性和适用场景
在讨论 Rust 与 Go 两种编程语言哪种更优秀时,我们将探讨它们在性能、简易性、安全性、功能、规模和并发处理等方面的比较。同时,我们看看它们有什么共同点和根本的差异。现在就来看看这个友好而公平的对比。
|
5天前
|
存储 Java 程序员
SpringBoot之分层解耦以及 IOC&DI的详细解析
SpringBoot之分层解耦以及 IOC&DI的详细解析
8 0
|
11天前
yolo-world 源码解析(六)(2)
yolo-world 源码解析(六)
22 0
|
11天前
yolo-world 源码解析(六)(1)
yolo-world 源码解析(六)
15 0
|
11天前
yolo-world 源码解析(五)(4)
yolo-world 源码解析(五)
22 0
|
3天前
|
存储 Kubernetes Docker
Kubernetes(K8S)集群管理Docker容器(概念篇)
Kubernetes(K8S)集群管理Docker容器(概念篇)
|
3天前
|
存储 Ubuntu 安全
Docker容器常用命令
Docker容器常用命令
15 1

推荐镜像

更多