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这块(虽然与本问题没有必然关联),也是可以加分的(毕竟是面试而非考试~)

相关文章
|
11月前
|
安全 前端开发 Java
《深入理解Spring》:现代Java开发的核心框架
Spring自2003年诞生以来,已成为Java企业级开发的基石,凭借IoC、AOP、声明式编程等核心特性,极大简化了开发复杂度。本系列将深入解析Spring框架核心原理及Spring Boot、Cloud、Security等生态组件,助力开发者构建高效、可扩展的应用体系。(238字)
|
11月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
894 8
|
12月前
|
安全 Java 应用服务中间件
Spring Boot + Java 21:内存减少 60%,启动速度提高 30% — 零代码
通过调整三个JVM和Spring Boot配置开关,无需重写代码即可显著优化Java应用性能:内存减少60%,启动速度提升30%。适用于所有在JVM上运行API的生产团队,低成本实现高效能。
1261 3
|
12月前
|
人工智能 Java API
构建基于Java的AI智能体:使用LangChain4j与Spring AI实现RAG应用
当大模型需要处理私有、实时的数据时,检索增强生成(RAG)技术成为了核心解决方案。本文深入探讨如何在Java生态中构建具备RAG能力的AI智能体。我们将介绍新兴的Spring AI项目与成熟的LangChain4j框架,详细演示如何从零开始构建一个能够查询私有知识库的智能问答系统。内容涵盖文档加载与分块、向量数据库集成、语义检索以及与大模型的最终合成,并提供完整的代码实现,为Java开发者开启构建复杂AI智能体的大门。
6033 58
|
12月前
|
监控 Java 数据库
从零学 Dropwizard:手把手搭轻量 Java 微服务,告别 Spring 臃肿
Dropwizard 整合 Jetty、Jersey 等成熟组件,开箱即用,无需复杂配置。轻量高效,启动快,资源占用少,内置监控、健康检查与安全防护,搭配 Docker 部署便捷,是构建生产级 Java 微服务的极简利器。
1154 117
|
前端开发 Java 开发者
Java新手指南:在Spring MVC中使用查询字符串与参数
通过结合实际的需求和业务逻辑,开发者可以灵活地利用这些机制,为用户提供更丰富而高效的Web应用体验。
294 15
|
Cloud Native Java API
Java Spring框架技术栈选和最新版本及发展史详解(截至2025年8月)-优雅草卓伊凡
Java Spring框架技术栈选和最新版本及发展史详解(截至2025年8月)-优雅草卓伊凡
1828 0
|
Java 测试技术 数据格式
第2章—装配Bean—通过java代码装配bean
通过java代码装配bean ​ 在进行显式装配的时候,有两种选型方案:java和XML配置,这里先介绍java的配置方式. 2.3.1创建配置类 先复习下上一章的配置内容: @Configuration public class SgtPeppe...
1105 0
|
11月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
536 1
|
11月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
471 1