Spring官网阅读(十四)Spring中的BeanWrapper及类型转换(2)

简介: Spring官网阅读(十四)Spring中的BeanWrapper及类型转换(2)

唯一子类(BeanWrapperImpl)


继承关系


微信图片_20221113103609.png

结合我们之前对接口的分析以及上面这张UML图,我们可以知道BeanWrapperImpl主要实现了一下几个功能


1.对Bean进行包装

2.对Bean的属性进行访问以及设置

3.在操作属性的过程中,必然涉及到类型转换,所以还有类型转换的功能


Java中的内置机制


在详细了解BeanWrapperImpl前,必须要了解java中的一个机制:内省


核心概念


首先可以先了解下JavaBean的概念:一种特殊的类,主要用于传递数据信息。这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为“值对象”(Value Object),或“VO”。


因此JavaBean都有如下几个特征:


1.属性都是私有的;

2.有无参的public构造方法;

3.对私有属性根据需要提供公有的getXxx方法以及setXxx方法;

4.getters必须有返回值没有方法参数;setter值没有返回值,有方法参数;


符合这些特征的类,被称为JavaBean;JDK中提供了一套API用来访问某个属性的getter/setter方法,这些API存放在java.beans中,这就是内省(Introspector)。


内省和反射的区别:


反射:Java反射机制是在运行中,对任意一个类,能够获取得到这个类的所有属性和方法;它针对的是任意类

内省(Introspector):是Java语言对JavaBean类属性、事件的处理方法


1.反射可以操作各种类的属性,而内省只是通过反射来操作JavaBean的属性

2.内省设置属性值肯定会调用setter方法,反射可以不用(反射可直接操作属性Field)

3.反射就像照镜子,然后能看到.class的所有,是客观的事实。内省更像主观的判断:比如看到getName(),内省就会认为这个类中有name字段,但事实上并不一定会有name;通过内省可以获取bean的getter/setter


使用示例

public class Main {
    public static void main(String[] args) throws Exception{
        BeanInfo beanInfo = Introspector.getBeanInfo(People.class);
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            System.out.print(propertyDescriptor.getName()+"   ");
        }
    }
    // 程序输出:age   class   name 
    // 为什么会输出class呢?前文中有提到,“看到getName(),内省就会认为这个类中有name字段,但事实上并不一定会有name”,我们知道每个对象都会有getClass方法,所以使用内省时,默认就认为它具有class这个字段
}
class People{
    String name;
    int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

源码分析

// 这个类我只保留一些关键的代码,其余的琐碎代码都不看了
public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper {
  // 缓存内省的结果,BeanWrapperImpl就是通过这个对象来完成对包装的Bean的属性的控制
  @Nullable
  private CachedIntrospectionResults cachedIntrospectionResults;
    ......       
    public void setBeanInstance(Object object) {
    this.wrappedObject = object;
    this.rootObject = object;
        // 实际进行类型转换的对象:typeConverterDelegate
    this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject);
    setIntrospectionClass(object.getClass());
  }
    ......
    // 最终调用的就是CachedIntrospectionResults的forClass方法进行内省并缓存,底层调用的就是java的内省机制
    private CachedIntrospectionResults getCachedIntrospectionResults() {
        if (this.cachedIntrospectionResults == null) {
            this.cachedIntrospectionResults = CachedIntrospectionResults.forClass(getWrappedClass());
        }
        return this.cachedIntrospectionResults;
    }
   .......
       // 最终进行类型转换的方法
       private Object convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue,
                                         @Nullable Object newValue, @Nullable Class<?> requiredType, @Nullable TypeDescriptor td)
       throws TypeMismatchException {
       Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
       try {
           // 可以看到,最后就是调用typeConverterDelegate来进行类型转换
           return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td);
       }
       ......
   }
}

父类作用分析


对于接口,我们已经分析过了,这里就不再赘述了,我们重点看下BeanWrapperImpl继承的几个父类


PropertyEditorRegistrySupport


这个类最大的作用在于管理PropertyEditor,添加了很多的默认的PropertyEditor。在PropertyEditorRegistry的基础上做了进一步的扩展,提供的还是属性编辑器注册的功能。


TypeConverterSupport

public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport implements TypeConverter {
      @Nullable
  TypeConverterDelegate typeConverterDelegate;
......
}

这个接口实现了TypeConverter,所以它具有类型转换的能力,而它这种能力的实现,依赖于它所持有的一个TypeConverterDelegate。


AbstractPropertyAccessor

public abstract class AbstractPropertyAccessor extends TypeConverterSupport implements ConfigurablePropertyAccessor {
  // 省略部分代码......
  @Override
  public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
      throws BeansException {
    List<PropertyAccessException> propertyAccessExceptions = null;
    List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
        ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
    for (PropertyValue pv : propertyValues) {
      try {
        setPropertyValue(pv);
      }
      // ....
    }
  }
  @Override
  @Nullable
  public abstract Object getPropertyValue(String propertyName) throws BeansException;
  @Override
  public abstract void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException;
}

核心的代码其实就是这些,这个类继承了TypeConverterSupport,所以它具备了类型转换的能力。同时它也是一个属性访问器,但是它只是实现了批量设置属性的方法,真正的setPropertyValue还是留待子类实现。可以看到,到这个类为止,还没有将属性的设置跟类型转换的能力结合起来。


AbstractNestablePropertyAccessor


这个类开始真正的将属性访问跟类型转换结合到一起,它真正的实现了setPropertyValue,并在设置属性的时候会进行类型的转换,具体代码就不看了,非常繁杂,但是整体不难。


上面我们多次提到了类型转换,但是还没有真正看到类型转换的逻辑,因为上面类最终将类型转换的逻辑委托给了TypeConverterDelegate。接下来我们看看,类型转换到底是怎么完成。


相关文章
|
Java Nacos Spring
Nacos spring-cloud 版本没找到共享配置文件的说明,Nacos服务中共享,并且可以被多个应用获取和使用。这个在官网哪里有说明啊
Nacos spring-cloud 版本没找到共享配置文件的说明,Nacos服务中共享,并且可以被多个应用获取和使用。这个在官网哪里有说明啊
78 1
|
7月前
|
Java 应用服务中间件 Spring
Spring5源码(50)-SpringMVC源码阅读环境搭建
Spring5源码(50)-SpringMVC源码阅读环境搭建
86 0
|
6月前
|
存储 Java 程序员
Spring 注册BeanPostProcessor 源码阅读
Spring 注册BeanPostProcessor 源码阅读
|
Java 中间件 Maven
Spring 6 源码编译和高效阅读源码技巧分享
Spring 6 源码编译和高效阅读源码技巧分享
|
Java API Spring
Spring 6 源码编译和高效阅读源码技巧分享
Spring 6 源码编译和高效阅读源码技巧分享
|
Java Spring
Spring 官网无法查看的版本依赖如何查看?
Spring 官网无法查看的版本依赖如何查看?
75 0
|
前端开发 Java Spring
《Spring MVC》 第六章 MVC类型转换器、格式化器
《Spring MVC》 第六章 MVC类型转换器、格式化器
196 0
|
Java 容器 Spring
【Spring源码阅读】IOC容器的依赖注入
SpringIOC容器的依赖注入发生在用户第一次向IOC容器获取Bean时。除在BeanDefinition中设置lazy-init属性让容器完成bean的预实例化。我们在前面《Spring-IOC容器接口设计与功能》中曾讲过,容器BeanFactory通过getBean方法获取Bean。所以这篇文章,我们将从getBean()方法入手分析SpringIOC容器依赖注入的过程。
|
XML Java 数据格式
Spring源码阅读-IOC容器初始化过程
Spring IOC容器的初始化过程:Resource定位,BeanDefinition载入,向IOC容器注册BeanDefinition。整个过程由refresh()方法触发,三个过程由不同的模块完成,使用户更加灵活的对这三个过程剪裁和扩展。
165 0
|
XML 缓存 JSON
Spring MVC 阅读官方文档知识点总结
Spring MVC 阅读官方文档知识点总结
191 8
下一篇
DataWorks