Spring IoC是如何使用BeanWrapper和Java内省结合起来给Bean属性赋值的【享学Spring】(下)

简介: Spring IoC是如何使用BeanWrapper和Java内省结合起来给Bean属性赋值的【享学Spring】(下)

关于BeanDefinitionValueResolver此处我补充一点:关于占位符的使用,形如我们可以这样配置:


<bean id="myPerson" class="com.fsx.bean.Person">
    <property name="name" value="${diy.name}"/>
    <property name="age" value="18"/>
</bean>


此处我想补充的是对${diy.name}这个占位符的解析时机:它并不发生在BeanDefinitionValueResolver里。

我们知道它的resolveValueIfNecessary方法能解析各式各样的值,此处就补充说明它对string值的处理:


// @since 1.2
class BeanDefinitionValueResolver {
  @Nullable
  public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
    ...
    // 处理String类型的值:处理SpEL表达式,但是它并不处理占位符~~~
    else if (value instanceof TypedStringValue) {
      TypedStringValue typedStringValue = (TypedStringValue) value;
      // 此处evaluate最终调用的是我们熟悉的this.beanFactory.evaluateBeanDefinitionString(value, this.beanDefinition)方法
      // 而它底层调用是this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
      Object valueObject = evaluate(typedStringValue);
    }
  }
}


由此可见此类最终做的是this.beanExpressionResolver.evaluate()这个计算处理,它处理的是SpEL表达式,此处并未处理占位符。

但是呢,我们知道Spring这样配置是可行的,所以我们有理由去相信:处理占位符操作发生在这个步骤之前(这个步骤是什么?答:给属性赋值阶段,已经属于容器初始化非常靠后的阶段了~)


至于此部分的占位符在哪解析的,其实之前是分析过步骤的,那么此处我也只给出步骤提示吧:


  1. Spring处理属性占位符依赖于PropertyResourceConfigurer来处理的~1. PropertyResourceConfigurer是个BeanFactoryPostProcessor,它会在Bean工厂完成后,借助BeanDefinitionVisitor对所有的BeanDefinition进行visitBeanDefinition()处理(这里会调用valueResolver.resolveStringValue(strVal)进行占位符的处理~~)2. 包括xml配置的ParentName、BeanClassName、FactoryBeanName、FactoryMethodName、Scope、构造函数类型。。。等等都是能够通过属性文件来配置的3. 大名鼎鼎的实现类:PropertyPlaceholderConfigurer和PropertySourcesPlaceholderConfigurer大家应该非常熟悉~
  2. 言外之意:若你没向容器内注入PropertyResourceConfigurer这个Bean,xml里是使用占位符是不生效的哟~~~(SpringBoot默认给配置的是PropertySourcesPlaceholderConfigurer)


处理xml中Bean定义的占位符的核心方法是这:


  public void visitBeanDefinition(BeanDefinition beanDefinition) {
    visitParentName(beanDefinition);
    visitBeanClassName(beanDefinition);
    visitFactoryBeanName(beanDefinition);
    visitFactoryMethodName(beanDefinition);
    visitScope(beanDefinition);
    // 这里 它会处理所有的配置的Bean定义的占位符、数组、List等等
    if (beanDefinition.hasPropertyValues()) {
      visitPropertyValues(beanDefinition.getPropertyValues());
    }
    if (beanDefinition.hasConstructorArgumentValues()) {
      ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
      visitIndexedArgumentValues(cas.getIndexedArgumentValues());
      visitGenericArgumentValues(cas.getGenericArgumentValues());
    }
  }
  // 显然,重点在resolveValue这个方法~~~
  protected void visitPropertyValues(MutablePropertyValues pvs) {
    PropertyValue[] pvArray = pvs.getPropertyValues();
    for (PropertyValue pv : pvArray) {
      Object newVal = resolveValue(pv.getValue());
      if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {
        pvs.add(pv.getName(), newVal);
      }
    }
  }
  @Nullable
  protected Object resolveValue(@Nullable Object value) {
    if (value instanceof BeanDefinition) {
      visitBeanDefinition((BeanDefinition) value);
    }
    ...
    else if (value instanceof TypedStringValue) {
      ...
      // 处理占位符~~~
      String visitedString = resolveStringValue(stringValue);
      ...
    }
  }


说明:PropertyResourceConfigurer不仅能够解析处理Bean定义的占位符,还能够loadProperties(result);加载属性文件的~

具体可参考:【小家Spring】详解PropertyPlaceholderConfigurer、PropertyOverrideConfigurer等对属性配置文件Properties的加载和使用


从命名中就能看出,它处理BeanDefinition的各式各样的情况,它主要是在xml配置时代起到了非常大的作用,形如这样:


<bean class="foo.bar.xxx">
    <property name="referBeanName" ref="otherBeanName" />
</bean>


因为我们知道在xml时代配置Bean非常的灵活:引用Bean、Map、List甚至支持SpEL等等,这一切权得益于BeanDefinitionValueResolver这个类来处理各种case~


其实在现在注解大行其道的今天,配置Bean我们大都使用@Bean来配置,它是一种工厂方法的实现,因此这个处理类的作用就被弱化了很多。但是,但是,但是,它仍旧是我们实施定制化BeanDefinition的一个有力武器~


applyPropertyValues()这一步完成之后,就彻底完成了对Bean实例属性的赋值。从中可以看到最终的赋值操作,核心依赖的就是这么一句话:


bw.setPropertyValues(new MutablePropertyValues(deepCopy))


并且从转换的逻辑我们也需要知道的是:IoC并不是100%得使用BeanWrapper的,若我们是自定义了一个转换器,其实是可以不经过Java内省机制,而是直接通过反射来实现的,当然并不建议这么去做~


总结


BeanWrapper体系相比于 Spring 中其他体系是比较简单的,它作为BeanDefinition向 Bean转换过程中的中间产物,承载了 bean 实例的包装、类型转换、属性的设置以及访问等重要作用(请不要落了访问这个重要能力)。


关于此面试题怎么去回答,如果是我主考我会这么评价回答:


  1. 能答到populateBean()这里算是对这块知识入门了
  2. 能答到applyPropertyValues()这里,那基本对此回答就比较满意了
  3. 当然若能答到:通过自定义实现一个转换器+反射实现作为实现,而绕过Java内省机制。那势必就可以加分了~1. 若达到自定义、个性化定义BeanDefinition这块(虽然与本问题没有必然关联),也是可以加分的(毕竟是面试而非考试~)

相关文章
|
7月前
|
XML Java 测试技术
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
490 26
|
6月前
|
Java 测试技术 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
本课主要讲解Spring Boot项目中的属性配置方法。在实际开发中,测试与生产环境的配置往往不同,因此不应将配置信息硬编码在代码中,而应使用配置文件管理,如`application.yml`。例如,在微服务架构下,可通过配置文件设置调用其他服务的地址(如订单服务端口8002),并利用`@Value`注解在代码中读取这些配置值。这种方式使项目更灵活,便于后续修改和维护。
90 0
|
6月前
|
Java 数据库 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——指定项目配置文件
在实际项目中,开发环境和生产环境的配置往往不同。为简化配置切换,可通过创建 `application-dev.yml` 和 `application-pro.yml` 分别管理开发与生产环境配置,如设置不同端口(8001/8002)。在 `application.yml` 中使用 `spring.profiles.active` 指定加载的配置文件,实现环境快速切换。本节还介绍了通过配置类读取参数的方法,适用于微服务场景,提升代码可维护性。课程源码可从 [Gitee](https://gitee.com/eson15/springboot_study) 下载。
223 0
|
6月前
|
Java 微服务 Spring
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
在微服务架构中,随着业务复杂度增加,项目可能需要调用多个微服务。为避免使用`@Value`注解逐一引入配置的繁琐,可通过定义配置类(如`MicroServiceUrl`)并结合`@ConfigurationProperties`注解实现批量管理。此方法需在配置文件中设置微服务地址(如订单、用户、购物车服务),并通过`@Component`将配置类纳入Spring容器。最后,在Controller中通过`@Resource`注入配置类即可便捷使用,提升代码可维护性。
96 0
|
9月前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
181 12
|
9月前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
216 12
|
Java Spring 容器
Spring 源码解析 | populateBean 属性填充(上)
本文主要是讲 Spring 容器的启动过程主要是以题干的方式进行可能不回涉及过多的细节,希望大家阅读本文能对 Spring 源码有一个简单的了解。 环境介绍: Spring 5.3.10 Jdk 11
287 0
Spring 源码解析 | populateBean 属性填充(上)
|
Java Spring 容器
Spring 源码解析 | populateBean 属性填充(下)
本文主要是讲 Spring 容器的启动过程主要是以题干的方式进行可能不回涉及过多的细节,希望大家阅读本文能对 Spring 源码有一个简单的了解。 环境介绍: Spring 5.3.10 Jdk 11
222 0
|
Java 数据库连接 Spring
Spring 属性填充(下)
Spring 的属性填充主要是在 Bean 被创建后,通过 populateBean 方法来完成对象属性赋值以逐步完成 Bean 的初始化工作。
264 0
|
Java Spring
Spring 属性填充(上)
Spring 的属性填充主要是在 Bean 被创建后,通过 populateBean 方法来完成对象属性赋值以逐步完成 Bean 的初始化工作。
207 0

热门文章

最新文章