spring-beans包源码阅读-2-BeanWrapper

欢迎光临我的个人博客:https://www.jelliclecat.cn/

一. BeanWrapper

/**
 * The central interface of Spring's low-level JavaBeans infrastructure.
 *
 * 

Typically not used directly but rather implicitly via a * {@link org.springframework.beans.factory.BeanFactory} or a * {@link org.springframework.validation.DataBinder}. * *

Provides operations to analyze and manipulate standard JavaBeans: * the ability to get and set property values (individually or in bulk), * get property descriptors, and query the readability/writability of properties. * *

This interface supports nested properties enabling the setting * of properties on subproperties to an unlimited depth. * *

A BeanWrapper's default for the "extractOldValueForEditor" setting * is "false", to avoid side effects caused by getter method invocations. * Turn this to "true" to expose present property values to custom editors. * */ public interface BeanWrapper extends ConfigurablePropertyAccessor { ... }

简单翻译一下:

Spring底层架构的核心接口。

主要用在BeanFactory中,而不是直接使用

提供了分析和操控标准JavaBean的操作:

get和set一个property的能力,获取一个property的descriptors,以及查询properties是否可以读写。

...

从第一句话就知道,这是一个非常重要的接口,被描述为"Spring底层架构的核心接口",可想而知其重要程度。这个接口做的事情也解释的很清楚了。BeanWrapper还继承和间接继承了很多其他的接口,之后一一解读。

这个接口有一个直接实现类:BeanWrapperImpl.java,我们直接去看这个类吧。

二. BeanWrapperImpl

还是先看注释:

/**
 * Default {@link BeanWrapper} implementation that should be sufficient
 * for all typical use cases. Caches introspection results for efficiency.
 * ...
 
 ...
 
 /**
  * Create a new BeanWrapperImpl for the given object.
  * @param object object wrapped by this BeanWrapper
  */
  public BeanWrapperImpl(Object object) {
    super(object);
  }

BeanWrapper的一个默认实现,可以满足所有的典型使用情形,缓存了自省结果。

一开头就告诉你了,这个类是一个完备的实现类,感觉spring得开发者已经在心中悄悄的给这个类标上了final,当然处于严谨没有这么做。而且构造函数也非常简单,就是传入一个实例(还有其他构造函数)。

既然这个类实现了绝大部分的功能,我们就仔细的看看这个类吧:

image

首先我们看继承结构。

顶级接口有三个:

  • PropertyAccessor
  • PropertyEditorRegistry
  • TypeConverter

这里简单解释一下,感兴趣的朋友可以自行研究。

1. PropertyAccessor

顾名思义,提供了对Bean的Property的set和get的方法,其实还有很丰富的方法,比如批量set和get属性,获取一个属性的读写权限信息,获取某个属性的类型或者类型描述(TypeDescriptor:Context about a type to convert from or to.)等方法。

2. PropertyEditorRegistry

修改一个property属性不是我们自己动手修改的,而是通过PropertyEditor接口,这个接口是java.beans包下的标准接口,这个接口用来修改一个bean的特定属性。java.beans包下提供了一批默认的PropertyEditor的实现,用来修改一些常见类型的属性,比如int,List等等,而PropertyEditorRegistry接口的作用,就是让我们可以注册自定义的PropertyEditor,让spring知道对于特定的类型的属性,去调用那个PropertyEditor进行具体的操作。

3. TypeConverter

对类型转换的支持,比如我设置一个属性,但是传入的类型和属性的类型不匹配,怎么办呢?那就进行类型转换,其实常见的类型转换有很多,比如String.valueOf就是讲一个其他类型的变量转换成String类型的方法。当然,可以预计有很多复杂的和自定义的不同类型之间的转换,那就是通过这个接口去实现。

到这里其实就已经能够知道BeanWrapper的主要作用了,那就是将一个Bean包起来,然后去set和get它的各种属性。

要注意的是,BeanWrapper对象的内部没有保存bean的属性的字段,最初我以为bean的属性会以一个map的形式存在BeanWrapper中,然后需要操作那个具体的Property就去根据这个Property的名字去get,其实不是的,所有的bean的信息在后来转换成了CachedIntrospectionResults对象:

public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper {

  /**
   * Cached introspections results for this object, to prevent encountering
   * the cost of JavaBeans introspection every time.
   */
  @Nullable
  private CachedIntrospectionResults cachedIntrospectionResults;
  

这个对象里面保存了一个static Map,缓存了所有的Class自省的结果:

public final class CachedIntrospectionResults {
  
  ...
  /**
   * Map keyed by Class containing CachedIntrospectionResults, strongly held.
   * This variant is being used for cache-safe bean classes.
   */
  static final ConcurrentMap, CachedIntrospectionResults> strongClassCache =
    new ConcurrentHashMap<>(64);
  ...
}

每个CachedIntrospectionResults对象里面又有以下属性:

/** The BeanInfo object for the introspected bean class. */
  private final BeanInfo beanInfo;

  /** PropertyDescriptor objects keyed by property name String. */
  private final Map propertyDescriptorCache;

  /** TypeDescriptor objects keyed by PropertyDescriptor. */
  private final ConcurrentMap typeDescriptorCache;

这些属性保存了BeanWrapper包装Bean的BeanInfo、PropertyDescriptor、TypeDescriptor,这些信息就是Bean的自省结果。所有对Bean的属性的设置和获取最后都是通过CachedIntrospectionResults获取的。CachedIntrospectionResults在BeanWrapper中是懒加载的,在用户真正去调用Bean的相关信息的时候才去创建CachedIntrospectionResults。懒加载也是Spring的一贯作风。

三. PropertyEditorRegistrySupport

为了进一步理解BeanWrapper中有哪些东西,我们再看看上层的一些实现类,里面做了一些什么:

public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {

  @Nullable
  private ConversionService conversionService;

  private boolean defaultEditorsActive = false;

  private boolean configValueEditorsActive = false;

  @Nullable
  private Map, PropertyEditor> defaultEditors;

  @Nullable
  private Map, PropertyEditor> overriddenDefaultEditors;

  @Nullable
  private Map, PropertyEditor> customEditors;

  @Nullable
  private Map customEditorsForPath;

  @Nullable
  private Map, PropertyEditor> customEditorCache;

  ...
 
  /**
   * Actually register the default editors for this registry instance.
   */
  private void createDefaultEditors() {
    this.defaultEditors = new HashMap<>(64);

    // Simple editors, without parameterization capabilities.
    // The JDK does not contain a default editor for any of these target types.
    this.defaultEditors.put(Charset.class, new CharsetEditor());
    this.defaultEditors.put(Class.class, new ClassEditor());
    this.defaultEditors.put(Class[].class, new ClassArrayEditor());
    this.defaultEditors.put(Currency.class, new CurrencyEditor());
    this.defaultEditors.put(File.class, new FileEditor());
    this.defaultEditors.put(InputStream.class, new InputStreamEditor());
    this.defaultEditors.put(InputSource.class, new InputSourceEditor());
    this.defaultEditors.put(Locale.class, new LocaleEditor());
    this.defaultEditors.put(Path.class, new PathEditor());
    this.defaultEditors.put(Pattern.class, new PatternEditor());
    this.defaultEditors.put(Properties.class, new PropertiesEditor());
    this.defaultEditors.put(Reader.class, new ReaderEditor());
    this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
    this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
    this.defaultEditors.put(URI.class, new URIEditor());
    this.defaultEditors.put(URL.class, new URLEditor());
    this.defaultEditors.put(UUID.class, new UUIDEditor());
    this.defaultEditors.put(ZoneId.class, new ZoneIdEditor());

    // Default instances of collection editors.
    // Can be overridden by registering custom instances of those as custom editors.
    this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
    this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
    this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
    this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
    this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));

    // Default editors for primitive arrays.
    this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
    this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());

    // The JDK does not contain a default editor for char!
    this.defaultEditors.put(char.class, new CharacterEditor(false));
    this.defaultEditors.put(Character.class, new CharacterEditor(true));

    // Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
    this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
    this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));

    // The JDK does not contain default editors for number wrapper types!
    // Override JDK primitive number editors with our own CustomNumberEditor.
    this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
    this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
    this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
    this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
    this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
    this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
    this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
    this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
    this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
    this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
    this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
    this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
    this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
    this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));

    // Only register config value editors if explicitly requested.
    if (this.configValueEditorsActive) {
      StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
      this.defaultEditors.put(String[].class, sae);
      this.defaultEditors.put(short[].class, sae);
      this.defaultEditors.put(int[].class, sae);
      this.defaultEditors.put(long[].class, sae);
    }
  }
  
  ...
    
  @Override
    public void registerCustomEditor(Class requiredType, PropertyEditor propertyEditor) {
        registerCustomEditor(requiredType, null, propertyEditor);
    }
  
  ...
    
}

首先简单粗暴的加载了各种默认的PropertiesEditor以及Spring自己实现的一些PropertiesEditor,然后最用户自定义的PropertiesEditor做了支持。所有的PropertiesEditor都保存在Map中,用的时候根据不同的Class去查询对应Class的PropertiesEditor。

四. TypeConverterSupport

使用了委托模式,将所有的TypeConvert请求都委托给了真正进行类型转换的类TypeConverterDelegate,而TypeConverterDelegate的转换请求最终又由一个ConversionService去实现,ConversionService也只是一个管理类,并没有做真正的类型转换操作,最最最终的类型转换,交给了负责各种类型互相转的Converter,这些Converter实现了GenericConverter接口,感兴趣的可以仔细阅读一下这部分代码,这部分代码位于spring-core模块下的org.springframework.core.convert包中,里面包含了大量spring已经编码好的Converter。

总之这里就是对不同类型的互相转换做支持。

五. AbstractNestablePropertyAccessor

最后讲一下AbstractNestablePropertyAccessor这个类,这个类完成了非常多的BeanWrapper功能,但是主要是对嵌套类的属性操作做支持。

AbstractNestablePropertyAccessor内部持有一个

private Map nestedPropertyAccessors;

理解这个是理解AbstractNestablePropertyAccessor的关键。

例如有以下的类型:

class bean {
  private A a;
  get() & set()
}

class A {
  private String name;
  get() & set()
}

那么,bean对应的AbstractNestablePropertyAccessor内部的nestedPropertyAccessors就有一个:

a -> AbstractNestablePropertyAccessor of class A 的map entry。

假设AbstractNestablePropertyAccessor of class A 的实例名称是nestablePropertyAccessorOfA,那么nestablePropertyAccessorOfA内部的nestedPropertyAccessors就有一个:

name -> AbstractNestablePropertyAccessor of class String 的map entry。

所以Bean中类型的嵌套和AbstractNestablePropertyAccessor中nestedPropertyAccessors的嵌套是一一对应的。

AbstractNestablePropertyAccessor有一个字段nestedPath,它表示对于一个嵌套属性的路径,比如在上例中,class A的name属性在class Bean中被表示为:a.name,那么如何拿到最name属性的PropertyAccessor呢?

/**
 * Recursively navigate to return a property accessor for the nested property path.
 * @param propertyPath property path, which may be nested
 * @return a property accessor for the target bean
 */
  @SuppressWarnings("unchecked")  // avoid nested generic
  protected AbstractNestablePropertyAccessor getPropertyAccessorForPropertyPath(String propertyPath) {
    int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
    // Handle nested properties recursively.
    if (pos > -1) {
      String nestedProperty = propertyPath.substring(0, pos);
      String nestedPath = propertyPath.substring(pos + 1);
      AbstractNestablePropertyAccessor nestedPa = getNestedPropertyAccessor(nestedProperty);
      return nestedPa.getPropertyAccessorForPropertyPath(nestedPath);
    }
    else {
      return this;
    }
  }

就在这段代码中,这段代码递归的解析propertyPath,每层递归都返回外层嵌套的AbstractNestablePropertyAccessor,直到拿到最里面的属性,这里,当我们传入propertyPath = "a.name"时,先回拿到属性a对应的AbstractNestablePropertyAccessor,然后调用属性a的AbstractNestablePropertyAccessor去查找"name"属性,最终返回的是"name"属性对应的AbstractNestablePropertyAccessor。

AbstractNestablePropertyAccessor这个类理解起来稍复杂一些,关键是理解如果Bean的属性是其他Bean的情况下,如果去处理。

六. 简单的总结

BeanWrapper的初始化流程:

传入一个实例 -> 将registerDefaultEditors设置为true,表示要注册默认的PropertyEditors -> 然后调用setWrappedInstance:

// AbstractNestablePropertyAccessor.java
public void setWrappedInstance(Object object, @Nullable String nestedPath, @Nullable Object rootObject) {
    this.wrappedObject = ObjectUtils.unwrapOptional(object);
    Assert.notNull(this.wrappedObject, "Target object must not be null");
    this.nestedPath = (nestedPath != null ? nestedPath : "");
    this.rootObject = (!"".equals(this.nestedPath) ? rootObject : this.wrappedObject);
    this.nestedPropertyAccessors = null;
    this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject);
  }

// BeanWrapperImpl.java
@Override
public void setWrappedInstance(Object object, @Nullable String nestedPath, @Nullable Object rootObject) {
    super.setWrappedInstance(object, nestedPath, rootObject);
    setIntrospectionClass(getWrappedClass());
  }

    /**
    * Set the class to introspect.
    * Needs to be called when the target object changes.
    * @param clazz the class to introspect
    */
protected void setIntrospectionClass(Class clazz) {
    if (this.cachedIntrospectionResults != null && this.cachedIntrospectionResults.getBeanClass() != clazz) {
      this.cachedIntrospectionResults = null;
    }
  }

AbstractNestablePropertyAccessor中,设置了各种基本的bean的信息,并初始化了TypeConverterDelegate,BeanWrapperImpl中重写了父类AbstractNestablePropertyAccessor中的setWrappedInstance方法,并多了一个操作就是调用setIntrospectionClass。到这里初始化就完成了。

然后再调用convertForProperty、getLocalPropertyHandler、getPropertyDescriptors、getPropertyDescriptor这四个方法中的一个时,对包装的bean进行自省,创建CachedIntrospectionResults并缓存下来。

BeanWrapper主要封装了对一个Bean的属性的操作。

有不对的地方,欢迎指正~

欢迎光临我的个人博客:https://www.jelliclecat.cn/

你可能感兴趣的:(spring-beans包源码阅读-2-BeanWrapper)