Springを探検するPropertyEditor
23837 ワード
Springを探検するPropertyEditor
今日はつい最近の質問に対して、SpringのPropertyEditorのメカニズムを深く理解しました.以前は大体知っていましたが、それは何かを知っていますが、Spring全体のメカニズムの中ではどうやって実行されているのか分かりません.今日はお昼の合間にSpringのソースを見ました.だからここは主にSpringのソースコードを分析して
実は
この中は基本的にすべてのJDKが提供するタイプに関連しています.スプリングがどのような
PropertyEditor Registry
これはSpringフレーム内部にSpringを
PropertyEditor Registar
このインターフェースは一つの方法
次にもう一つの
今日はつい最近の質問に対して、SpringのPropertyEditorのメカニズムを深く理解しました.以前は大体知っていましたが、それは何かを知っていますが、Spring全体のメカニズムの中ではどうやって実行されているのか分かりません.今日はお昼の合間にSpringのソースを見ました.だからここは主にSpringのソースコードを分析して
PropertyEditor
を理解します.実は
PropertyEditor
はJDKの中のjava.beansの下のクラスで、AWTを提供してレンダリングするのです.Springは、このインターフェースを利用することにより、Benの属性変換器を実現する.Springのxmlまたは他の経路で構成したbean属性は文字列タイプの値であるが、各具体的な属性に対応する各種のタイプであり、Springは各種PropertyEditor
を通じて各属性をタイプ変換し、SpringのPropertyEditor
は直接的にPropertyEditor
インターフェースを実現していない.PropertyEditorSupport
類を通していくつかのSpringが不要な方法、例えばpaintValue
を遮蔽して、それらに対してデフォルトの実施を提供しているので、Spring内のPropertyEditor
は全部PropertyEditorSupport
に基づいて実現されている.まず、SpringがどのようなPropertyEditor
の実装を提供しているかを見てみてください.以下はIDEツールによって発見された実装クラスのスクリーンショットを切り取ります.この中は基本的にすべてのJDKが提供するタイプに関連しています.スプリングがどのような
PropertyEditor
を提供しているかを知った後、これらのPropertyEditor
がどのようにSpringに埋め込まれているかも必要です.PropertyEditor Registry
これはSpringフレーム内部にSpringを
PropertyEditor
に埋め込む方式であり、BeanWrapperImpl
は一例である.まず、BeanWrapperImpl
の類図を見てください.BeanWrapperImpl
が見られますが、実はPropertyEditorRegistry
のサブクラスです.実はPropertyEditorRegistry
は一つのインターフェースで、具体的なことをするのはPropertyEditorRegistrySupport
です.PropertyEditorRegistrySupport
には、SpringのデフォルトcreateDefaultEditors
を初期化する方法がある.次にどれがSpringのデフォルトのPropertyEditor
ですか?private void createDefaultEditors() {
this.defaultEditors = new HashMap, PropertyEditor>(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(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);
}
}
これを実行し終わったら、PropertyEditor
は上記のタイプの変換機能を備えています.上で変換できるタイプはまだ私達の需要を満たすことができないかもしれません.他の方法でProptyEditorをSpringに注入してもいいです.PropertyEditor Registar
このインターフェースは一つの方法
BeanWrapperImpl
だけであり、この方法を実現すると、入ってきたregistryにカスタムvoid registerCustomEditors(PropertyEditorRegistry registry)
を追加することができ、一般的に入ってきたPropertyEditor
はregistry
のエンティティであるので、カスタムBeanWrapperImpl
をPropertyEditor
に注入することと同じである.現在の問題は、BeanWrapperImpl
をどのようにSpringに注入すれば、所定のPropertyEditorRegistrar
をSpringに追加することができます.Springは、第三者のPropertyEditor
をその中に注入するためのクラスを提供しています.このクラスはPropertyEditor
です.まず、このクラスの類図を見てみます.CustomEditorConfigurer
インターフェースが実現されていることが分かりました.BeanFactoryPostProcessor
を構成した後に、方法BeanDefinition
を呼び出します.全体の方法だけがSpringに埋め込まれた唯一の経路です.この方法を見てみたら、何をしましたか?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 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);
}
}
}
}
}
postProcessBeanFactory
を呼び出す方法は、配置されたbeanFactory
およびPropertyEditorRegistrar
をSpringのbean工場に注入することができる.我々は、スプリング容器にcustomEditors
ビーンを注入し、CustomEditorConfigurer
及びpropertyEditorRegistrars
属性を設定すれば、カスタムcustomEditors
をSpringに注入することができる.もちろん、PropertyEditor
クラスを使わずに、クラスをカスタマイズしてCustomEditorConfigurer
インターフェースを実現してもいいです.そして、BeanFactoryPostProcessor
と同様の経路で目的を達成してもいいです.カスタムCustomEditorConfigurer
の概略的な流れを以下に示す.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に注入する必要がある.<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は、PropertyEditor
メソッドを呼び出して、この属性のワード値を設定し、規定の仕様に従って、対応する属性オブジェクトに変換する必要があります.次にもう一つの
setAsText
の実装を示します.これを真似して実現しても良いです. 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 "";
}
}
}