Spring 属性填充(上)

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

Spring 属性填充


Spring 属性填充过程,核心就是实现,对 @Value@Autowired@Resource 等属性或者方法修饰注解的依赖进行注入或者说是对于依赖对象的查找和填充过程。


  • @Value@Autowired 的处理类是通过 AutowiredAnnotationBeanPostProcessor 处理的。


  • @Resource 是通过 CommonAnnotationBeanPostProcessor 处理


上述的两个类也是我们本文中所涉及到的核心类。


属性填充的入口方法 DefaultListableBeanFactory#populateBean, 下面我们就从入口方法开始一步步的解析 Spring IOC 对 Bean 属性填充的过程。


populateBean 方法


下面是 populateBean 的流程


(1)InstantiationAwareBeanPostProcessor 处理器 postProcessAfterinstantiation 方法的行, 该方法执行后可以判断是否继续执行默认的属性填充处理。


(2) 根据注入类型(byName/byType),提取依赖的bean, 并且统一存入 PropertyValues


(3) 应用 InstantiationAwareBeanPostProcessor 处理器的 postProcessProperties 方法,在属性填充之间对属性的再次梳理,典型的就行 AutowiredAnnotationBeanPostProcessor 实现 @Autowried 注解的解析。


(4)PropertyValues 中的属性填充到 BeanWrapper 中。

下面是源码中的处理过程


protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    //...
    //在属性被填充前,给 InstantiationAwareBeanPostProcessor 类型的后置处理器一个修改 bean 状态的机会。
    //定义该类型的后置处理器,可以通过实现 InstantiationAwareBeanPostProcessorAdapter 抽象类完成。
  if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof InstantiationAwareBeanPostProcessor) {
        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
        if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
          return;
        }
      }
    }
  }
  // 从Bean定义里面把准备好的值都拿出来~~~
  // 它是个MutablePropertyValues,持有N多个属性的值~~~
  PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
  // 自动注入模式
  int resolvedAutowireMode = mbd.getResolvedAutowireMode();
  if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
    // by_name 是根据属性名称查找 bean
    // by_type 是根据属性类型查找 bean
    // 查找到 bean 之后进行 set 注入
    MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
    // Add property values based on autowire by name if applicable.
    if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
      autowireByName(beanName, mbd, bw, newPvs);
    }
    // Add property values based on autowire by type if applicable.
    if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
      autowireByType(beanName, mbd, bw, newPvs);
    }
    pvs = newPvs;
    // 总结:
    // spring 自动注入通过某个类的 set 方法来查找 bean, byName 就是根据某个 set 方法所对应的属性名去查找 bean
    // byType 就是根据某个 set 方法的参数去找 bean
    // 如果目前采用注解的方式,很少使用到
  }
  // 执行完成 Spring 的自动注入之后,就开始解析 @Autowired . 这里叫做实例化回调
  boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
  boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
  // @Autowired 注解的 AutowiredAnnotationBeanPostProcessor
  // @Resource 注解的 CommonAnnotationBeanPostProcessor
  PropertyDescriptor[] filteredPds = null;
  if (hasInstAwareBpps) {
    if (pvs == null) {
      pvs = mbd.getPropertyValues();
    }
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof InstantiationAwareBeanPostProcessor) {
        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
        // 调用 BeanPostProcessor 分别解析 @Autowired、@Resource、@Value 为属性注入值
        // 此处会从后置处理,从里面把依赖的属性,值都拿到。AutowiredAnnotationBeanPostProcessor就是在此处拿出值的~~~
        PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
        if (pvsToUse == null) {
          if (filteredPds == null) {
            filteredPds =filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
          }
          pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
          if (pvsToUse == null) {
            return;
          }
        }
        pvs = pvsToUse;
      }
    }
  }
  if (needsDepCheck) {
    if (filteredPds == null) {
      filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
    }
    checkDependencies(beanName, mbd, filteredPds, pvs);
  }
  if (pvs != null) {
    // 完成 pvs 赋值的运用
    applyPropertyValues(beanName, mbd, bw, pvs);
  }
}


autowireByName


通过方法 beanName 来实现自动注入,因为在 Spring IOC 中beanName是唯一的, 所以这样的效率是最高的,复杂度最低,代码如下。


protected void autowireByName(
    String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
  //获取非简单类型属性的名称,且该属性未被配置在配置文件中
  String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
  for (String propertyName : propertyNames) {
    if (containsBean(propertyName)) {
      // 根据属性名称去查找 bean, 这个就是 byName
      Object bean = getBean(propertyName);
      // 给属性赋值
      pvs.add(propertyName, bean);
      registerDependentBean(propertyName, beanName);
      //.... print log
    }
    else {
      //.... print log
    }
  }
}


autowireByType


通过类型来获取 Bean 进行注入,由于在 Spring IOC 中,同一个类型的 Bean 可以存在多个,对比 autowireByName 处理要复杂一些。


protected void autowireByType(
    String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
  TypeConverter converter = getCustomTypeConverter();
  if (converter == null) {
    converter = bw;
  }
  Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
  //查找到有对应 set 方法的属性
  String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
  for (String propertyName : propertyNames) {
    try {
      // 描述的是属性
      PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
      // Don't try autowiring by type for type Object: never makes sense,
      // even if it technically is a unsatisfied, non-simple property.
      if (Object.class != pd.getPropertyType()) {
        // set 方法中的参数信息
        MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
        // Do not allow eager init for type matching in case of a prioritized post-processor.
        // 当前 bean 是否实现了 PriorityOrdered
        boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
        DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
        // 根据 bean 类型找 bean, 这里是 byType
        Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
        if (autowiredArgument != null) {
          pvs.add(propertyName, autowiredArgument);
        }
        for (String autowiredBeanName : autowiredBeanNames) {
          registerDependentBean(autowiredBeanName, beanName);
          if (logger.isTraceEnabled()) {
            logger.trace("Autowiring by type from bean name '" + beanName + "' via property '" +
                propertyName + "' to bean named '" + autowiredBeanName + "'");
          }
        }
        autowiredBeanNames.clear();
      }
    }
    catch (BeansException ex) {
      throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
    }
  }
}


通过上面的代码我们可以看到在这里的它的核心方法也就是 resolveDependency


resolveDependency


再 resolveDependency 方法中首先判断了依赖的类型, 然后在做对应的处理, 核心的方法是 doResolveDependency


public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
                @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
  // DependencyDescriptor 表示一个依赖,可以是一个属性字段,可能是一个构造方法参数,可能是一个 set 参数
  // 根据 descriptor 去 BeanFactory 中找到 bean
  descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
  // 如果是 Optional
  if (Optional.class == descriptor.getDependencyType()) {
    return createOptionalDependency(descriptor, requestingBeanName);
  } else if (ObjectFactory.class == descriptor.getDependencyType() ||
      ObjectProvider.class == descriptor.getDependencyType()) {
    return new DependencyObjectProvider(descriptor, requestingBeanName);
  } else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
    return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
  } else {
    // 在使用 @Autowired 注解时,也可以使用 @Lazy 注解,到时候注入的会是一个代理对象
    Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
        descriptor, requestingBeanName);
    if (result == null) {
      // 通过解析 descriptor 找到 bean 对象
      result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
    }
    return result;
  }
}


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