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

一. BeanWrapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* The central interface of Spring's low-level JavaBeans infrastructure.
*
* <p>Typically not used directly but rather implicitly via a
* {@link org.springframework.beans.factory.BeanFactory} or a
* {@link org.springframework.validation.DataBinder}.
*
* <p>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.
*
* <p>This interface supports <b>nested properties</b> enabling the setting
* of properties on subproperties to an unlimited depth.
*
* <p>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

还是先看注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 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对象:

1
2
3
4
5
6
7
8
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自省的结果:

1
2
3
4
5
6
7
8
9
10
11
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<Class<?>, CachedIntrospectionResults> strongClassCache =
new ConcurrentHashMap<>(64);
...
}

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

1
2
3
4
5
6
7
8
/** The BeanInfo object for the introspected bean class. */
private final BeanInfo beanInfo;

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

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

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

三. PropertyEditorRegistrySupport

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {

@Nullable
private ConversionService conversionService;

private boolean defaultEditorsActive = false;

private boolean configValueEditorsActive = false;

@Nullable
private Map<Class<?>, PropertyEditor> defaultEditors;

@Nullable
private Map<Class<?>, PropertyEditor> overriddenDefaultEditors;

@Nullable
private Map<Class<?>, PropertyEditor> customEditors;

@Nullable
private Map<String, CustomEditorHolder> customEditorsForPath;

@Nullable
private Map<Class<?>, 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<String, AbstractNestablePropertyAccessor> nestedPropertyAccessors;

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

例如有以下的类型:

1
2
3
4
5
6
7
8
9
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呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 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的属性的操作。

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