深入Spring官網系列(十四):BeanWrapper詳細解析
BeanWrapper是Spring中一個很重要的介面,Spring在通過配置資訊建立物件時,第一步首先就是建立一個BeanWrapper。這篇文章我們就分析下這個介面,本文內容主要對應官網中的
3.3
及3.4
小結
介面定義
// Spring低階JavaBeans基礎設施的中央介面。通常來說並不直接使用BeanWrapper,而是藉助BeanFactory或者DataBinder來一起使用,BeanWrapper對Spring中的Bean做了包裝,為的是更加方便的操作Bean中的屬性
public interface BeanWrapper extends ConfigurablePropertyAccessor {
void setAutoGrowCollectionLimit(int autoGrowCollectionLimit);
int getAutoGrowCollectionLimit();
// 獲取包裝的Bean
Object getWrappedInstance();
// 獲取包裝的Bean的class
Class<?> getWrappedClass();
// 獲取所有屬性的屬性描述符
PropertyDescriptor[] getPropertyDescriptors();
// 獲取指定屬性的屬性描述符
PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;
}
這裡需要解釋一個概念,什麼是屬性描述符?
PropertyDescriptor:屬性描述符,能夠描述javaBean中的屬性,通過屬性描述符我們能知道這個屬性的型別,獲取到操縱屬性的方法(getter/setter)
繼承關係
BeanWrapper的子類只有一個:BeanWrapperImpl
,它繼承了ConfigurablePropertyAccessor
,這個介面的主要功能是進行屬性訪問,同時它又有三個父介面,接下來我們一一分析他們的功能。
介面功能
1、PropertyEditorRegistry(屬性編輯器註冊器)
介面定義
// 這個介面的功能很簡單,就是用來注入屬性編輯器(PropertyEditor),那麼什麼是PropertyEditor呢?
public interface PropertyEditorRegistry {
void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);
void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor);
@Nullable
PropertyEditor findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath);
}
PropertyEditor
概念
PropertyEditor是JavaBean規範定義的介面,這是
java.beans
中一個介面,其設計的意圖是圖形化程式設計上,方便物件與String之間的轉換工作,而Spring將其擴充套件,方便各種物件與String之間的轉換工作。
Spring中對PropertyEditor使用的例項
- 我們在通過XML的方式對Spring中的Bean進行配置時,不管Bean中的屬性是何種型別,都是直接通過字面值來設定Bean中的屬性。那麼是什麼在這其中做轉換呢?這裡用到的就是PropertyEditor
- SpringMVC在解析請求引數時,也是使用的PropertyEditor
Spring內建的PropertyEditor
2、PropertyAccessor(屬性訪問器)
介面定義
public interface PropertyAccessor {
// 巢狀屬性的分隔符,比如"foo.bar"將會呼叫getFoo().getBar()兩個方法
String NESTED_PROPERTY_SEPARATOR = ".";
char NESTED_PROPERTY_SEPARATOR_CHAR = '.';
// 代表角標index的符號 如person.addresses[0] 這樣就可以把值放進集合/陣列/Map裡了
String PROPERTY_KEY_PREFIX = "[";
char PROPERTY_KEY_PREFIX_CHAR = '[';
String PROPERTY_KEY_SUFFIX = "]";
char PROPERTY_KEY_SUFFIX_CHAR = ']';
// 該屬性是否可讀/可寫,不存在則返回false
boolean isReadableProperty(String propertyName);
boolean isWritableProperty(String propertyName);
// 獲取/設定屬性的方法,基本見名知意
@Nullable
Class<?> getPropertyType(String propertyName) throws BeansException;
@Nullable
TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException;
@Nullable
Object getPropertyValue(String propertyName) throws BeansException;
void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException;
void setPropertyValue(PropertyValue pv) throws BeansException;
void setPropertyValues(Map<?, ?> map) throws BeansException;
void setPropertyValues(PropertyValues pvs) throws BeansException;
void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown)
throws BeansException;
void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
throws BeansException;
}
這裡需要解釋一個概念,什麼是PropertyValue?
當設定屬性值時,少不了兩樣東西:
- 屬性訪問表示式:如
listMap[0][0]
- 屬性值:
ProperyValue
物件就是用來封裝這些資訊的。如果某個值要給賦值給bean屬性,Spring都會把這個值包裝成ProperyValue
物件。
3、TypeConverter(型別轉換器)
介面定義
// 定義了進行型別轉換時的一些規範,就像名字定義的那樣,主要用來做型別轉換
public interface TypeConverter {
// 將指定的值轉換成指定的型別
@Nullable
<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException;
// 相對於上面這個方法下面這個三種方法能處理轉換過程中的泛型
@Nullable
<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable MethodParameter methodParam) throws TypeMismatchException;
@Nullable
<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field)
throws TypeMismatchException;
default <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
throw new UnsupportedOperationException("TypeDescriptor resolution not supported");
}
}
4、ConfigurablePropertyAccessor
public interface ConfigurablePropertyAccessor extends PropertyAccessor, PropertyEditorRegistry, TypeConverter {
// ConversionService:進行轉換的業務類,轉換系統的入口
void setConversionService(@Nullable ConversionService conversionService);
@Nullable
ConversionService getConversionService();
// 進行屬性編輯是是否返回舊的值
void setExtractOldValueForEditor(boolean extractOldValueForEditor);
boolean isExtractOldValueForEditor();
// 當設定(dog.name)這種巢狀屬性的情況下,如果dog屬性為null是否會報錯
// 為true的話不會,為false會丟擲NullValueInNestedPathException
void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);
boolean isAutoGrowNestedPaths();
}
從上面可以看到,BeanWrapper介面自身對Bean進行了一層包裝。另外它的幾個通過間接繼承了幾個介面,所以它還能對Bean中的屬性進行操作。PropertyAccessor賦予了BeanWrapper對屬性進行訪問及設定的能力,在對Bean中屬性進行設定時,不可避免的需要對型別進行轉換,而恰好PropertyEditorRegistry,TypeConverter就提供了型別轉換的統一約束。
在瞭解了介面之後,我們接下來看看它的唯一實現類BeanWrapperImpl
唯一子類(BeanWrapperImpl)
繼承關係
結合我們之前對介面的分析以及上面這張UML圖,我們可以知道BeanWrapperImpl主要實現了一下幾個功能
- 對Bean進行包裝
- 對Bean的屬性進行訪問以及設定
- 在操作屬性的過程中,必然涉及到型別轉換,所以還有型別轉換的功能
Java中的內建機制
在詳細瞭解BeanWrapperImpl前,必須要了解java中的一個機制:內省
核心概念
首先可以先了解下JavaBean的概念:一種特殊的類,主要用於傳遞資料資訊。這種類中的方法主要用於訪問私有的欄位,且方法名符合某種命名規則。如果在兩個模組之間傳遞資訊,可以將資訊封裝進JavaBean中,這種物件稱為“值物件”(Value Object),或“VO”。
因此JavaBean都有如下幾個特徵:
- 屬性都是私有的;
- 有無參的public構造方法;
- 對私有屬性根據需要提供公有的getXxx方法以及setXxx方法;
- getters必須有返回值沒有方法引數;setter值沒有返回值,有方法引數;
符合這些特徵的類,被稱為JavaBean;JDK中提供了一套API用來訪問某個屬性的getter/setter方法,這些API存放在java.beans中,這就是內省(Introspector)。
內省和反射的區別:
反射:Java反射機制是在執行中,對任意一個類,能夠獲取得到這個類的所有屬性和方法;它針對的是任意類
內省(Introspector):是Java語言對JavaBean類屬性、事件的處理方法
- 反射可以操作各種類的屬性,而內省只是通過反射來操作JavaBean的屬性
- 內省設定屬性值肯定會呼叫setter方法,反射可以不用(反射可直接操作屬性Field)
- 反射就像照鏡子,然後能看到.class的所有,是客觀的事實。內省更像主觀的判斷:比如看到getName(),內省就會認為這個類中有name欄位,但事實上並不一定會有name;通過內省可以獲取bean的getter/setter
使用示例
public class Main {
public static void main(String[] args) throws Exception{
BeanInfo beanInfo = Introspector.getBeanInfo(People.class);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
System.out.print(propertyDescriptor.getName()+" ");
}
}
// 程式輸出:age class name
// 為什麼會輸出class呢?前文中有提到,“看到getName(),內省就會認為這個類中有name欄位,但事實上並不一定會有name”,我們知道每個物件都會有getClass方法,所以使用內省時,預設就認為它具有class這個欄位
}
class People{
String name;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
原始碼分析
// 這個類我只保留一些關鍵的程式碼,其餘的瑣碎程式碼都不看了
public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper {
// 快取內省的結果,BeanWrapperImpl就是通過這個物件來完成對包裝的Bean的屬性的控制
@Nullable
private CachedIntrospectionResults cachedIntrospectionResults;
......
public void setBeanInstance(Object object) {
this.wrappedObject = object;
this.rootObject = object;
// 實際進行型別轉換的物件:typeConverterDelegate
this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject);
setIntrospectionClass(object.getClass());
}
......
// 最終呼叫的就是CachedIntrospectionResults的forClass方法進行內省並快取,底層呼叫的就是java的內省機制
private CachedIntrospectionResults getCachedIntrospectionResults() {
if (this.cachedIntrospectionResults == null) {
this.cachedIntrospectionResults = CachedIntrospectionResults.forClass(getWrappedClass());
}
return this.cachedIntrospectionResults;
}
.......
// 最終進行型別轉換的方法
private Object convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue,
@Nullable Object newValue, @Nullable Class<?> requiredType, @Nullable TypeDescriptor td)
throws TypeMismatchException {
Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
try {
// 可以看到,最後就是呼叫typeConverterDelegate來進行型別轉換
return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td);
}
......
}
}
父類作用分析
對於介面,我們已經分析過了,這裡就不再贅述了,我們重點看下BeanWrapperImpl繼承的幾個父類
PropertyEditorRegistrySupport
這個類最大的作用在於管理PropertyEditor
,新增了很多的預設的PropertyEditor
。在PropertyEditorRegistry
的基礎上做了進一步的擴充套件,提供的還是屬性編輯器註冊的功能。
TypeConverterSupport
public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport implements TypeConverter {
@Nullable
TypeConverterDelegate typeConverterDelegate;
......
}
這個介面實現了TypeConverter,所以它具有型別轉換的能力,而它這種能力的實現,依賴於它所持有的一個TypeConverterDelegate。
AbstractPropertyAccessor
public abstract class AbstractPropertyAccessor extends TypeConverterSupport implements ConfigurablePropertyAccessor {
// 省略部分程式碼......
@Override
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
throws BeansException {
List<PropertyAccessException> propertyAccessExceptions = null;
List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
for (PropertyValue pv : propertyValues) {
try {
setPropertyValue(pv);
}
// ....
}
}
@Override
@Nullable
public abstract Object getPropertyValue(String propertyName) throws BeansException;
@Override
public abstract void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException;
}
核心的程式碼其實就是這些,這個類繼承了TypeConverterSupport
,所以它具備了型別轉換的能力。同時它也是一個屬性訪問器,但是它只是實現了批量設定屬性的方法,真正的setPropertyValue
還是留待子類實現。可以看到,到這個類為止,還沒有將屬性的設定跟型別轉換的能力結合起來。
AbstractNestablePropertyAccessor
這個類開始真正的將屬性訪問跟型別轉換結合到一起,它真正的實現了setPropertyValue
,並在設定屬性的時候會進行型別的轉換,具體程式碼就不看了,非常繁雜,但是整體不難。
上面我們多次提到了型別轉換,但是還沒有真正看到型別轉換的邏輯,因為上面類最終將型別轉換的邏輯委託給了TypeConverterDelegate
。接下來我們看看,型別轉換到底是怎麼完成。
型別轉換
TypeConverterDelegate
這個類我們只看一個核心方法,如下:
class TypeConverterDelegate {
private final PropertyEditorRegistrySupport propertyEditorRegistry;
@Nullable
private final Object targetObject;
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
// 檢視是否為當前這個型別配置了定製的PropertyEditor
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
// 獲取當前容器中的型別轉換業務類
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
// 在這裡可以看出,Spring底層在進行型別轉換時有兩套機制
// 1.首選的是採用PropertyEditor
// 2.在沒有配置PropertyEditor的情況下,會採用conversionService
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
// 通過conversionService進行型別轉換
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
conversionAttemptEx = ex;
}
}
}
Object convertedValue = newValue;
// 配置了定製的屬性編輯器,採用PropertyEditor進行屬性轉換
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
convertedValue instanceof String) {
TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
if (elementTypeDesc != null) {
Class<?> elementType = elementTypeDesc.getType();
if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
}
}
if (editor == null) {
// 沒有配置定製的屬性編輯器,採用預設的屬性編輯器
editor = findDefaultEditor(requiredType);
}
// 採用屬性編輯器進行轉換,需要注意的是,預設情況下PropertyEditor只會對String型別的值進行型別轉換
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
// .....
return (T) convertedValue;
}
}
從上面的程式碼中我們可以知道,Spring在實現型別轉換時,有兩套機制,第一套機制依賴於PropertyEditor,第二套機制依賴於ConversionService。關於屬性編輯器PropertyEditor我們之前已經介紹過了,主要進行的是String到Object的轉換,正因為如此,屬性編輯器進行型別轉換有很大的侷限性,所以Spring又推出了一套ConversionService的體系。
ConversionService體系
1、Converter
介面定義
package org.springframework.core.convert.converter;
// 將一個S型別的資料轉換成T型別
public interface Converter<S, T> {
T convert(S source);
}
這個介面只能進行一對一的轉換,S->T
2、ConverterFactory
介面定義
public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
利用這個轉換工廠,我們可以進行一對多的轉換,以Spring內建的一個轉換器為例:
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
@Override
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnum(ConversionUtils.getEnumType(targetType));
}
private class StringToEnum<T extends Enum> implements Converter<String, T> {
private final Class<T> enumType;
public StringToEnum(Class<T> enumType) {
this.enumType = enumType;
}
@Override
public T convert(String source) {
if (source.isEmpty()) {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
return (T) Enum.valueOf(this.enumType, source.trim());
}
}
}
通過傳入不同的列舉型別,我們可以從這個工廠中獲取到不同的轉換器,並把對應的String型別的引數轉換成對應的列舉型別資料。
可以看到,通過ConverterFactory,我們能實現一對多的型別轉換S->(T extends R)
3、GenericConverter
介面定義
public interface GenericConverter {
// 獲取能夠轉換的ConvertiblePair的集合,這個物件就是一組可以進行轉換的型別
@Nullable
Set<ConvertiblePair> getConvertibleTypes();
// 根據源資料型別轉換成目標型別資料
@Nullable
Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
final class ConvertiblePair {
// 源資料型別
private final Class<?> sourceType;
// 目標資料型別
private final Class<?> targetType;
// .....省略部分程式碼
}
}
相比於前面的Converter以及ConverterFactory,這個介面就更加牛逼了,使用它能完成多對多的轉換。因為它內部儲存了一個能夠進行轉換的ConvertiblePair的集合,每個ConvertiblePair代表一組能進行轉換的資料型別。同時,這個介面相比我們前面介紹的兩個介面,更加的複雜,所以一般情況也不推薦使用這個介面,沒有非常必要的話,最好是使用上面兩種
一般GenericConverter會與ConditionalGenericConverter配合使用,其介面定義如下:
public interface ConditionalConverter {
// 判斷是否需要對目標型別轉換到原型別,返回true的話代表要執行轉換,否則不執行轉換
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
// 結合了上面兩個介面
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}
我們來看下Spring內部的一個實際使用的例子:
final class StringToCollectionConverter implements ConditionalGenericConverter {
private final ConversionService conversionService;
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, Collection.class));
}
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return (targetType.getElementTypeDescriptor() == null ||
// 根據conversionService來判斷是否需要執行轉換
this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor()));
}
@Override
@Nullable
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
// 這裡會藉助conversionService來執行轉換
}
}
可以看到,最終的實現還是藉助了ConversionService,那麼ConversionService到底是啥呢?
4、ConversionService
介面定義
public interface ConversionService {
// 判斷是否能進行型別轉換
boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);
boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
// 進行型別轉換
@Nullable
<T> T convert(@Nullable Object source, Class<T> targetType);
@Nullable
Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
}
UML類圖
一般來說,實現了ConversionService
跟ConverterRegistry
會結合使用,對於這種xxxRegistry
我相信大家猜都能猜出來它是幹什麼的了,程式碼如下:
ConverterRegistry
// 就是在新增Converter或者ConverterFactory
public interface ConverterRegistry {
void addConverter(Converter<?, ?> converter);
<S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter);
void addConverter(GenericConverter converter);
void addConverterFactory(ConverterFactory<?, ?> factory);
void removeConvertible(Class<?> sourceType, Class<?> targetType);
}
ConfigurableConversionService
// 單純的整合了ConversionService以及ConverterRegistry的功能
public interface ConfigurableConversionService extends ConversionService, ConverterRegistry {
}
GenericConversionService
這個類已經是一個具體的實現類,可以直接使用,但是我們一般不會直接使用它,而是使用它的子類DefaultConversionService
,因為子類提供了很多預設的轉換器。
DefaultConversionService
public class DefaultConversionService extends GenericConversionService {
@Nullable
private static volatile DefaultConversionService sharedInstance;
public DefaultConversionService() {
addDefaultConverters(this);
}
public static ConversionService getSharedInstance() {
DefaultConversionService cs = sharedInstance;
if (cs == null) {
synchronized (DefaultConversionService.class) {
cs = sharedInstance;
if (cs == null) {
cs = new DefaultConversionService();
sharedInstance = cs;
}
}
}
return cs;
}
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
addScalarConverters(converterRegistry);
addCollectionConverters(converterRegistry);
converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
......
}
public static void addCollectionConverters(ConverterRegistry converterRegistry) {
......
}
private static void addScalarConverters(ConverterRegistry converterRegistry) {
converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
......
}
}
相比其父類GenericConversionService
,這個子類預設新增了很多的轉換器,這樣可以極大的方便我們進行開發,所以一般情況下我們都會使用這個類。
如何配置ConversionService
講了這麼多,那麼如何往容器中配置一個ConversionService呢?我們需要藉助Spring提供的一個ConversionServiceFactoryBean
。其程式碼如下:
public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {
@Nullable
private Set<?> converters;
@Nullable
private GenericConversionService conversionService;
public void setConverters(Set<?> converters) {
this.converters = converters;
}
@Override
public void afterPropertiesSet() {
this.conversionService = createConversionService();
ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
}
protected GenericConversionService createConversionService() {
return new DefaultConversionService();
}
@Override
@Nullable
public ConversionService getObject() {
return this.conversionService;
}
@Override
public Class<? extends ConversionService> getObjectType() {
return GenericConversionService.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
這個類的實現邏輯很簡單,ConversionServiceFactoryBean
建立完成後,在進行初始化時呼叫afterPropertiesSet
方法,建立一個DefaultConversionService
,然後將提供的converters
全部註冊到這個DefaultConversionService
中。所以我們進行如下的配置就行了
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
# 提供自己的converter,可以覆蓋預設的配置
<bean class="example.MyCustomConverter"/>
</set>
</property>
</bean>
總結
這篇文章中,我們學習了BeanWrapper,知道一個BeanWrapper其實就是一個Bean的包裝器,它對Bean包裝的目的是為了能操縱Bean中的屬性,所以它同時需要具備獲取以及設定Bean中的屬效能力,所以它也必須是一個屬性訪問器(PropertyAccessor),另外為了將各種不同型別的配置資料繫結到Bean的屬性上,那麼它還得具備屬性轉換的能力,因為它還得是一個型別轉換器(TypeConverter)。
通過上面的分析,我們知道Spring中將型別轉換的功能都委託給了一個TypeConverterDelegate
,這個委託類在進行型別轉換時會有兩套方案:
- PropertyEditor,這是Spring最初提供的方案,擴充套件了java中的PropertyEditor(java原先提供這個介面的目的更多是為了進行圖形化程式設計)
- ConversionService,Spring後來提供的一個進行型別轉換的體系,用來取代PropertyEditor,因為PropertyEditor有很大的侷限性,只能進行String->Object的轉換。
畫圖如下:
相關文章
- 深入Spring官網系列(十八):AOP詳細解析!Spring
- Spring原始碼系列:BeanWrapperSpring原始碼BeanAPP
- 深入Spring官網系列(十七):Java資料校驗SpringJava
- 詳細解析DES系列加密技術(二)加密
- 詳細解析DES系列加密技術(一)加密
- 網址(URL)的詳細解析
- 深入理解Spring系列之十三:IntrospectorCleanupListener解析SpringROS
- Hadoop 學習系列(二)之 HDFS 詳細解析Hadoop
- Hadoop 學習系列(三)之 YARN 詳細解析HadoopYarn
- Spring Security系列之Session管理(十四)SpringSession
- 深入理解javascript系列(十四):純函式JavaScript函式
- Spring官網Spring
- Spring原始碼系列(一)--詳細介紹bean元件Spring原始碼Bean元件
- CoreLocation框架詳細解析框架
- Semaphore最詳細解析
- RxLifecycle詳細解析
- SAP BOM詳細解析
- MySQL:排序(filesort)詳細解析MySql排序
- scala模式匹配詳細解析模式
- mysql日誌詳細解析MySql
- Mysql系列第十四講 檢視詳解MySql
- Java中泛型的詳細解析,深入分析泛型的使用方式Java泛型
- Spring security OAuth2 深入解析SpringOAuth
- 深入解析 Flink 細粒度資源管理
- Java 集合系列:Vector原始碼深入解析Java原始碼
- 超詳細 DNS 協議解析DNS協議
- Hadoop Yarn框架詳細解析HadoopYarn框架
- MyBatis詳細原始碼解析(上篇)MyBatis原始碼
- 詳細的AWR解析報告
- AFNetworking框架-詳細解析框架
- == 和 === 操作符詳細解析
- Spring bean詳細介紹SpringBean
- Spring-Aop詳細教程Spring
- Xshell 7官網免費版下載與安裝(詳細教程)
- Flutter系列(一)——詳細介紹Flutter
- Spring AOP全面詳解(超級詳細)Spring
- Spring MVC ControllerAdvice深入解析SpringMVCController
- XML從入門到深入(超詳細)XML