深入Spring官網系列(十四):BeanWrapper詳細解析

Hi丶ImViper發表於2020-10-30

BeanWrapper是Spring中一個很重要的介面,Spring在通過配置資訊建立物件時,第一步首先就是建立一個BeanWrapper。這篇文章我們就分析下這個介面,本文內容主要對應官網中的3.33.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使用的例項

  1. 我們在通過XML的方式對Spring中的Bean進行配置時,不管Bean中的屬性是何種型別,都是直接通過字面值來設定Bean中的屬性。那麼是什麼在這其中做轉換呢?這裡用到的就是PropertyEditor
  2. 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?

當設定屬性值時,少不了兩樣東西:

  1. 屬性訪問表示式:如listMap[0][0]
  2. 屬性值:

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主要實現了一下幾個功能

  1. 對Bean進行包裝
  2. 對Bean的屬性進行訪問以及設定
  3. 在操作屬性的過程中,必然涉及到型別轉換,所以還有型別轉換的功能

Java中的內建機制

在詳細瞭解BeanWrapperImpl前,必須要了解java中的一個機制:內省

核心概念

首先可以先了解下JavaBean的概念:一種特殊的類,主要用於傳遞資料資訊。這種類中的方法主要用於訪問私有的欄位,且方法名符合某種命名規則。如果在兩個模組之間傳遞資訊,可以將資訊封裝進JavaBean中,這種物件稱為“值物件”(Value Object),或“VO”。

因此JavaBean都有如下幾個特徵:

  1. 屬性都是私有的;
  2. 有無參的public構造方法;
  3. 對私有屬性根據需要提供公有的getXxx方法以及setXxx方法;
  4. getters必須有返回值沒有方法引數;setter值沒有返回值,有方法引數;

符合這些特徵的類,被稱為JavaBean;JDK中提供了一套API用來訪問某個屬性的getter/setter方法,這些API存放在java.beans中,這就是內省(Introspector)。

內省和反射的區別:

反射:Java反射機制是在執行中,對任意一個類,能夠獲取得到這個類的所有屬性和方法;它針對的是任意類
內省(Introspector):是Java語言對JavaBean類屬性、事件的處理方法

  1. 反射可以操作各種類的屬性,而內省只是通過反射來操作JavaBean的屬性
  2. 內省設定屬性值肯定會呼叫setter方法,反射可以不用(反射可直接操作屬性Field)
  3. 反射就像照鏡子,然後能看到.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類圖

在這裡插入圖片描述

一般來說,實現了ConversionServiceConverterRegistry會結合使用,對於這種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,這個委託類在進行型別轉換時會有兩套方案:

  1. PropertyEditor,這是Spring最初提供的方案,擴充套件了java中的PropertyEditor(java原先提供這個介面的目的更多是為了進行圖形化程式設計)
  2. ConversionService,Spring後來提供的一個進行型別轉換的體系,用來取代PropertyEditor,因為PropertyEditor有很大的侷限性,只能進行String->Object的轉換。

畫圖如下:

在這裡插入圖片描述

相關文章