浅析 Spring 依赖解析实现

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 背景接上篇《Spring 依赖注入的方式,你了解哪些?》,上篇介绍了 Spring 包括 setter、字段、构造器、方法、接口回调等依赖注入的几种方式以及依赖注入的来源,更多的是停留在认识或者使用层面。这篇从源码的角度进行分析,Spring 在依赖注入的过程中如何解析依赖。

背景


接上篇《Spring 依赖注入的方式,你了解哪些?》,上篇介绍了 Spring 包括 setter、字段、构造器、方法、接口回调等依赖注入的几种方式以及依赖注入的来源,更多的是停留在认识或者使用层面。这篇从源码的角度进行分析,Spring 在依赖注入的过程中如何解析依赖。


在《Spring 中 @Autowired 和 @Resource 有什么区别?》 一文中,我们有提到,Spring 在依赖注入时会先将 @Autowired 注解以及 @Resource 注解标注的对象解析为依赖描述符 DependencyDescriptor,然后调用AutowireCapableBeanFactory#resolveDependency(DependencyDescriptor,String,Set<String>, TypeConverter)解析依赖,最后通过反射设置字段或调用方法达到依赖注入的目的。我们下面就从 resolveDependency 方法进行分析 Spring 如何实现依赖解析。


依赖解析实现分析


Spring 对 BeanFactory 底层的最终实现是 DefaultListableBeanFactory,DefaultListableBeanFactory 对 resolveDependency 方法实现的源码如下。


  /**
   * @param descriptor         字段或方法的依赖描述信息
   * @param requestingBeanName 需要注入依赖的 bean 的名称
   * @param autowiredBeanNames 需要注入的依赖的 bean 名称的集合,解析的结果会存放该集合中
   * @param typeConverter      类型转换
   * @return
   * @throws BeansException
   */
  @Override
  @Nullable
  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()) {
      // 解析 Optional 依赖
      return createOptionalDependency(descriptor, requestingBeanName);
    } else if (ObjectFactory.class == descriptor.getDependencyType() ||
        ObjectProvider.class == descriptor.getDependencyType()) {
      // 解析 ObjectFactory 或 ObjectProvider 依赖
      return new DependencyObjectProvider(descriptor, requestingBeanName);
    } else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
      // 解析 javax.inject.Provider 注解标注的依赖
      return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
    } else {
      // 懒加载的对象返回代理对象
      Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
          descriptor, requestingBeanName);
      if (result == null) {
        // 其他对象进行解析
        result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
      }
      return result;
    }
  }


解析依赖的方法,根据不同的依赖类型使用不同的解析方式。对于 Optional、ObjectFactory、ObjectProvider 依赖类型,由于需要解析的是其泛型的实际类型,因此 Spring 会将依赖描述信息重新包装为另一个 DependencyDescriptor ,然后再解析。而 Java 规范 JSR-330 中注解 javax.inject.Provider 的处理则只是将实现委托给另一个类处理。对于不同类型的依赖解析,最终都会调用 doResolveDependency 方法。以 Optional 类型的解析方法 createOptionalDependency 为例,源码如下。


  private Optional<?> createOptionalDependency(
      DependencyDescriptor descriptor, @Nullable String beanName, final Object... args) {
    // 包装依赖描述信息,解析依赖的类型时会将嵌套层次加1
    // 例如原来要解析的类型是 Optional<T> 的原始类型,嵌套层次加1后会解析泛型类型的实际类型 T
    DependencyDescriptor descriptorToUse = new NestedDependencyDescriptor(descriptor) {
      @Override
      public boolean isRequired() {
        return false;
      }
      @Override
      public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) {
        return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, args) :
            super.resolveCandidate(beanName, requiredType, beanFactory));
      }
    };
    // 然后真正解析依赖
    Object result = doResolveDependency(descriptorToUse, beanName, null, null);
    // 再将解析出的依赖包装到 Optional 中
    return (result instanceof Optional ? (Optional<?>) result : Optional.ofNullable(result));
  }


跟踪真正解析依赖的方法 doResolveDependency,源码如下。


  public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    ... 省略部分代码
      // 快捷解析依赖
      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);
          value = evaluateBeanDefinitionString(strVal, bd);
        }
        TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
        try {
          // 将结果进行类型转换
          return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
        } catch (UnsupportedOperationException ex) {
          ... 省略部分代码
        }
      }
      // 尝试解析包含多个 bean 的依赖对象
      Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
      if (multipleBeans != null) {
        return multipleBeans;
      }
      // 查找所需类型的依赖
      Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
      if (matchingBeans.isEmpty()) {
        if (isRequired(descriptor)) {
          // 依赖不存在,抛出异常
          raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
        }
        return null;
      }
      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 {
            // 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();
      }
      if (autowiredBeanNames != null) {
        autowiredBeanNames.add(autowiredBeanName);
      }
      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;
  }


真正解析依赖的方法 doResolveDependency 代码稍长,上面省略了部分代码。我们可以看出解析流程如下。


优先尝试根据依赖描述符快捷进行解析,默认返回 null,实现类 ShortcutDependencyDescriptor 会根据依赖的名称和类型从 BeanFactory 中获取 bean。

如果快捷解析失败,则尝试解析 @Value 标注的字段,会对 @Value 注解的 value 值中的占位符进行解析,以及解析表达式,最后还会把解析出的对象进行类型转换。

如果 @Value 注解也解析失败,则会尝试解析包含多个 bean 对象的依赖类型,如集合、数组、Map 等。

如果依赖的是单一类型,会先根据类型查找除所有的依赖,如果依赖不存在并且依赖是必须的则会抛出异常。

对于单一类型,如果只查找到一个依赖,那么直接返回即可,否则需要根据 Primary、优先级等选择一个最佳的依赖。

对于集合类型以及单一类型,最终都会调用 findAutowireCandidates 方法查找自动装配的候选对象,对于集合类型会把查找到的候选对象包装为对应所需的类型,对于单一类型则需要确认最终使用哪个依赖。跟踪查找依赖候选对象的方法 findAutowireCandidates ,源码如下。


  protected Map<String, Object> findAutowireCandidates(
      @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
    // 查找 Spring 中给定类型的 bean 名称
    String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
        this, requiredType, true, descriptor.isEager());
    Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
    // 先解析 Spring 中游离的对象,如果类型和所需类型匹配则加入到结果中
    for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
      Class<?> autowiringType = classObjectEntry.getKey();
      if (autowiringType.isAssignableFrom(requiredType)) {
        Object autowiringValue = classObjectEntry.getValue();
        autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
        if (requiredType.isInstance(autowiringValue)) {
          result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
          break;
        }
      }
    }
    // 然后解析 Spring 中注册 BeanDefinition 中的名称
    for (String candidate : candidateNames) {
      if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
        // 依赖非 bean 自身,并且 bean 可以作为候选项,加入到结果中
        addCandidateEntry(result, candidate, descriptor, requiredType);
      }
    }
    ... 省略回退处理代码,和上述候选名称循环类似
    return result;
  }


候选对象的获取主要有两个来源,一个是游离对象,一个是 Spring 中的 bean,Spring 根据类型获取到对应的实例后添加到返回结果。这也印证了前面文章所说的依赖注入的依赖来源。需要留意的是对 isAutowireCandidate 方法的调用,isAutowireCandidate 方法用于判断一个一个对象是否可以作为候选项,其内部出了判断 bean 定义中是否可以作为候选项的标识,还会判断泛型、@Qualifier 等。关于 @Qualifier 的处理,我们后面再开一篇进行分析。


对于单一类型的依赖解析,如果查找到了多个 bean,则需要判断到底使用哪一个 bean 作为结果,跟踪 determineAutowireCandidate 方法的调用,其源码如下。


  protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
    Class<?> requiredType = descriptor.getDependencyType();
    // 先根据 primary 判断
    String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
    if (primaryCandidate != null) {
      return primaryCandidate;
    }
    // primary 不存在,则根据优先级判断
    String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
    if (priorityCandidate != null) {
      return priorityCandidate;
    }
    // Fallback
    for (Map.Entry<String, Object> entry : candidates.entrySet()) {
      String candidateName = entry.getKey();
      Object beanInstance = entry.getValue();
      // 依赖为游离对象或指定依赖的 bean 名称匹配,直接返回
      if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
          matchesBeanName(candidateName, descriptor.getDependencyName())) {
        return candidateName;
      }
    }
    return null;
  }


这里优先选择标记 primary 为 true 的 bean,如果都没有标注则会选择一个优先级最高的 bean,如果存在多个优先级相同的 bean 会抛出异常。最后会将游离对象或和依赖的名称匹配的 bean 作为结果进行返回。


总结

Spring 依赖解析会优先根据 @Value 注解进行解析,并且支持多种类型。对于类型为 Optional、ObjectFactory 的依赖 Spring 会将依赖包装后返回,其他类型又分为集合类型和单一类型,对于集合类型 Spring 查找对所有依赖后直接包装为对应的类型返回即可,对于单一类型 Spring 会优先考虑 primary、最高优先级、和所需 bean 名称匹配的 bean。


目录
相关文章
|
9天前
|
传感器 监控 安全
智慧工地云平台的技术架构解析:微服务+Spring Cloud如何支撑海量数据?
慧工地解决方案依托AI、物联网和BIM技术,实现对施工现场的全方位、立体化管理。通过规范施工、减少安全隐患、节省人力、降低运营成本,提升工地管理的安全性、效率和精益度。该方案适用于大型建筑、基础设施、房地产开发等场景,具备微服务架构、大数据与AI分析、物联网设备联网、多端协同等创新点,推动建筑行业向数字化、智能化转型。未来将融合5G、区块链等技术,助力智慧城市建设。
|
21天前
|
XML Java 开发者
Spring底层架构核心概念解析
理解 Spring 框架的核心概念对于开发和维护 Spring 应用程序至关重要。IOC 和 AOP 是其两个关键特性,通过依赖注入和面向切面编程实现了高效的模块化和松耦合设计。Spring 容器管理着 Beans 的生命周期和配置,而核心模块为各种应用场景提供了丰富的功能支持。通过全面掌握这些核心概念,开发者可以更加高效地利用 Spring 框架开发企业级应用。
73 18
|
2月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
2月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
74 2
|
3月前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
234 2
|
3月前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
84 2
|
3月前
|
前端开发 Java 开发者
Spring MVC中的控制器:@Controller注解全解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序控制层的核心。它不仅简化了控制器的定义,还提供了灵活的请求映射和处理机制。本文将深入探讨`@Controller`注解的用法、特点以及在实际开发中的应用。
168 0
|
3月前
|
前端开发 Java Maven
深入解析:如何用 Spring Boot 实现分页和排序
深入解析:如何用 Spring Boot 实现分页和排序
148 2
|
3月前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
72 4
|
3月前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
105 8

热门文章

最新文章

推荐镜像

更多