Spring源码 ---类型转换TypeConverterSupport在Spring的使用(BeanWrapperImpl设置对应Bean对象属性值的过程)
以前写了一篇介绍了BeanWrapperImpl整体结构,这篇就介绍其的一些细节,看其具体是怎样整合对象(wrappedObject--还未进行属性赋值的Bean对象)、属性(Property)、Converter/PropertyEditer(类型转换器),来完成对对象属性赋值的。接口介绍:这里的TypeConverterSupport(类型转换)已经在上一篇讲过了,现在我们来看下关于类型转换操
以前写了一篇介绍了BeanWrapperImpl整体结构,这篇就介绍其的一些细节,看其具体是怎样整合对象(wrappedObject--还未进行属性赋值的Bean对象)、属性(Property)、Converter/PropertyEditer(类型转换器),来完成对对象属性赋值的。
接口介绍:
这里的TypeConverterSupport(类型转换)已经在上一篇讲过了,现在我们来看下关于类型转换操作的对象PropertyAccessor接口以及BeanWrapper。
PropertyAccessor:
通过方法的名称可以知道,这个接口是通过Property的名称去设置Property的值,以及通过名称获取对应Property的Value的。而下面那些描叙符是用于划分,获取真正的Property的name的。这个到下面具体的流程介绍就明白了。
BeanWrapper:
public interface BeanWrapper extends ConfigurablePropertyAccessor { void setAutoGrowCollectionLimit(int autoGrowCollectionLimit); int getAutoGrowCollectionLimit(); /** * Return the bean instance wrapped by this object. */ Object getWrappedInstance(); /** * Return the type of the wrapped bean instance. */ Class<?> getWrappedClass(); PropertyDescriptor[] getPropertyDescriptors(); PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException; }
这个就是放Bean对象的接口,要注意标粗的两个方法。一个是获取设置的对象getWrappedInstance(),一个是获取这个对象的Class描叙getWrappedClass()。
BeanWrapperImp整个操作过程
现在我们来看下BeanWrapperImp整个操作过程:
AbstractNestablePropertyAccessor:
我们先来看实现类型转换接口与属性设置接口的抽象类AbstractNestablePropertyAccessor,我们先来看其在Spring源码中的测试demo,这个类是抽象类,有些方法需要其子类去实现,所以我们需要结合其子类BeanWrapperImpl一起来梳理其流程:
@Test public void getNestedProperty() { Person target = createPerson("John", "London", "UK"); AbstractPropertyAccessor accessor = createAccessor(target); assertThat(accessor.getPropertyValue("address.city"), is("London")); } private Person createPerson(String name, String city, String country) { return new Person(name, new Address(city, country)); } private static class Person { private String name; private Address address; private Person(String name, Address address) { this.name = name; this.address = address; } ........ } private static class Address { private String city; private Country country; ........ } protected BeanWrapperImpl createAccessor(Object target) { return new BeanWrapperImpl(target); }
AbstractNestablePropertyAccessor的初始化方法有一个传Object:
protected AbstractNestablePropertyAccessor(Object object) { registerDefaultEditors(); setWrappedInstance(object); }
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); }
这里就是注册默认的PropertyEdier。然后setWrappedInstance,这个就是为Bean对象。
现在这个AbstractNestablePropertyAccessor对象是Persion:
getPropertyValue方法
然后回到 accessor.getPropertyValue("address.city"),这个address.city,表示的就是去获取Person对象的的address属性对象的city属性的值。这个等下看其是怎样解析的。
public Object getPropertyValue(String propertyName) throws BeansException { AbstractNestablePropertyAccessor nestedPa = getPropertyAccessorForPropertyPath(propertyName); PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName)); return nestedPa.getPropertyValue(tokens); } 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; } }
这里的getFirstNestedPropertySeparatorIndex,就是处理前面的属性名称address.city的。我们最开始在前面讲PropertyAccessor接口有说其的一些描叙符:"."、"["、"]"等。如果是直接写的属性名,如Persion的name属性,就会返回-1,直接就返回当前AbstractNestablePropertyAccessor对象,this。如果有类型嵌套结构,就返回嵌套分割位置,再进一步处理:
private AbstractNestablePropertyAccessor getNestedPropertyAccessor(String nestedProperty) { ............ // Get value of bean property. PropertyTokenHolder tokens = getPropertyNameTokens(nestedProperty); String canonicalName = tokens.canonicalName; Object value = getPropertyValue(tokens); ........ } protected static class PropertyTokenHolder { .......... public String actualName; public String canonicalName; @Nullable public String[] keys; }
这个PropertyTokenHolder接收用来放描叙PropertyName的,这里有个属性keys,就是用来放集合的index的,通过例子就能很好的了解描叙符与这个了:
private static class Foo { private List list; } accessor.setPropertyValue("list[0]", map);
我们再回到getNestedPropertyAccessor方法,获取PropertyTokenHolder后,进入getPropertyValue方法:
protected Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException { String propertyName = tokens.canonicalName; String actualName = tokens.actualName; PropertyHandler ph = getLocalPropertyHandler(actualName); if (ph == null || !ph.isReadable()) { throw new NotReadablePropertyException(getRootClass(), this.nestedPath + propertyName); } try { Object value = ph.getValue(); if (tokens.keys != null) { if (value == null) { if (isAutoGrowNestedPaths()) { value = setDefaultValue(new PropertyTokenHolder(tokens.actualName)); } ............ } String indexedPropertyName = tokens.actualName; // apply indexes and map keys for (int i = 0; i < tokens.keys.length; i++) { ..........处理集合类型内容 } } return value; } }
这里的是getLocalPropertyHandler是AbstractNestablePropertyAccessor的抽象方法,其在BeanWrapperImp的实现:
protected BeanPropertyHandler getLocalPropertyHandler(String propertyName) { PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName); if (pd != null) { return new BeanPropertyHandler(pd); } return null; }
private CachedIntrospectionResults getCachedIntrospectionResults() { if (this.cachedIntrospectionResults == null) { this.cachedIntrospectionResults = CachedIntrospectionResults.forClass(getWrappedClass()); } return this.cachedIntrospectionResults; }
static CachedIntrospectionResults forClass(Class<?> beanClass) throws BeansException { ............ results = new CachedIntrospectionResults(beanClass); ........... return (existing != null ? existing : results); }
private CachedIntrospectionResults(Class<?> beanClass) throws BeansException { ........... this.beanInfo = getBeanInfo(beanClass, shouldIntrospectorIgnoreBeaninfoClasses); this.propertyDescriptorCache = new LinkedHashMap<>(); PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors(); for (PropertyDescriptor pd : pds) { ................. this.propertyDescriptorCache.put(pd.getName(), pd); }
public PropertyDescriptor[] getPropertyDescriptors() { return properties; }
初次进入调用CachedIntrospectionResults.forClass(getWrappedClass()),这个getWrappedClass就是获取在初始化AbstractNestablePropertyAccessor的时候传的wrappedObject,这里获取其的Class描叙。然后通过这个beanClass去创建beanInfo,这个BeanInfo就是表示这个Bean的Class有哪些Property以及Method方法:
然后循环这个BeanInfo的Property,将其添加到propertyDescriptorCache 中(这个propertyDescriptorCache 是CachedIntrospectionResults类的成员变量。
再回到BeanWrapperImp实现的getLocalPropertyHandler方法,现在初始化设置了CachedIntrospectionResults对象,就调用其getPropertyDescriptor方法,通过PropertyName获取前面放在propertyDescriptorCache对应的PropertyDescriptor了。
public class PropertyDescriptor extends FeatureDescriptor { ......... private final MethodRef readMethodRef = new MethodRef(); private final MethodRef writeMethodRef = new MethodRef(); ........... private String baseName; private String writeMethodName; private String readMethodName; }
这个直接看其变量名就可以知道这个类是描叙Property及其对应的write、read方法。
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName); PropertyDescriptor getPropertyDescriptor(String name) { PropertyDescriptor pd = this.propertyDescriptorCache.get(name); if (pd == null && StringUtils.hasLength(name)) { pd = this.propertyDescriptorCache.get(StringUtils.uncapitalize(name)); if (pd == null) { pd = this.propertyDescriptorCache.get(StringUtils.capitalize(name)); } } return (pd == null || pd instanceof GenericTypeAwarePropertyDescriptor ? pd : buildGenericTypeAwarePropertyDescriptor(getBeanClass(), pd)); }
获取到PropertyDescriptor后就运行:
if (pd != null) { return new BeanPropertyHandler(pd); }
将PropertyDescriptor包装为BeanPropertyHandler(其是BeanWrapperImp的内部类):
private class BeanPropertyHandler extends PropertyHandler { private final PropertyDescriptor pd; }
protected abstract static class PropertyHandler(定义在AbstractNestablePropertyAccessor中) { private final Class<?> propertyType; private final boolean readable; private final boolean writable; }
现在有了BeanPropertyHandler我们再回到前面的getPropertyValue方法:
protected Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException { String propertyName = tokens.canonicalName; String actualName = tokens.actualName; PropertyHandler ph = getLocalPropertyHandler(actualName); if (ph == null || !ph.isReadable()) { throw new NotReadablePropertyException(getRootClass(), this.nestedPath + propertyName); } .......... Object value = ph.getValue(); ........... return value; }
现在是ph.getValue:
public Object getValue() throws Exception { final Method readMethod = this.pd.getReadMethod(); ............ ReflectionUtils.makeAccessible(readMethod); return readMethod.invoke(getWrappedInstance(), (Object[]) null); } }
public final Object getWrappedInstance() { Assert.state(this.wrappedObject != null, "No wrapped object"); return this.wrappedObject; }
通过反射执行对应的read方法,获取返回值,拿到返回值后再运行newNestedPropertyAccessor方法:
private AbstractNestablePropertyAccessor getNestedPropertyAccessor(String nestedProperty) { ............. PropertyTokenHolder tokens = getPropertyNameTokens(nestedProperty); String canonicalName = tokens.canonicalName; Object value = getPropertyValue(tokens); .......... nestedPa = newNestedPropertyAccessor(value, this.nestedPath + canonicalName + NESTED_PROPERTY_SEPARATOR); .......... return nestedPa; }
这个newNestedPropertyAccessor也是一个抽象方法,其在BeanWrapperImp的实现是:
protected BeanWrapperImpl newNestedPropertyAccessor(Object object, String nestedPath) { return new BeanWrapperImpl(object, nestedPath, this); }
可以看到这里是将返回的对象再包为一个BeanWrapperImpl对象。
我们再回到前面AbstractNestablePropertyAccessor类:
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; } }
这里实际是AbstractNestablePropertyAccessor的getPropertyAccessorForPropertyPath循环调用。如果不是这个address.city这种一个类型嵌套另一个对象再去获取对应属性。就是直接返回this,例如,如果是获取属性name的value就直接返回是原来的AbstractNestablePropertyAccessor 对象。
所以这上面的getPropertyAccessorForPropertyPath过程总结就是:
如果没有进行一个类型A套另一个类型B去获取属性。就返回原来的AbstractNestablePropertyAccessor ,不会再去调getNestedPropertyAccessor方法了。如果是一个对象套另一个对象,就通过getNestedPropertyAccessor方法,创建类型B的AbstractNestablePropertyAccessor 。
在这里我们可以看到这个wrappedObject已经变为是Address了,而原来有提到通过createAccessor(target)产生的是Persion。
然后我们再回到原来的方法调用:
public Object getPropertyValue(String propertyName) throws BeansException { AbstractNestablePropertyAccessor nestedPa = getPropertyAccessorForPropertyPath(propertyName); PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName)); return nestedPa.getPropertyValue(tokens); }
同时这个PropertyTokenHolder也是变为描叙Address的属性city了。
下面就是调用getPropertyValue,去获取对应的值了。这个getPropertyValue在前面已经讲过了,所以整个获取过程就完成了。
setPropertyValue
@Override public void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException { AbstractNestablePropertyAccessor nestedPa; try { nestedPa = getPropertyAccessorForPropertyPath(propertyName); } catch (NotReadablePropertyException ex) { throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName, "Nested property in path '" + propertyName + "' does not exist", ex); } PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName)); nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value)); }
可以看到这里是与前面getPropertyValue方法一样的,只是最后调用的是setPropertyValue方法:
protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException { if (tokens.keys != null) { processKeyedProperty(tokens, pv); } else { processLocalProperty(tokens, pv); } }
private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) { PropertyHandler ph = getLocalPropertyHandler(tokens.actualName); if (ph == null || !ph.isWritable()) { .......... return; } ........ } Object oldValue = null; try { Object originalValue = pv.getValue(); Object valueToApply = originalValue; ....... if (isExtractOldValueForEditor() && ph.isReadable()) { ...... oldValue = ph.getValue(); .......... valueToApply = convertForProperty( tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor()); ........... ph.setValue(valueToApply); } }
这个getLocalPropertyHandler方法在前面get方法时也讲了,然后是调用ph.getValue()方法去获取原来的值,在调用convertForProperty方法:
private Object convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class<?> requiredType, @Nullable TypeDescriptor td) throws TypeMismatchException { .............. return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td); ........... }
这里就是进行类型转换了。
类型转换之后就调用ph.setValue(valueToApply),将其设置到wrappedObject对象中:
public void setValue(final @Nullable Object value) throws Exception {
final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?
((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :
this.pd.getWriteMethod());
...............
ReflectionUtils.makeAccessible(writeMethod);
writeMethod.invoke(getWrappedInstance(), value);
}
}
}
这个getWrappedInstance方法就是获取这个AbstractNestablePropertyAccessor实现类BeanWrapperImp的wrapperObject对象:
return this.wrappedObject;
至此,关于是怎样设置一个Bean对象的属性就梳理完毕。
更多推荐
所有评论(0)