探祕Spring的PropertyEditor

#探祕Spring的PropertyEditor 今天無心之中一位網友諮詢一個問題讓我有了深刻了解一下Spring的PropertyEditor機制的機會,雖然以前也大概知道些,可是都是知道它是什麼,殊不知道在Spring整個機制中它是如何執行的。今天就趁着中午閒暇時間看了一下Spring這方面的源碼。因此這裏主要是經過分析Spring的源碼來了解PropertyEditorjava

其實PropertyEditor是JDK裏面的java.beans下面的類,是提供AWT進行渲染用的。Spring經過利用該接口,來實現Bean的屬性轉換器。咱們在Spring的xml或者其餘途徑配置的bean屬性都是字符串類型的值,可是對應到每一個具體的屬性是各類類型的,Spring經過各類PropertyEditor來對各個屬性進行類型轉換,在Spring中的PropertyEditor並非直接實現PropertyEditor接口,是經過PropertyEditorSupport類屏蔽了一些Spring不須要的方法好比paintValue,從而對它們提供了默認的實現,因此Spring裏面的PropertyEditor都是在PropertyEditorSupport的基礎上實現的。那麼先看看Spring到底提供了哪些PropertyEditor的實現,下面截取了經過IDE工具找到的實現類截圖: spring

這裏面基本涉及到了全部JDK提供的類型,知道spring提供哪些PropertyEditor以後,還得須要這些PropertyEditor是怎麼嵌入Spring的。app

##PropertyEditorRegistry 這個是Spring框架內部將PropertyEditor嵌入Spring的方式,其中BeanWrapperImpl就是一個例子。先看看BeanWrapperImpl的類圖: 框架

能夠看到BeanWrapperImpl其實就是PropertyEditorRegistry的子類。其實PropertyEditorRegistry是一個接口,作具體事情的是PropertyEditorRegistrySupport。在PropertyEditorRegistrySupport中存在一個方法createDefaultEditors,這個方法就是初始化Spring中默認PropertyEditor。下面看看哪些是Spring默認的PropertyEditor: <!--lang:java--> private void createDefaultEditors() { this.defaultEditors = new HashMap<Class<?>, PropertyEditor>(64);ide

// 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(Pattern.class, new PatternEditor());
	this.defaultEditors.put(Properties.class, new PropertiesEditor());
	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());

	// 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);
	}
}

執行完這個那麼BeanWrapperImpl就具有上面類型的轉換功能,可能上面可以轉換的類型還不能知足咱們的需求,那麼能夠經過另外一種方式將PropertyEditor注入到Spring中。工具

##PropertyEditorRegistrar 該接口只有一個方法void registerCustomEditors(PropertyEditorRegistry registry),實現該方法就能夠往傳入的registry添加自定義的PropertyEditor,通常狀況下傳入的registryBeanWrapperImpl的實體,因此就等於將自定義的PropertyEditor注入到BeanWrapperImpl裏面。如今的問題是怎麼將PropertyEditorRegistrar注入到Spring中,那麼就能夠將自定的PropertyEditor添加到Spring中。Spring提供了一個類專門用來將第三方的PropertyEditor注入到它裏面,這個類就是CustomEditorConfigurer,先看看這個類的類圖: 發現它是實現了BeanFactoryPostProcessor接口,那麼將會在構造完BeanDefinition以後調用方法postProcessBeanFactory,只有整個方法是嵌入Spring的惟一途經,那麼看看這個方法裏面作了什麼事情:post

<!--lang:java-->
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
	if (this.propertyEditorRegistrars != null) {
		for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
			beanFactory.addPropertyEditorRegistrar(propertyEditorRegistrar);
		}
	}

	if (this.customEditors != null) {
		for (Map.Entry<String, ?> entry : this.customEditors.entrySet()) {
			String key = entry.getKey();
			Object value = entry.getValue();
			Class requiredType = null;

			try {
				requiredType = ClassUtils.forName(key, this.beanClassLoader);
				if (value instanceof PropertyEditor) {
					if (logger.isWarnEnabled()) {
						logger.warn("Passing PropertyEditor instances into CustomEditorConfigurer is deprecated: " +
								"use PropertyEditorRegistrars or PropertyEditor class names instead. " +
								"Offending key [" + key + "; offending editor instance: " + value);
					}
					beanFactory.addPropertyEditorRegistrar(
							new SharedPropertyEditorRegistrar(requiredType, (PropertyEditor) value));
				}
				else if (value instanceof Class) {
					beanFactory.registerCustomEditor(requiredType, (Class) value);
				}
				else if (value instanceof String) {
					Class editorClass = ClassUtils.forName((String) value, this.beanClassLoader);
					Assert.isAssignable(PropertyEditor.class, editorClass);
					beanFactory.registerCustomEditor(requiredType, editorClass);
				}
				else {
					throw new IllegalArgumentException("Mapped value [" + value + "] for custom editor key [" +
							key + "] is not of required type [" + PropertyEditor.class.getName() +
							"] or a corresponding Class or String value indicating a PropertyEditor implementation");
				}
			}
			catch (ClassNotFoundException ex) {
				if (this.ignoreUnresolvableEditors) {
					logger.info("Skipping editor [" + value + "] for required type [" + key + "]: " +
							(requiredType != null ? "editor" : "required type") + " class not found.");
				}
				else {
					throw new FatalBeanException(
							(requiredType != null ? "Editor" : "Required type") + " class not found", ex);
				}
			}
		}
	}
}

發現它都調用了beanFactory的方法將配置的PropertyEditorRegistrarcustomEditors注入到Spring的bean工廠裏面。咱們只要往spring容器中注入一個CustomEditorConfigurerBean,而後設置propertyEditorRegistrarscustomEditors屬性就能夠將自定義的PropertyEditor注入到Spring中了。固然你也能夠不用CustomEditorConfigurer類,自定義一個類實現BeanFactoryPostProcessor接口,而後按照CustomEditorConfigurer相似的路徑,也能夠達到目的。下面給出一個自定義PropertyEditor大體流程:ui

<!--lang:java-->
public class CustomPropertyEditor extends PropertyEditorSupport {

@Override
public void setAsText(String text) throws IllegalArgumentException {
    super.setAsText(text);
}

@Override
public Object getValue() {
    return super.getValue();
}
}

上面是定義了一個自定義PropertyEditor,下面須要將這個PropertyEditor注入到Spring裏面中:this

<!--lang:xml-->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
	<property name="customEditors">
		<map>
			<entry key="com.xx.foo.FooPojo" value="com.xx.foo.CustomPropertyEditor"/>
		</map>
	</property>
</bean>

Spring都會調用setAsText方法將該屬性的字面值設置進來,而後須要根據你規定的規範轉換成對應的屬性對象。code

下面再給出一個ClassEditor的實現,你們能夠仿照這個來實現:

<!--lang:java-->
public class ClassEditor extends PropertyEditorSupport {

private final ClassLoader classLoader;


/**
 * Create a default ClassEditor, using the thread context ClassLoader.
 */
public ClassEditor() {
	this(null);
}

/**
 * Create a default ClassEditor, using the given ClassLoader.
 * @param classLoader the ClassLoader to use
 * (or {@code null} for the thread context ClassLoader)
 */
public ClassEditor(ClassLoader classLoader) {
	this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}


@Override
public void setAsText(String text) throws IllegalArgumentException {
	if (StringUtils.hasText(text)) {
		setValue(ClassUtils.resolveClassName(text.trim(), this.classLoader));
	}
	else {
		setValue(null);
	}
}

@Override
public String getAsText() {
	Class clazz = (Class) getValue();
	if (clazz != null) {
		return ClassUtils.getQualifiedName(clazz);
	}
	else {
		return "";
	}
}

}
相關文章
相關標籤/搜索