Spring之PropertyEditor

概述PropertyEditor簡介內置PropertyEditor自定義PropertyEditorCustomEditorConfigurerDate屬性編輯自定義屬性編輯器參考文獻html

概述

在 Spring 配置文件裏,咱們每每經過字面值爲 Bean 各類類型的屬性提供設置值:不論是 double 類型仍是 int 類型,在配置文件中都應字符串類型的字面值。BeanWrapper 填充 Bean 屬性時如何將這個字面值轉換爲對應的 double 或 int 等內部類型呢 ?咱們能夠隱約地感受到必定有一個轉換器在其中起做用,這個轉換器就是屬性編輯器。java

「屬性編輯器」這個名字可能 會讓人誤覺得是一個帶用戶界面的輸入器,其實屬性編輯器不必定非得有用戶界面,任何實現 java.beans.PropertyEditor 接口的類都是屬性編輯器。屬性編輯器的主要功能就是將外部的設置值轉換爲 JVM 內部的對應類型,因此屬性編輯器其實就是一個類型轉換器。web

轉回剛纔說到的問題:BeanWrapper 填充屬性時是如何進行屬性編輯的?恰好上節 Spring IoC之BeanWrapper 講述了關於 BeanWrapper 設置屬性的過程,在最後設置屬性值的時候,在 AbstractNestablePropertyAccessor 類中有這麼一段代碼:spring

valueToApply = this.convertForProperty(tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
複製代碼

該段代碼就是獲取 xml 文件中屬性的設置值,繼續往下看。api

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");

    PropertyChangeEvent pce;
    try {
        return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td);
    } 
    .......
}
複製代碼

convertIfNecessary()方法中執行時會跳轉到 TypeConverterDelegate 類中的 convertIfNecessary()方法,其中會涉及到 PropertyEditor 接口的實現類。關於 TypeConverterDelegate 類在 BeanWrapper 的構造方法中有說起到,那個時候就已經構建了關於當前 bean 對象的屬性編輯器。關於 TypeConverterDelegate 類想要了解更多能夠參看:SpringMVC類型轉換器、屬性編輯器PropertiesEditor源碼分析CustomDateEditor源碼分析TypeConverterDelegate源碼分析數組

PropertyEditor簡介

PropertyEditor 是屬性編輯器的接口,它規定了將外部設置值轉換爲內部 JavaBean 屬性值的轉換接口方法。PropertyEditor 主要的接口方法說明以下:安全

  • Object getValue() :返回屬性的當前值。基本類型被封裝成對應的封裝類實例;
  • void setValue(Object newValue) :設置屬性的值,基本類型以封裝類傳入;
  • String getAsText() :將屬性對象用一個字符串表示,以便外部的屬性編輯器能以可視化的方式顯示。缺省返回 null,表示該屬性不能以字符串表示;
  • void setAsText(String text) :用一個字符串去更新屬性的內部值,這個字符串通常從外部屬性編輯器傳入;
  • String[] getTags() :返回表示有效屬性值的字符串數組(如boolean屬性對應的有效Tag爲true和false),以便屬性編輯器能如下拉框的方式顯示出來。缺省返回null,表示屬性沒有匹配的字符值有限集合;
  • String getJavaInitializationString() :爲屬性提供一個表示初始值的字符串,屬性編輯器以此值做爲屬性的默認值。
  • void paintValue(Graphics gfx,Rectangle box):將值的表示形式繪製到屏幕空間的給定區域中。請注意,propertyEditor 負責本身進行剪切,以使其適合給定的矩形。若是 PropertyEditor 不接受繪畫請求(請參見isPaintable),則此方法應爲靜默noop。給定的 Graphics 對象將具備父容器的默認字體,顏色等。PropertyEditor 能夠更改圖形屬性,例如字體和顏色,而且不須要還原舊值。
  • Component getCustomEditor(): PropertyEditor能夠選擇提供可編輯其屬性值的完整自定義組件。PropertyEditor的責任是將自身鏈接到其編輯器Component自己,並經過觸發PropertyChange事件來報告屬性值更改。

能夠看出 PropertyEditor 接口方法是內部屬性值和外部設置值的溝通橋樑。此外,咱們能夠很容易地發現該接口的不少方法是專爲 IDE 中的可視化屬性編輯器提供的:如 getTags()getJavaInitializationString()paintValue()等。ruby

簡單的 PropertyEditor 可能僅支持 getAsText 和 setAsText 方法,而無需支持(例如)paintValue 或 getCustomEditor。更復雜的類型可能沒法支持 getAsText 和 setAsText,但將支持 paintValue 和 getCustomEditor。 bash

Java 爲 PropertyEditor 提供了一個方便類:PropertyEditorSupport,該類實現了 PropertyEditor 接口並提供默認實現,通常狀況下,用戶能夠經過擴展這個方便類設計本身的屬性編輯器。 併發

內置PropertyEditor

Spring IoC之BeanWrapper 一文中有提到關於 BeanWrapper 的使用場景,咱們看一下當時構建 BeanWrapper 對象的方法。

public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
    BeanWrapperImpl bw = new BeanWrapperImpl();
    this.beanFactory.initBeanWrapper(bw);
    Constructor<?> constructorToUse = null;
    ConstructorResolver.ArgumentsHolder argsHolderToUse = null;
    Object[] argsToUse = null;

    ......

    Assert.state(argsToUse != null"Unresolved constructor arguments");
    bw.setBeanInstance(this.instantiate(beanName, mbd, constructorToUse, argsToUse));
    return bw;
}
複製代碼

最開始執行了 initBeanWrapper()方法,該方法用來配置一些必要數據,繼續往下看。該方法具體實如今 AbstractBeanFactory 類中,定義以下:

protected void initBeanWrapper(BeanWrapper bw) {
    bw.setConversionService(this.getConversionService());
    this.registerCustomEditors(bw);
}

protected void registerCustomEditors(PropertyEditorRegistry registry) {
    PropertyEditorRegistrySupport registrySupport = registry instanceof PropertyEditorRegistrySupport ? (PropertyEditorRegistrySupport)registry : null;
    if (registrySupport != null) {
        registrySupport.useConfigValueEditors();
    }

    if (!this.propertyEditorRegistrars.isEmpty()) {
        Iterator var3 = this.propertyEditorRegistrars.iterator();

        while(var3.hasNext()) {
            PropertyEditorRegistrar registrar = (PropertyEditorRegistrar)var3.next();

            try {
                registrar.registerCustomEditors(registry);
            } catch (BeanCreationException var9) {
                Throwable rootCause = var9.getMostSpecificCause();
                if (rootCause instanceof BeanCurrentlyInCreationException) {
                    BeanCreationException bce = (BeanCreationException)rootCause;
                    String bceBeanName = bce.getBeanName();
                    if (bceBeanName != null && this.isCurrentlyInCreation(bceBeanName)) {
                        if (this.logger.isDebugEnabled()) {
                            this.logger.debug("PropertyEditorRegistrar [" + registrar.getClass().getName() + "] failed because it tried to obtain currently created bean '" + var9.getBeanName() + "': " + var9.getMessage());
                        }

                        this.onSuppressedException(var9);
                        continue;
                    }
                }

                throw var9;
            }
        }
    }

    if (!this.customEditors.isEmpty()) {
        this.customEditors.forEach((requiredType, editorClass) -> {
            registry.registerCustomEditor(requiredType, (PropertyEditor)BeanUtils.instantiateClass(editorClass));
        });
    }

}
複製代碼

該方法的功能是把容器中默認的 PropertyEditor 和註冊到容器中的自定義 PropertyEditor 複製到 BeanWrapper中,來輔助構造子注入時的值轉換操做以及爲後面其它屬性的注入值轉換作準備,爲何要把這些 PropertyEditor複製到各個BeanWrapper中?由於 PropertyEditor 是單例模式,非線程安全的接口,每一個 BeanWrapper 複製一份能夠消除高併發下的狀態同步開銷。

關於那些內置 PropertyEditor 在哪裏添加進去的,這裏須要關注一下 PropertyEditorRegistrySupport 類,在該類中有一個 createDefaultEditors()方法,它會將一些必要的 PropertyEditor 提早加進去。

private void createDefaultEditors() {
    this.defaultEditors = new HashMap(64);
    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(Path.class, new PathEditor());
    this.defaultEditors.put(Pattern.class, new PatternEditor());
    this.defaultEditors.put(Properties.class, new PropertiesEditor());
    this.defaultEditors.put(Reader.class, new ReaderEditor());
    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());
    this.defaultEditors.put(ZoneId.class, new ZoneIdEditor());
    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));
    this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
    this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());
    this.defaultEditors.put(Character.TYPE, new CharacterEditor(false));
    this.defaultEditors.put(Character.class, new CharacterEditor(true));
    this.defaultEditors.put(Boolean.TYPE, new CustomBooleanEditor(false));
    this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));
    this.defaultEditors.put(Byte.TYPE, new CustomNumberEditor(Byte.class, false));
    this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
    this.defaultEditors.put(Short.TYPE, new CustomNumberEditor(Short.class, false));
    this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
    this.defaultEditors.put(Integer.TYPE, new CustomNumberEditor(Integer.class, false));
    this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
    this.defaultEditors.put(Long.TYPE, new CustomNumberEditor(Long.class, false));
    this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
    this.defaultEditors.put(Float.TYPE, new CustomNumberEditor(Float.class, false));
    this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
    this.defaultEditors.put(Double.TYPE, 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));
    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);
    }

}
複製代碼

此外還內置了 一些 Resource 相關的 PropertyEditor,代碼在 ResourceEditorRegistrar 類的registerCustomEditors 方法中:

PropertyEditorRegistrar registrar = (PropertyEditorRegistrar)var3.next();
registrar.registerCustomEditors(registry);
複製代碼

具體實如今 ResourceEditorRegistrar 類中:

public void registerCustomEditors(PropertyEditorRegistry registry) {
    ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
    this.doRegisterEditor(registry, Resource.class, baseEditor);
    this.doRegisterEditor(registry, ContextResource.class, baseEditor);
    this.doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
    this.doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
    this.doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
    this.doRegisterEditor(registry, Path.class, new PathEditor(baseEditor));
    this.doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
    this.doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));
    ClassLoader classLoader = this.resourceLoader.getClassLoader();
    this.doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
    this.doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
    this.doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));
    if (this.resourceLoader instanceof ResourcePatternResolver) {
        this.doRegisterEditor(registry, Resource[].class, new ResourceArrayPropertyEditor((ResourcePatternResolver)this.resourceLoader, this.propertyResolver));
    }

}
複製代碼

ResourceEditorRegistrar 是框架內置的一個 PropertyEditor 註冊器,它是一個 BFPP,在ClasspathXmlApplicationContext 啓動時就會把它添加到容器中。

在內置的 PropertyEditor 中有這麼一個類 URLEditor,實際使用代碼以下:

@Test
public void propertyEditorTest() throws Exception{
    PropertyEditor propertyEditor = new URLEditor();
    propertyEditor.setAsText("http://www.springframework.org");
    Object value = propertyEditor.getValue();
    assertTrue(value instanceof URL);
    URL url = (URL) value;
    assertEquals(url.toString(),propertyEditor.getAsText());
}
複製代碼

簡單來講就是字符串和其餘對象的類型轉換器,經過 setAsText ()設置,再經過 getValue()獲取轉換值。

自定義PropertyEditor

當 Spring 內置的 PropertyEditor 沒法知足咱們的要求的時候,咱們能夠根據 Spring 提供的擴展機制來自定義 PropertyEditor,下面經過一個例子來介紹如何實現自定義的 PropertyEditor,這個 PropertyEditor 是一個時間相關的 Editor,它能夠一個知足特定時間格式的字符串轉換成日期對象。

CustomEditorConfigurer

在講述案例前咱們須要瞭解一下 org.springframework.beans.factory.config.CustomEditorConfigurer類, 一般,在使用容器以前在容器中註冊一個屬性編輯器。CustomEditorConfigurer類被實現爲內置的 bean工廠後處理器,供您在實例化任何 bean 以前註冊自定義屬性編輯器。

該類有兩種使用方式:

一、從Spring 2.0開始,推薦的用法是使用自定義 PropertyEditorRegistrar實現,這些實現又將給定的註冊任何所需的編輯器實例 registry。每一個 PropertyEditorRegistrar 能夠註冊任意數量的自定義編輯器。

 <bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
   <property name="propertyEditorRegistrars">
     <list>
       <bean class="mypackage.MyCustomDateEditorRegistrar"/>
       <bean class="mypackage.MyObjectEditorRegistrar"/>
     </list>
   </property>
 </bean>
複製代碼

二、經過 customEditors 屬性註冊 PropertyEditor 類, Spring 將爲每次編輯嘗試建立它們的新實例,例如:

 <bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
   <property name="customEditors">
     <map>
       <entry key="java.util.Date" value="mypackage.MyCustomDateEditor"/>
       <entry key="mypackage.MyObject" value="mypackage.MyObjectEditor"/>
     </map>
   </property>
 </bean>
複製代碼

注意:如今不鼓勵經過 customEditors 屬性註冊 PropertyEditor Bean 實例,由於 PropertyEditor 是非線程安全的,所以對於每次編輯嘗試,實例都必須同步。 這意味着在在高併發壞境下這裏的同步會對應用的性能形成比較大的影響。因此這種方式是不推薦的,事實上這種方式已經被 Spring 打上 deprecated 標籤了。若是您須要控制 PropertyEditor 的實例化過程,請使用 PropertyEditorRegistrar 進行註冊。

Date屬性編輯

首先定義一個 Boy 類,其中包含時間格式的屬性:

public class Boy {
    private String name;
    private Date birthday;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Boy{" +
                "name='" + name + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}
複製代碼

定義一個 PropertyEditor,框架中提供了一個 PropertyEditor 基類 PropertyEditorSupport,直接繼承這個類能夠省去一部分代碼,代碼以下:

public class DateEditor extends PropertyEditorSupport {
    private DateFormat dateFormat;

    public DateFormat getDateFormat() {
        return dateFormat;
    }

    public void setDateFormat(DateFormat dateFormat) {
        this.dateFormat = dateFormat;
    }

    @Override
    public void setAsText(String s) throws IllegalArgumentException {
        try {
            Object value = dateFormat.parse(s);
            setValue(value);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String getAsText() {
        if (getValue() instanceof Date){
            Date date = (Date) getValue();
            return dateFormat.format(date);
        }
        return super.getAsText();
    }
}
複製代碼

還須要定義一個 PropertyEditorRegistrar 實現類,它的職責是用來註冊 PropertyEditor,並且能夠註冊任意數量的 PropertyEditor。

public class DateFormatEditor implements PropertyEditorRegistrar {
    private String dateFormat;

    public String getDateFormat() {
        return dateFormat;
    }

    public void setDateFormat(String dateFormat) {
        this.dateFormat = dateFormat;
    }

    @Override
    public void registerCustomEditors(PropertyEditorRegistry propertyEditorRegistry) {
        DateEditor dateEditor = new DateEditor();
        dateEditor.setDateFormat(new SimpleDateFormat(dateFormat));
        propertyEditorRegistry.registerCustomEditor(Date.class,dateEditor);

    }

}
複製代碼

在註冊器的 registerCustomEditors 方法中把 DateEditor 註冊到容器中而且和 Date 類型綁定,要確保 DateEditor 對象時臨時建立的對象而不是一個全局對象,不然同樣可能會引起性能問題。把日期格式經過dateFormat 字段注入,須要注意的是先將注入的字符串轉換爲 DateEditor 中定義的 DateFormat 類型。

接着就須要配置 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="boy" class="com.msdn.bean.Boy">
        <property name="name" value="hresh" />
        <property name="birthday" value="1996年1月1日" />
    </bean>

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

        <property name="propertyEditorRegistrars">
            <list>
                <bean class="com.msdn.editor.DateFormatEditor">
                    <property name="dateFormat" value="yyyy年MM月dd日" />
                </bean>
            </list>
        </property>
    </bean>

</beans>
複製代碼

這種方式下,每一個 BeanWrapper 都會註冊不一樣的 PropertyEditor,不會有高併發下的性能問題,並且用法也比較靈活,因此 Spring 的推薦是經過 propertyEditorRegistrars 注入來完成 PropertyEditor 的自定義。

測試代碼以下:

@Test
public void updateBean() throws MalformedURLException {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans_editor.xml");

    Boy boy = (Boy) context.getBean("boy");
    System.out.println(boy);
    System.out.println(boy.getBirthday());

}
複製代碼

執行結果爲:

Boy{name='hresh', birthday=Mon Jan 01 00:00:00 CST 1996}
Mon Jan 01 00:00:00 CST 1996
複製代碼

看到注入的屬性被轉換爲 Date 類型填充到 Boy 對象中,也獲得正確的輸出結果,可是想了一個問題:若是咱們要獲取原值該怎麼獲取?只有經過 DateEditor 類中的 getAsText()方法才能獲取到屬性轉換前的值,可是問題是獲取不到 DateEditor 的實例對象。一番查看在 beanFactory 中看到一點有用信息,可是沒法後續處理。

查了一些資料,發現經過 BeanWrapper 和 PropertyEditor 結合使用也能對屬性進行轉換,不過這已經不涉及到 XML 文件的使用。代碼以下:

@Test
public void testCustomEditorForSingleProperty(){
    Boy boy = new Boy();
    BeanWrapperImpl beanWrapper = new BeanWrapperImpl(boy);
    DateEditor dateEditor = new DateEditor();
    dateEditor.setDateFormat(new SimpleDateFormat("yyyy年MM月dd日"));
    beanWrapper.registerCustomEditor(Date.class,"birthday",dateEditor);
    beanWrapper.setPropertyValue("name","hresh");
    beanWrapper.setPropertyValue("birthday","1997年1月1日");

    System.out.println(boy.getBirthday());
    System.out.println(beanWrapper.getPropertyValue("birthday"));
    System.out.println(dateEditor.getAsText());

    beanWrapper.setPropertyValue("name","hresh");
    beanWrapper.setPropertyValue("birthday","1998年1月1日");
    System.out.println(boy.getBirthday());
    System.out.println(dateEditor.getAsText());
}
複製代碼

執行結果爲:

Wed Jan 01 00:00:00 CST 1997
Wed Jan 01 00:00:00 CST 1997
19970101
Thu Jan 01 00:00:00 CST 1998
19980101
複製代碼

若是有大佬瞭解上述提到的問題,請不吝賜教。

自定義屬性編輯器

Spring bean之間的關係 一文中講述了 bean 之間的幾種關係,其中介紹的案例中的 Person 類中有個 Car 對象屬性,在依賴和引用關係中均可以實現,接下來咱們再介紹一種實現方式。

爲 Car 類提供一個自定義的屬性編輯器,而後經過字面值爲 Person 的 car 屬性提供配置值。

如今,咱們來爲 Car 編寫一個自定義的屬性編輯器,其代碼以下所示:

public class CustomCarEditor extends PropertyEditorSupport {

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        if(text == null || text.indexOf(",") == -1){
            throw new IllegalArgumentException("設置的字符串格式不正確");
        }
        String[] infos = text.split(",");
        Car car = new Car();
        car.setBrand(infos[0]);
        car.setMaxSpeed(Integer.valueOf(infos[1]));
        car.setPrice(Double.valueOf(infos[2]));
        car.setColor(infos[3]);

        setValue(car);
    }
}
複製代碼

CustomCarEditor 很簡單,它僅覆蓋 PropertyEditorSupport 類的 setAsText(String text) 方法,該方法負責將配置文件以字符串提供的字面值轉換爲 Car 對象。字面值採用逗號分隔的格式同時爲 brand、maxSpeed、price 和 color屬性值提供設置值,setAsText()方法解析這個字面值並生成對應的 Car 對象。因爲咱們並不須要將 Person 內部的 car 屬性反顯到屬性編輯器中,所以不須要覆蓋 getAsText()方法。

定義 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


    <!--③該屬性將使用②處的屬性編輯器完成屬性填充操做-->
    <bean id="person" class="com.msdn.bean.Person" >
        <property name="name" value="hresh" />
        <property name="car" value="東風,299,5400,銀色" />
        <property name="desc" value="xxxxx" />
    </bean>

    <!--①配置自動註冊屬性編輯器的CustomEditorConfigurer -->
    <bean id="editorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map><!--②-1屬性編輯器對應的屬性類型 2對應的屬性編輯器Bean-->
                <entry key="com.msdn.bean.Car" value="com.msdn.editor.CustomCarEditor" />
            </map>
        </property>

    </bean>

</beans>
複製代碼

在①處,咱們定義了用於註冊自定義屬性編輯器的 CustomEditorConfigurer,Spring 容器將經過反射機制自動調用這個 Bean。CustomEditorConfigurer 經過一個 Map 屬性定義須要自動註冊的自定義屬性編輯器。在②處,咱們爲 Car 類型指定了對應屬性編輯器 CustomCarEditor,注意鍵是屬性類型,而值是對應的屬性編輯器Bean,而不是屬性編輯器的類名。

最精彩的部分固然是③處的配置,咱們原來經過一個元素標籤配置好 car Bean,而後在 person 的中經過 ref 引用 car Bean,可是如今咱們直接經過 value 爲 car 屬性提供配置。BeanWrapper 在設置person 的car屬性時,它將檢索自定義屬性編輯器的註冊表,當發現 Car 屬性類型擁有對應的屬性編輯器 CustomCarEditor 時,它就會利用 CustomCarEditor 將"東風,299,5400,銀色"轉換爲 Car 對象。

測試代碼以下:

@Test
public void updateBean(){
    ApplicationContext context = new ClassPathXmlApplicationContext("person_car.xml");

    Person person = (Person) context.getBean("person");
    System.out.println(person);
}
複製代碼

執行結果爲:

調用Car類的無參構造函數
Person{name='hresh', 擁有一輛car=Car{maxSpeed=299, price=5400.0, brand='東風', color='銀色'}}
複製代碼

參考文獻

https://www.cnblogs.com/Tony-Mu/articles/2565527.html

https://blog.csdn.net/pentiumchen/article/details/44026575

https://www.jianshu.com/p/aeb97395d9c5

相關文章
相關標籤/搜索