Spring 属性填充(下)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: Spring 的属性填充主要是在 Bean 被创建后,通过 populateBean 方法来完成对象属性赋值以逐步完成 Bean 的初始化工作。

我们再来到 doResolveDependency 方法


public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
                  @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
  InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
  try {
    // 如果 DependencyDescriptor 是一个 ShortcutDependencyDescriptor
    // 那么会直接理解 beanName 从 beanFactory 中拿到一个 bean
    // 在利用 @Autowired 注解来进行依赖注入时会利用 ShortcutDependencyDescriptor 来进行依赖注入的缓存
    // 表示当解析完成某个依赖信息后,会把依赖的 bean 的 beanName 缓存起来
    Object shortcut = descriptor.resolveShortcut(this);
    if (shortcut != null) {
      return shortcut;
    }
    // 获取 descriptor 的具体赖姓
    Class<?> type = descriptor.getDependencyType();
    // 1. 获取 @Value 注解中所配置的值
    Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); //判断是否通过 @Value 指定了
    if (value != null) {
      if (value instanceof String) {
        // 先进行占位符的填充,解析 "$" 符号
        String strVal = resolveEmbeddedValue((String) value);
        BeanDefinition bd = (beanName != null && containsBean(beanName) ?
            getMergedBeanDefinition(beanName) : null);
        // 解析 Spring EL 表达式, 解析 "#" 符号(可以是运算,也可以是某个 bean 的名字)
        value = evaluateBeanDefinitionString(strVal, bd);
      }
      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()));
      }
    }
    //没有使用 @Value 注解
    //2. 要注入的类型不是一个 Map,Array, Collection
    Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
    if (multipleBeans != null) {
      return multipleBeans;
    }
    //通过 Type 查找,可能找到多个, 这里的 value ,可能是具体的实例对象, 也能暂时只是 Class 对象
    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;
    // 根据 type 查找到了多个
    if (matchingBeans.size() > 1) {
      // 如果找到了多个,去尝试确定出唯一的一个
      autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
      if (autowiredBeanName == null) {
        if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
          //如果找到多个,并且依赖是 required , 或者不是数组或者 Collection 或者 Map
          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) {
      // 调用 beanFactory.getbean() 创建 bean 对象
      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);
  }
}


上面有我们的常用注入 @Value, @Autowired 的处理,当我们同一个类型的 Bean 查找到多个结果的时候,我们来看如何选择的 determineAutowireCandidate 方法的实现。


protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
    Class<?> requiredType = descriptor.getDependencyType();
    // 取 @Primary 的 bean
    String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
    if (primaryCandidate != null) {
      return primaryCandidate;
    }
    // 取优先级最高的 Bean 通过 @Priority 来定义优先级,数字越小,优先级越高
    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();
      if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
          // 根据属性名确定
          matchesBeanName(candidateName, descriptor.getDependencyName())) {
        return candidateName;
      }
    }
    return null;
  }


doResolveDependency 总结


(1) 首先调用 DefaultListableBeanFactory#doResolveDependency 方法中通过属性来获取对应的需要注入的 Bean 。


(2) 第一步解析 @Value 注解, 如果存在就解析 el 表达式获取 Bean 。


(3)  第二步解析 @Autowired 自动注入注解,如果存在首先就去解析这个注解。

(4) 通过依赖的类型去查找所有符合条件的候选 Bean。


(5)  当符合条件的 Bean 的个数为 0 的时候,判断 @Autowired 的 required 属性是否为 true, 如果是 true  那么就抛异常,提示找不到 Bean。


(6) 如果匹配到1个的时候,我们就直接使用。


(7) 如果匹配到多个的时候, 我们就需要做一下的选择:


首先遍历所有的候选 beanName, 判断是否有 @Primary 注解,如果有一个就返回,如果有多个会抛出异常 NoUniqueBeanDefinitionException

然后,遍历所有的候选 beanName, 判断是否有 @Priority 注解,如果有就按照自然顺序排序返回第一个,如果存在多个 Bean 优先级相同则抛出异常 NoUniqueBeanDefinitionException( 注意:这里容易混淆 @Order 注解);

最后,遍历所有的候选 beanName,如果上述两种情况都不能选择到匹配的,那么将读取参数名通过 byName 的方式来匹配 Bean。


(8)  把所有的字段和需要注入的到 Bean 的属性之后完成属性的注入。


(9) 最后,若存在属性pvs,那就做赋值操作


applyPropertyValues


该方法主要是实现 pvs 的值的运用


(1) 检测属性值列表是否已转换过的,若转换过,则直接填充属性,无需再次转换

(2) 遍历属性值列表 pvs,解析原始值 originalValue,得到解析值 resolvedValue


(3) 对解析后的属性值 resolvedValue 进行类型转换


(4) 将类型转换后的属性值设置到 PropertyValue 对象中,并将 PropertyValue 对象存入 deepCopy 集合中将 deepCopy 中的属性信息注入到 bean 对象中


// 本方法传入了beanName和bean定义信息,以及它对应的BeanWrapper和value值
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
  if (pvs.isEmpty()) {
    return;
  }
  if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
    ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
  }
  MutablePropertyValues mpvs = null;
  List<PropertyValue> original;
  // 判断是否是 类型 MutablePropertyValues
  if (pvs instanceof MutablePropertyValues) {
    mpvs = (MutablePropertyValues) pvs;
    // 若该mpvs中的所有属性值都已经转换为对应的类型,则把mpvs设置到BeanWrapper中,返回
    if (mpvs.isConverted()) {
      // Shortcut: use the pre-converted values as-is.
      try {
        bw.setPropertyValues(mpvs);
        return;
      }
      catch (BeansException ex) {
        throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Error setting property values", ex);
      }
    }
    // 否则,拿到里面的属性值
    original = mpvs.getPropertyValueList();
  }
  else {
    original = Arrays.asList(pvs.getPropertyValues());
  }
  // 若调用者没有自定义转换器,那就使用BeanWrapper本身(因为BeanWrapper实现了TypeConverter 接口)
  TypeConverter converter = getCustomTypeConverter();
  if (converter == null) {
    converter = bw;
  }
  // 获取BeanDefinitionValueResolver,该Bean用于将bean定义对象中包含的值解析为应用于目标bean实例的实际值。
  BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
  // Create a deep copy, resolving any references for values.
  List<PropertyValue> deepCopy = new ArrayList<>(original.size());
  boolean resolveNecessary = false;
  // 遍历没有被解析的original属性值
  for (PropertyValue pv : original) {
    if (pv.isConverted()) {
      deepCopy.add(pv);
    }
    else {// 被解析过的PropertyValue此处会一步步解析
      String propertyName = pv.getName();
      Object originalValue = pv.getValue();
      // 解析各式各样的值
      Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
      Object convertedValue = resolvedValue;
      // 属性可写 并且 不是嵌套(如foo.bar,java中用getFoo().getBar()表示)或者索引(如person.addresses[0])属性
      boolean convertible = bw.isWritableProperty(propertyName) &&
          !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
      if (convertible) {
        // 用类型转换器进行转换
        convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
      }
      // Possibly store converted value in merged bean definition,
      // in order to avoid re-conversion for every created bean instance.
      if (resolvedValue == originalValue) {
        if (convertible) {
          pv.setConvertedValue(convertedValue);
        }
        deepCopy.add(pv);
      }
      else if (convertible && originalValue instanceof TypedStringValue &&
          !((TypedStringValue) originalValue).isDynamic() &&
          !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
        pv.setConvertedValue(convertedValue);
        deepCopy.add(pv);
      }
      else {
        resolveNecessary = true;
        deepCopy.add(new PropertyValue(pv, convertedValue));
      }
    }
  }
  // 标记mpvs已经转换
  if (mpvs != null && !resolveNecessary) {
    mpvs.setConverted();
  }
  // Set our (possibly massaged) deep copy.
  // 使用转换后的值进行填充
  try {
    // 对属性的值进行注入
    bw.setPropertyValues(new MutablePropertyValues(deepCopy));
  }
  catch (BeansException ex) {
    throw new BeanCreationException(
        mbd.getResourceDescription(), beanName, "Error setting property values", ex);
  }
}


Spring 源码解析











相关文章
|
7月前
|
存储 Java 数据安全/隐私保护
|
4月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
5月前
|
Java 测试技术 数据库
Spring Boot中的项目属性配置
本节课主要讲解了 Spring Boot 中如何在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。除此之外,例如数据库相关的连接参数等等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后介绍了开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。
|
6月前
|
XML druid Java
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
72 0
|
4月前
|
Java Spring 开发者
Spring 框架配置属性绑定大比拼:@Value 与 @ConfigurationProperties,谁才是真正的王者?
【8月更文挑战第31天】Spring 框架提供 `@Value` 和 `@ConfigurationProperties` 两种配置属性绑定方式。`@Value` 简单直接,适用于简单场景,但处理复杂配置时略显不足。`@ConfigurationProperties` 则以类级别绑定配置,简化代码并更好组织配置信息。本文通过示例对比两者特点,帮助开发者根据具体需求选择合适的绑定方式,实现高效且易维护的配置管理。
67 0
|
4月前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
56 0
|
6月前
|
运维 Java 测试技术
Spring运维之boo项目表现层测试加载测试的专用配置属性以及在JUnit中启动web服务器发送虚拟请求
Spring运维之boo项目表现层测试加载测试的专用配置属性以及在JUnit中启动web服务器发送虚拟请求
56 3
|
6月前
|
运维 Java 关系型数据库
Spring运维之boot项目bean属性的绑定读取与校验
Spring运维之boot项目bean属性的绑定读取与校验
60 2
|
6月前
|
XML 运维 Java
Spring运维之boot项目打包jar和插件运行并且设置启动时临时属性和自定义配置文件
Spring运维之boot项目打包jar和插件运行并且设置启动时临时属性和自定义配置文件
59 1
|
7月前
|
Java Apache Spring
Spring BeanUtils与Apache BeanUtils提供基本属性复制,适用于简单需求
【5月更文挑战第4天】Spring BeanUtils与Apache BeanUtils提供基本属性复制,适用于简单需求;Cglib BeanCopier用于转换为Cglib代理对象;Apache PropertyUtils处理属性操作;Dozer支持复杂对象映射。选择工具取决于具体需求,如需精细控制或对象映射,推荐Dozer或Apache PropertyUtils。Apache BeanUtils可能因潜在的封装性破坏被禁用。
76 3