欢迎光临我的个人博客: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,当然处于严谨没有这么做。而且构造函数也非常简单,就是传入一个实例(还有其他构造函数)。
既然这个类实现了绝大部分的功能,我们就仔细的看看这个类吧:
首先我们看继承结构。
顶级接口有三个:
- 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
理解这个是理解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/