歡迎光臨我的個人博客:https://www.jelliclecat.cn/
一. BeanWrapper
/**
* 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底層架構(gòu)的核心接口。
主要用在BeanFactory中,而不是直接使用
提供了分析和操控標(biāo)準(zhǔn)JavaBean的操作:
get和set一個property的能力聘萨,獲取一個property的descriptors华弓,以及查詢properties是否可以讀寫族跛。
...
從第一句話就知道鞍爱,這是一個非常重要的接口初肉,被描述為"Spring底層架構(gòu)的核心接口",可想而知其重要程度荆责。這個接口做的事情也解釋的很清楚了滥比。BeanWrapper還繼承和間接繼承了很多其他的接口,之后一一解讀草巡。
這個接口有一個直接實現(xiàn)類: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的一個默認(rèn)實現(xiàn)山憨,可以滿足所有的典型使用情形查乒,緩存了自省結(jié)果。
一開頭就告訴你了郁竟,這個類是一個完備的實現(xiàn)類玛迄,感覺spring得開發(fā)者已經(jīng)在心中悄悄的給這個類標(biāo)上了final,當(dāng)然處于嚴(yán)謹(jǐn)沒有這么做棚亩。而且構(gòu)造函數(shù)也非常簡單蓖议,就是傳入一個實例(還有其他構(gòu)造函數(shù))。
既然這個類實現(xiàn)了絕大部分的功能讥蟆,我們就仔細(xì)的看看這個類吧:
首先我們看繼承結(jié)構(gòu)勒虾。
頂級接口有三個:
- PropertyAccessor
- PropertyEditorRegistry
- TypeConverter
這里簡單解釋一下,感興趣的朋友可以自行研究瘸彤。
1. PropertyAccessor
顧名思義修然,提供了對Bean的Property的set和get的方法,其實還有很豐富的方法质况,比如批量set和get屬性愕宋,獲取一個屬性的讀寫權(quán)限信息,獲取某個屬性的類型或者類型描述(TypeDescriptor:Context about a type to convert from or to.)等方法结榄。
2. PropertyEditorRegistry
修改一個property屬性不是我們自己動手修改的中贝,而是通過PropertyEditor接口,這個接口是java.beans包下的標(biāo)準(zhǔn)接口臼朗,這個接口用來修改一個bean的特定屬性邻寿。java.beans包下提供了一批默認(rèn)的PropertyEditor的實現(xiàn),用來修改一些常見類型的屬性依溯,比如int老厌,List等等,而PropertyEditorRegistry接口的作用黎炉,就是讓我們可以注冊自定義的PropertyEditor枝秤,讓spring知道對于特定的類型的屬性,去調(diào)用那個PropertyEditor進(jìn)行具體的操作慷嗜。
3. TypeConverter
對類型轉(zhuǎn)換的支持淀弹,比如我設(shè)置一個屬性丹壕,但是傳入的類型和屬性的類型不匹配,怎么辦呢薇溃?那就進(jìn)行類型轉(zhuǎn)換菌赖,其實常見的類型轉(zhuǎn)換有很多,比如String.valueOf就是講一個其他類型的變量轉(zhuǎn)換成String類型的方法沐序。當(dāng)然琉用,可以預(yù)計有很多復(fù)雜的和自定義的不同類型之間的轉(zhuǎn)換,那就是通過這個接口去實現(xiàn)策幼。
到這里其實就已經(jīng)能夠知道BeanWrapper的主要作用了邑时,那就是將一個Bean包起來,然后去set和get它的各種屬性特姐。
要注意的是晶丘,BeanWrapper對象的內(nèi)部沒有保存bean的屬性的字段,最初我以為bean的屬性會以一個map的形式存在BeanWrapper中唐含,然后需要操作那個具體的Property就去根據(jù)這個Property的名字去get浅浮,其實不是的,所有的bean的信息在后來轉(zhuǎn)換成了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自省的結(jié)果:
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對象里面又有以下屬性:
/** 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的自省結(jié)果叔遂。所有對Bean的屬性的設(shè)置和獲取最后都是通過CachedIntrospectionResults獲取的。CachedIntrospectionResults在BeanWrapper中是懶加載的争剿,在用戶真正去調(diào)用Bean的相關(guān)信息的時候才去創(chuàng)建CachedIntrospectionResults。懶加載也是Spring的一貫作風(fēng)痊末。
三. PropertyEditorRegistrySupport
為了進(jìn)一步理解BeanWrapper中有哪些東西蚕苇,我們再看看上層的一些實現(xiàn)類,里面做了一些什么:
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);
}
...
}
首先簡單粗暴的加載了各種默認(rèn)的PropertiesEditor以及Spring自己實現(xiàn)的一些PropertiesEditor凿叠,然后最用戶自定義的PropertiesEditor做了支持涩笤。所有的PropertiesEditor都保存在Map中,用的時候根據(jù)不同的Class去查詢對應(yīng)Class的PropertiesEditor盒件。
四. TypeConverterSupport
使用了委托模式蹬碧,將所有的TypeConvert請求都委托給了真正進(jìn)行類型轉(zhuǎn)換的類TypeConverterDelegate,而TypeConverterDelegate的轉(zhuǎn)換請求最終又由一個ConversionService去實現(xiàn)炒刁,ConversionService也只是一個管理類恩沽,并沒有做真正的類型轉(zhuǎn)換操作,最最最終的類型轉(zhuǎn)換翔始,交給了負(fù)責(zé)各種類型互相轉(zhuǎn)的Converter罗心,這些Converter實現(xiàn)了GenericConverter接口里伯,感興趣的可以仔細(xì)閱讀一下這部分代碼,這部分代碼位于spring-core模塊下的org.springframework.core.convert包中渤闷,里面包含了大量spring已經(jīng)編碼好的Converter疾瓮。
總之這里就是對不同類型的互相轉(zhuǎn)換做支持。
五. AbstractNestablePropertyAccessor
最后講一下AbstractNestablePropertyAccessor這個類飒箭,這個類完成了非常多的BeanWrapper功能狼电,但是主要是對嵌套類的屬性操作做支持。
AbstractNestablePropertyAccessor內(nèi)部持有一個
private Map<String, AbstractNestablePropertyAccessor> nestedPropertyAccessors;
理解這個是理解AbstractNestablePropertyAccessor的關(guān)鍵弦蹂。
例如有以下的類型:
class bean {
private A a;
get() & set()
}
class A {
private String name;
get() & set()
}
那么肩碟,bean對應(yīng)的AbstractNestablePropertyAccessor內(nèi)部的nestedPropertyAccessors就有一個:
a -> AbstractNestablePropertyAccessor of class A
的map entry。
假設(shè)AbstractNestablePropertyAccessor of class A
的實例名稱是nestablePropertyAccessorOfA盈匾,那么nestablePropertyAccessorOfA內(nèi)部的nestedPropertyAccessors就有一個:
name -> AbstractNestablePropertyAccessor of class String
的map entry腾务。
所以Bean中類型的嵌套和AbstractNestablePropertyAccessor中nestedPropertyAccessors的嵌套是一一對應(yīng)的。
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密末,直到拿到最里面的屬性,這里跛璧,當(dāng)我們傳入propertyPath = "a.name"時严里,先回拿到屬性a對應(yīng)的AbstractNestablePropertyAccessor,然后調(diào)用屬性a的AbstractNestablePropertyAccessor去查找"name"屬性追城,最終返回的是"name"屬性對應(yīng)的AbstractNestablePropertyAccessor刹碾。
AbstractNestablePropertyAccessor這個類理解起來稍復(fù)雜一些,關(guān)鍵是理解如果Bean的屬性是其他Bean的情況下座柱,如果去處理迷帜。
六. 簡單的總結(jié)
BeanWrapper的初始化流程:
傳入一個實例 -> 將registerDefaultEditors設(shè)置為true,表示要注冊默認(rèn)的PropertyEditors -> 然后調(diào)用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中色洞,設(shè)置了各種基本的bean的信息戏锹,并初始化了TypeConverterDelegate,BeanWrapperImpl中重寫了父類AbstractNestablePropertyAccessor中的setWrappedInstance方法火诸,并多了一個操作就是調(diào)用setIntrospectionClass锦针。到這里初始化就完成了。
然后再調(diào)用convertForProperty、getLocalPropertyHandler伞插、getPropertyDescriptors割粮、getPropertyDescriptor這四個方法中的一個時,對包裝的bean進(jìn)行自省媚污,創(chuàng)建CachedIntrospectionResults并緩存下來舀瓢。
BeanWrapper主要封裝了對一個Bean的屬性的操作。
有不對的地方耗美,歡迎指正~
歡迎光臨我的個人博客:https://www.jelliclecat.cn/