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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 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这块(虽然与本问题没有必然关联),也是可以加分的(毕竟是面试而非考试~)

相关文章
|
2天前
|
存储 人工智能 Java
将 Spring AI 与 LLM 结合使用以生成 Java 测试
AIDocumentLibraryChat 项目通过 GitHub URL 为指定的 Java 类生成测试代码,支持 granite-code 和 deepseek-coder-v2 模型。项目包括控制器、服务和配置,能处理源代码解析、依赖加载及测试代码生成,旨在评估 LLM 对开发测试的支持能力。
9 1
|
1月前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
169 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
1月前
|
XML Java 测试技术
spring复习01,IOC的思想和第一个spring程序helloWorld
Spring框架中IOC(控制反转)的思想和实现,通过一个简单的例子展示了如何通过IOC容器管理对象依赖,从而提高代码的灵活性和可维护性。
spring复习01,IOC的思想和第一个spring程序helloWorld
|
14天前
|
人工智能 缓存 Java
深入解析Spring AI框架:在Java应用中实现智能化交互的关键
【10月更文挑战第12天】Spring AI 是 Spring 框架家族的新成员,旨在满足 Java 应用程序对人工智能集成的需求。它支持自然语言处理、图像识别等多种 AI 技术,并提供与云服务(如 OpenAI、Azure Cognitive Services)及本地模型的无缝集成。通过简单的配置和编码,开发者可轻松实现 AI 功能,同时应对模型切换、数据安全及性能优化等挑战。
|
21天前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
74 9
|
16天前
|
Java
java构造方法时对象初始化,实例化,参数赋值
java构造方法时对象初始化,实例化,参数赋值
26 1
|
18天前
|
存储 开发框架 Java
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
文章详细介绍了Spring、IOC、DI的概念和关系,解释了控制反转(IOC)和依赖注入(DI)的原理,并提供了IOC的代码示例,阐述了Spring框架作为IOC容器的应用。
17 0
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
|
27天前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
33 4
|
5天前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
15 0
|
1月前
|
XML Java 开发者
经典面试---spring IOC容器的核心实现原理
作为一名拥有十年研发经验的工程师,对Spring框架尤其是其IOC(Inversion of Control,控制反转)容器的核心实现原理有着深入的理解。
80 3