Springを探検するPropertyEditor

23837 ワード

Springを探検するPropertyEditor
今日はつい最近の質問に対して、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)を追加することができ、一般的に入ってきたPropertyEditorregistryのエンティティであるので、カスタムBeanWrapperImplPropertyEditorに注入することと同じである.現在の問題は、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 "";
        }
    }

    }