[spring]從頭開始-PropertyEditor

開發工具:IntelliJ IDEA 13

開發環境:jdk1.6.0_45

Framework:spring 3

一般bean 在 inject 時,一般String類型的資料都可以轉換為真實對應的型別,但若想要在當中做一些特別的加工處理的話,可以使用 CustomEditorConfigurer 這個類別來處理,以下就簡單來個範例如何做配置的動作,先簡單建立一個 bean

PropertyEditorBean.java

package foo.bar;

import org.springframework.context.support.GenericXmlApplicationContext;

import java.io.File;

import java.io.InputStream;

import java.net.URL;

import java.util.Date;

import java.util.List;

import java.util.Locale;

import java.util.Properties;

import java.util.regex.Pattern;

/**

 * Created by Hsu on 2014/7/22.

 */

public class PropertyEditorBean {

    private byte[] bytes; // ByteArrayPropertyEditor

    private Class cls; // ClassEditor

    private Boolean trueOrFalse; // CustomBooleanEditor

    private List<String> stringList; // CustomCollectionEditor

    private Date date; // CustomDateEditor

    private Float floatValue; // CustomNumberEditor

    private File file; // FileEditor

    private InputStream stream; // InputStreamEditor

    private Locale locale; // LocaleEditor

    private Pattern pattern; // PatternEditor

    private Properties properties; // PropertiesEditor

    private String trimString; // StringTrimmerEditor

    private URL url; // URLEditor

    public void setCls(Class cls) {

        System.out.println("Setting class: " + cls.getName());

        this.cls = cls;

    }

    public void setFile(File file) {

        System.out.println("Setting file: " + file.getName());

        this.file = file;

    }

    public void setLocale(Locale locale) {

        System.out.println("Setting locale: " + locale.getDisplayName());

        this.locale = locale;

    }

    public void setProperties(Properties properties) {

        System.out.println("Loaded " + properties.size() + " properties");

        this.properties = properties;

    }

    public void setUrl(URL url) {

        System.out.println("Setting URL: " + url.toExternalForm());

        this.url = url;

    }

    public void setBytes(byte[] bytes) {

        System.out.println("Adding " + bytes.length + " bytes");

        this.bytes = bytes;

    }

    public void setTrueOrFalse(Boolean trueOrFalse) {

        System.out.println("Setting Boolean: " + trueOrFalse);

        this.trueOrFalse = trueOrFalse;

    }

    public void setStringList(List<String> stringList) {

        System.out.println("Setting string list with size: "

                + stringList.size());

        this.stringList = stringList;

        for (String string: stringList) {

            System.out.println("String member: " + string);

        }

    }

    public void setDate(Date date) {

        System.out.println("Setting date: " + date);

        this.date = date;

    }

    public void setFloatValue(Float floatValue) {

        System.out.println("Setting float value: " + floatValue);

        this.floatValue = floatValue;

    }

    public void setStream(InputStream stream) {

        System.out.println("Setting stream: " + stream);

        this.stream = stream;

    }

    public void setPattern(Pattern pattern) {

        System.out.println("Setting pattern: " + pattern);

        this.pattern = pattern;

    }

    public void setTrimString(String trimString) {

        System.out.println("Setting trim string: " + trimString);

        this.trimString = trimString;

    }

    public static void main(String[] args) {

        GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

        ctx.load("classpath:builtin.xml");

        ctx.refresh();

        PropertyEditorBean bean =

                (PropertyEditorBean) ctx.getBean("builtInSample");

    }

}

上面有看到一些屬性已經準備好被inject,接著再設置spring configuration file

builtin.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:util="http://www.springframework.org/schema/util"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

http://www.springframework.org/schema/util

http://www.springframework.org/schema/util/spring-util-3.1.xsd">

    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">

        <property name="customEditors">

            <map>

                <entry key="java.util.Date">

<!-- 參數說明

CustomDateEditor(DateFormat dateFormat, boolean allowEmpty)

Create a new CustomDateEditor instance, using the given DateFormat for parsing and rendering.

-->

                    <bean class="org.springframework.beans.propertyeditors.CustomDateEditor">

                        <constructor-arg>

                            <bean class="java.text.SimpleDateFormat">

                                <constructor-arg value="yyyy-MM-dd" />

                            </bean>

                        </constructor-arg>

                        <constructor-arg value="true" />

                    </bean>

                </entry>

                <entry key="java.lang.String">

<!-- 參數說明

StringTrimmerEditor(boolean emptyAsNull)

Create a new StringTrimmerEditor.

-->

                    <bean

                            class="org.springframework.beans.propertyeditors.StringTrimmerEditor">

                        <constructor-arg value="true" />

                    </bean>

                </entry>

            </map>

        </property>

    </bean>

    <bean id="builtInSample" class="foo.bar.PropertyEditorBean">

        <property name="bytes">

            <value>Hello World</value>

        </property>

        <property name="cls">

            <value>java.lang.String</value>

        </property>

        <property name="trueOrFalse">

            <value>true</value>

        </property>

        <property name="stringList">

            <util:list>

                <value>String member 1</value>

                <value>String member 2</value>

            </util:list>

        </property>

        <!--

                    將轉換為 SimpleDateFormat 型態

        -->

        <property name="date">

            <value>2011-12-29</value>

        </property>

        <property name="floatValue">

            <value>123.45678</value>

        </property>

        <property name="file">

            <value>classpath:test.txt</value>

        </property>

        <property name="stream">

            <value>classpath:test.txt</value>

        </property>

        <property name="locale">

            <value>en_US</value>

        </property>

        <property name="pattern">

            <value>a*b</value>

        </property>

        <property name="properties">

            <value>

                name=foo

                age=19

            </value>

        </property>

        <!-- 因為前面有自訂了 CustomEditorConfigurer 的 customEditors 中的 java.lang.String

                        系統會自動將字串前後空格去除

         -->

        <property name="trimString">

            <value> String need trimming </value>

        </property>

        <property name="url">

            <value>http://www.springframework.org</value>

        </property>

    </bean>

</beans>

程式的執行結果:

Adding 11 bytes

Setting class: java.lang.String

Setting Boolean: true

Setting string list with size: 2

String member: String member 1

String member: String member 2

Setting date: Thu Dec 29 00:00:00 CST 2011

Setting float value: 123.45678

Setting file: test.txt

Setting stream: java.io.BufferedInputStream@50269997

Setting locale: 英文 (美國)

Setting pattern: a*b

Loaded 2 properties

Setting trim string: String need trimming q 11

Setting URL: http://www.springframework.org

值得注意的是 Setting date及 Setting trim string這兩個部分,其中的值已經藉由我們使用 CustomEditorConfigurer 而改變成我們所想要的型態!!! spring 還有許多內建的 PropertyEditors

from: http://docs.spring.io/spring/docs/2.0.x/reference/validation.html

Class

Explanation

ByteArrayPropertyEditor

Editor for byte arrays. Strings will simply be converted to their corresponding byte representations. Registered by default byBeanWrapperImpl.

ClassEditor

Parses Strings representing classes to actual classes and the other way around. When a class is not found, anIllegalArgumentException is thrown. Registered by default by BeanWrapperImpl.

CustomBooleanEditor

Customizable property editor for Boolean properties. Registered by default by BeanWrapperImpl, but, can be overridden by registering custom instance of it as custom editor.

CustomCollectionEditor

Property editor for Collections, converting any source Collection to a given target Collection type.

CustomDateEditor

Customizable property editor for java.util.Date, supporting a custom DateFormat. NOT registered by default. Must be user registered as needed with appropriate format.

CustomNumberEditor

Customizable property editor for any Number subclass like Integer, Long, Float, Double. Registered by default by BeanWrapperImpl, but can be overridden by registering custom instance of it as a custom editor.

FileEditor

Capable of resolving Strings to java.io.File objects. Registered by default by BeanWrapperImpl.

InputStreamEditor

One-way property editor, capable of taking a text string and producing (via an intermediate ResourceEditor and Resource) anInputStream, so InputStream properties may be directly set as Strings. Note that the default usage will not close the InputStream for you! Registered by default by BeanWrapperImpl.

LocaleEditor

Capable of resolving Strings to Locale objects and vice versa (the String format is [language]_[country]_[variant], which is the same thing the toString() method of Locale provides). Registered by default by BeanWrapperImpl.

PatternEditor

Capable of resolving Strings to JDK 1.5 Pattern objects and vice versa.

PropertiesEditor

Capable of converting Strings (formatted using the format as defined in the Javadoc for the java.lang.Properties class) to Propertiesobjects. Registered by default by BeanWrapperImpl.

StringTrimmerEditor

Property editor that trims Strings. Optionally allows transforming an empty string into a null value. NOT registered by default; must be user registered as needed.

URLEditor

Capable of resolving a String representation of a URL to an actual URL object. Registered by default by BeanWrapperImpl.

以上各自有自己不同的用途,可以自己運用,但如果上述的 PropertyEditors 沒有適合的,那就Creating a Custom PropertyEditor ,我們來看一下簡單的範例,先建立一個 class

Name.java

package foo.bar;

/**

 * Created by Hsu on 2014/7/15.

 */

public class Name {

    private String firstName;

    private String lastName;

    public Name() {

    }

    public Name(String firstName, String lastName) {

        this.firstName = firstName;

        this.lastName = lastName;

    }

    public String getFirstName() {

        return firstName;

    }

    public void setFirstName(String firstName) {

        this.firstName = firstName;

    }

    public String getLastName() {

        return lastName;

    }

    public void setLastName(String lastName) {

        this.lastName = lastName;

    }

    //最後配合 System.out.println 輸出的結果

    public String toString() {

        return "First name: " + firstName + " - Last name: " + lastName;

    }

}

可以看到上面設定了兩個屬性 first name and last name,現在我們打算inject 一個String,中間的規則是遇到空字串後自動分成 first name and last name,並將其inject,我們可以必須建立專屬的 PropertyEditor,使用 extends PropertyEditorSupport 來完成,下面就是範例類別:

NamePropertyEditor.java

package foo.bar;

import java.beans.PropertyEditorSupport;

/**

 * Created by Hsu on 2014/7/15.

 */

public class NamePropertyEditor extends PropertyEditorSupport {

    @Override

    public void setAsText(String text) throws IllegalArgumentException {

        String[] name = text.split("\\s");

        Name result = new Name(name[0], name[1]);

        setValue(result);

    }

}

上面的類別我們繼承了 PropertyEditorSupport 這個類,並改寫其中的 setAsText 的方法,程式內的邏輯為,將輸入的String text,再藉由 setValue 將轉化後 Name result 的結果 inject,如此一來我們已經完成了自己的 PropertyEditor,但是要使用時,必須要告訴 spring 要使用他,所以必須要 register the editor in Spring’s

ApplicationContext,底下的 spring configuration file 就是配置方式:

spring-config.xml

..

..標頭的部分省略

    <bean name="customEditorConfigurer"

          class="org.springframework.beans.factory.config.CustomEditorConfigurer">

        <property name="customEditors">

            <map>

                <!-- 要執行轉換的類別 -->

                <entry key="foo.bar.Name">

                    <!-- 自訂的 PropertyEditor 轉換邏輯在這兒-->

                    <bean class="foo.bar.NamePropertyEditor"/>

                </entry>

            </map>

        </property>

    </bean>

    <bean id="exampleBean" class="foo.bar.CustomEditorExample">

        <property name="name">

            <value>Clarence Ho</value>

        </property>

    </bean>

..結尾省略

最後我們來建立一個演繹類別 CustomEditorExample

CustomEditorExample.java

package foo.bar;

import org.springframework.context.support.GenericXmlApplicationContext;

/**

 * Created by Hsu on 2014/7/15.

 */

public class CustomEditorExample {

    private Name name;

    public static void main(String[] args) {

        GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

        ctx.load("classpath:spring-config.xml");

        ctx.refresh();

        CustomEditorExample bean = (CustomEditorExample) ctx.getBean("exampleBean");

        System.out.println(bean.getName());

    }

    public Name getName() {

        return name;

    }

    public void setName(Name name) {

        this.name = name;

    }

}

程式在注入時,因為我們已經有使用 xml 將自訂的 CustomPropertyEditor 註冊到 ApplicationContext,所以當inject 遇到 foo.bar.Name 類別的資料時,就會去 RUN 我們的規則,所以最後輸出的結果為:

First name: Clarence - Last name: Ho

以上就是採用xml設定並搭配我們自訂的 PropertyEditor 的方式,當然也可以使用寫 java code 的方式去做設定喔。

demo程式(右鍵另開視窗下載)