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

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

前言


书写此篇博文的缘由是出自一道面试题:面试题目大概如标题所述。

我个人认为这道面试题问得是非常有水平的,因为它涉及到的知识点既有深度,又有广度,可谓一举两得~~~因此在这里分享给大家。


为了给此文做铺垫,前面已经有两篇文章分别叙述了Java内省和BeanWrapper,而且还分析了底层接口:属性访问器(PropertyAccessor)。若对此部分还不是很了解的话,建议可以先出门左拐或者单击【相关阅读】里的链接~


Spring IoC和Java内省的依赖关系说明


Spring需要依赖注入就需要使用BeanWrapper,上章节说了BeanWrapperImpl的实现大都委托给了CachedIntrospectionResults去完成,而CachedIntrospectionResults它的核心说法就是Java内省机制。


从层层委托的依赖关系可以看出,Spring IoC的依赖注入(给属性赋值)是层层委托的最终给了Java内省机制,这是Spring框架设计精妙处之一。这也符合我上文所诉:BeanWrapper这个接口并不建议应用自己去直接使用~~~

那么本文就着眼于此,结合源码去分析Spring IoC容器它使用BeanWrapper完成属性赋值(依赖注入)之精华~


Spring IoC中使用BeanWrapper源码分析


Spring IoC我相信小伙伴并不陌生了,但IoC的细节不是本文的重点。为了便于分析,我把这个过程画一个时序图描述如下:


image.png


有了这个简略的时序图,接下来就一步一步的分析吧

doCreateBean() 创建Bean


任何创建Bean的过程,都得经历doCreateBean()。这句代码我们已经非常熟悉了,它在AbstractAutowireCapableBeanFactory里:


  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
    BeanWrapper instanceWrapper = null;
    ...
    // 这一步简单的说:通过构造函数实例化Bean后,new BeanWrapperImpl(beanInstance)包装起来
    // 并且:initBeanWrapper(bw);  作用是注册ConversionService和registerCustomEditors() ...
    instanceWrapper = createBeanInstance(beanName, mbd, args);
    ...
    // 给属性赋值:此处会实施BeanWrapper的真正实力~~~~
    // 注意:此处第三个参数传入的是BeanWrapper,而不是源生beanduixiang~~~ 
    populateBean(beanName, mbd, instanceWrapper);
    exposedObject = initializeBean(beanName, exposedObject, mbd);
    ...
  }


doCreateBean这个方法完成整个Bean的实例化、初始化。而这里面我们最为关注的自然就是populateBean()这个方法,它的作用是完成给属性赋值,从时序图中也可以看出这是一个入口


populateBean():给Bean的属性赋值~


  protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    ...
    // 从Bean定义里面把准备好的值都拿出来~~~
    // 它是个MutablePropertyValues,持有N多个属性的值~~~
    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
    ...
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
      ...
      // 此处会从后置处理,从里面把依赖的属性,值都拿到。比如大名鼎鼎的AutowiredAnnotationBeanPostProcessor就是在此处拿出值的~~~
      PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
      ...
      pvs = pvsToUse;
    }
    ...
    // 若存在属性pvs ,那就做赋值操作吧~~~(本处才是今天关心的重点~~~)
    if (pvs != null) {
      applyPropertyValues(beanName, mbd, bw, pvs);
    }
  }


深入到方法内部,它完成了k-v值的准备工作,很多重要的BeanPostProcessor也在此处得到执行。对于最终给属性赋值的步骤,是交给了本类的applyPropertyValues()方法去完成~~~


其实到了此处,理论上小伙伴就应该就能猜到接下来的核心下文了~


applyPropertyValues():完成属性赋值


这个方法的处理内容才是本文最应该关注的核心,它在处理数据解析、转换这一块还是存在不小的复杂度的~


  // 本方法传入了beanName和bean定义信息,以及它对应的BeanWrapper和value值们~
  protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
    if (pvs.isEmpty()) {
      return;
    }
    ...
    MutablePropertyValues mpvs = null;
    List<PropertyValue> original;
    // 说明一下:为何这里还是要判断一下,虽然Spring对PropertyValues的内建实现只有MutablePropertyValues
    // 但是这个是调用者自己也可以实现逻辑的~~~so判断一下最佳~~~~
    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);
        }
        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);
    }
  }
  // 属性值的转换
  @Nullable
  private Object convertForProperty(@Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
    // 需要特别注意的是:convertForProperty方法是BeanWrapperImpl的实例方法,并非接口方法
    // 这个方法内部就用到了CachedIntrospectionResults,从何就和Java内省搭上了关系~~~
    if (converter instanceof BeanWrapperImpl) {
      return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
    } else { // 自定义转换器
      PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
      MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
      return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
    }
  }


说明:BeanDefinitionValueResolver是Spring一个内建的非public类,它在上述步骤中承担了非常多的任务,具体可参考此处:BeanDefinitionValueResolver和PropertyValues


相关文章
|
10月前
|
监控 Java 数据库
从零学 Dropwizard:手把手搭轻量 Java 微服务,告别 Spring 臃肿
Dropwizard 整合 Jetty、Jersey 等成熟组件,开箱即用,无需复杂配置。轻量高效,启动快,资源占用少,内置监控、健康检查与安全防护,搭配 Docker 部署便捷,是构建生产级 Java 微服务的极简利器。
1000 117
|
9月前
|
安全 前端开发 Java
《深入理解Spring》:现代Java开发的核心框架
Spring自2003年诞生以来,已成为Java企业级开发的基石,凭借IoC、AOP、声明式编程等核心特性,极大简化了开发复杂度。本系列将深入解析Spring框架核心原理及Spring Boot、Cloud、Security等生态组件,助力开发者构建高效、可扩展的应用体系。(238字)
|
10月前
|
人工智能 Java API
构建基于Java的AI智能体:使用LangChain4j与Spring AI实现RAG应用
当大模型需要处理私有、实时的数据时,检索增强生成(RAG)技术成为了核心解决方案。本文深入探讨如何在Java生态中构建具备RAG能力的AI智能体。我们将介绍新兴的Spring AI项目与成熟的LangChain4j框架,详细演示如何从零开始构建一个能够查询私有知识库的智能问答系统。内容涵盖文档加载与分块、向量数据库集成、语义检索以及与大模型的最终合成,并提供完整的代码实现,为Java开发者开启构建复杂AI智能体的大门。
5464 58
|
9月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
9月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
776 8
|
11月前
|
前端开发 Java 开发者
Java新手指南:在Spring MVC中使用查询字符串与参数
通过结合实际的需求和业务逻辑,开发者可以灵活地利用这些机制,为用户提供更丰富而高效的Web应用体验。
258 15
|
12月前
|
安全 Java 微服务
Java 最新技术和框架实操:涵盖 JDK 21 新特性与 Spring Security 6.x 安全框架搭建
本文系统整理了Java最新技术与主流框架实操内容,涵盖Java 17+新特性(如模式匹配、文本块、记录类)、Spring Boot 3微服务开发、响应式编程(WebFlux)、容器化部署(Docker+K8s)、测试与CI/CD实践,附完整代码示例和学习资源推荐,助你构建现代Java全栈开发能力。
1017 1
|
11月前
|
Cloud Native Java API
Java Spring框架技术栈选和最新版本及发展史详解(截至2025年8月)-优雅草卓伊凡
Java Spring框架技术栈选和最新版本及发展史详解(截至2025年8月)-优雅草卓伊凡
1742 0
|
12月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
1417 0
|
人工智能 Java 测试技术
Spring Boot 集成 JUnit 单元测试
本文介绍了在Spring Boot中使用JUnit 5进行单元测试的常用方法与技巧,包括添加依赖、编写测试类、使用@SpringBootTest参数、自动装配测试模块(如JSON、MVC、WebFlux、JDBC等),以及@MockBean和@SpyBean的应用。内容实用,适合Java开发者参考学习。
1313 0