1. 程式人生 > >《Spring官方文件》17.利用O/X對映器編組XML

《Spring官方文件》17.利用O/X對映器編組XML

原文連結 譯者:kdmhh

17. 利用O/X對映器編組XML

17.1 引言

這一章,我們將介紹Spring對物件/XML對映器的支援。物件/XML對映器或者簡稱O/X對映器,是一種在XML文件和物件之間互相轉換的行為。這種轉換過程也叫做XML編組或者XML序列化。本章將交替使用這兩種術語。

在O/X對映器中,編組是指把物件序列化為XML的過程。同樣,解組是指XML反序列化為物件,XML可以是DOM文件、輸入輸出流或者SAX處理程式。

使用Spring處理O/X對映器的好處是:

17.1.1 簡化的配置

Spring的bean工廠使得不需要構建JAXB、JiBX等第三方工具,就可以輕鬆配置編組器。編組器在應用程式上下文中可以配置為任意的bean。另外,基於XML的配置可應用於不同的編組器,並讓配置更加簡單化。

17.1.2 統一的介面

Spring的O/X對映器通過兩個全域性介面來實現:Marshaller 和 Unmarshaller。這些介面使你可以相對輕鬆切換O/X對映框架,你只需改動少量或者無需改動實現編組器的類。這種方式的另外優點是在同一個應用程式中以一種非侵入式的方式,將多種技術通過混合的方式來實現編組(比如,一些編組通過JAXB實現,而另外的通過Castor)。

17.1.3 統一的異常層次結構

Spring提供了基於底層O/X對映工具自身的異常層次結構,以XmlMappingException作為基礎異常。這樣原始異常被包裝到Spring執行時異常中,保證了沒有資訊丟失。

17.2 編組和解組

正如引言中所述,編組是將物件序列化為XML,解組是將XML流反序列化為物件。這一章節,我們講描述達到此目標的兩個Spring介面。

17.2.1 編組

Spring通過org.springframework.oxm.Marshaller介面抽象出所有編組操作,下面列出了主體方法。

public interface Marshaller {

/**
* Marshal the object graph with the given root into the provided Result.
*/
void marshal(Object graph, Result result) throws XmlMappingException, IOException;
}

Marshaller介面只有一個主體方法,這個方法將給定的物件編組為javax.xml.transform.Result。Result是一個標記介面,它表示一個XML的輸出抽象:具體的實現封裝了各種XML的表示,如下表所示:

Result實現類 封裝的XML表示
DOMResult org.w3c.dom.Node
SAXResult org.xml.sax.ContentHandler
StreamResult java.io.File, java.io.OutputStream, 或者 java.io.Writer

儘管marshal()方法接收一個普通物件作為它的第一個引數,但大多數編組器實現不能處理任意物件。相反地,物件類必須對映到檔案中,標記為註釋,並且註冊為編組器,或者有一個公共基類。請參閱本章節的其他內容,來決定你的O/X技術選擇。

17.2.2 解組

與Marshaller介面類似,org.springframework.oxm.Unmarshaller 介面如下:

public interface Unmarshaller {

/**
* Unmarshal the given provided Source into an object graph.
*/
Object unmarshal(Source source) throws XmlMappingException, IOException;
}

這介面也有一個方法,讀取給定的javax.xml.transform.Source 類(XML輸入抽象),返回物件。與Result類一樣,Source是一個有三個具體實現的標記介面,每一個都封裝了不同的XML表示,如下表所示。

Source實現類 封裝的XML表示
DOMSource org.w3c.dom.Node
SAXSource org.xml.sax.InputSource 和 org.xml.sax.XMLReader
StreamSource java.io.File,java.io.InputStream 或者java.io.Reader

儘管有兩個單獨的編組介面(Marshaller 和 Unmarshaller),但是所有在Spring-WS中的實現都在同一個類中。這意味著你也可以在applicationContext.xml引用同一個編組器類,並將其同時作為編組器和接編器。

17.2.3 XmlMappingException

Spring提供了基於底層O/X對映工具自身的異常層次結構,以XmlMappingException作為基礎異常。這樣原始異常被包裝到Spring執行時異常中,保證了沒有資訊丟失。

另外,儘管基於底層O/X的對映工具並未提供,但是,MarshallingFailureException 和 UnmarshallingFailureException提供了編組和解組之間的差異操作。

O/X對映異常層次結構如下所示:

17.3 使用編組器和解組器

Spring的OXM可以被使用在各種場景下,在下面的例項中,我們將使用它把Spring管理的應用設定編組為XML檔案,我們使用一個簡單的JavaBean來表示這種設定。

public class Settings {

private boolean fooEnabled;

public boolean isFooEnabled() {
return fooEnabled;
}

public void setFooEnabled(boolean fooEnabled) {
this.fooEnabled = fooEnabled;
}
}

Application類使用這個類儲存配置資訊,除了main方法,這個類有兩個方法:saveSettings()方法用來儲存配置bean到settings.xml檔案,loadSettings()方法用來再次載入這些配置。main()方法構造了Spring應用上下文,並呼叫了這兩個方法。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.Unmarshaller;

public class Application {

private static final String FILE_NAME = “settings.xml”;
private Settings settings = new Settings();
private Marshaller marshaller;
private Unmarshaller unmarshaller;

public void setMarshaller(Marshaller marshaller) {
this.marshaller = marshaller;
}

public void setUnmarshaller(Unmarshaller unmarshaller) {
this.unmarshaller = unmarshaller;
}

public void saveSettings() throws IOException {
FileOutputStream os = null;
try {
os = new FileOutputStream(FILE_NAME);
this.marshaller.marshal(settings, new StreamResult(os));
} finally {
if (os != null) {
os.close();
}
}
}

public void loadSettings() throws IOException {
FileInputStream is = null;
try {
is = new FileInputStream(FILE_NAME);
this.settings = (Settings) this.unmarshaller.unmarshal(new StreamSource(is));
} finally {
if (is != null) {
is.close();
}
}
}

public static void main(String[] args) throws IOException {
ApplicationContext appContext =
new ClassPathXmlApplicationContext(“applicationContext.xml”);
Application application = (Application) appContext.getBean(“application”);
application.saveSettings();
application.loadSettings();
}
}

Application 同時需要設定marshaller 和 unmarshaller兩個屬性,我們通過下面的applicationContext.xml來實現。

<beans>
    <bean id="application" class="Application">
        <property name="marshaller" ref="castorMarshaller" />
        <property name="unmarshaller" ref="castorMarshaller" />
    </bean>
    <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/>
</beans>

這個應用上下文使用了Castor,但是你可以使用任一在本章節中介紹的其他編組器介面。注意的是,Castor在預設情況下不需要任何進一步的配置,所以bean的定義相當簡單。同樣需要注意的是,CastorMarshaller同時實現了Marshaller 和 Unmarshaller,所以我們可以同時在marshaller 和 unmarshaller 兩個屬性中引用castorMarshaller bean。

這個樣例應用生成以下settings.xml檔案。

<?xml version="1.0" encoding="UTF-8"?>
<settings foo-enabled="false"/>

17.4 XML配置

可以使用OXM名稱空間標記更加簡潔地配置編組器。要使這些標籤可用,必須首先在XML配置檔案頭部引入適當的模板,注意下面“OXM”相關的配置:

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

目前,可以使用以下標籤:

每個標記將在各自的編組器部分中進行詳述。這裡據一個示例,下面是JAXB2編組器的配置方式:

<oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/>

17.5 JAXB

JAXB繫結編輯器將一個W3C XML模板轉換為一個或多個java類,jaxb.properties配置檔案或者其他資原始檔。JAXB還提供了從註解類生成模板的方法。

17.5.1 Jaxb2Marshaller

Jaxb2Marshaller類同時實現了Spring的Marshaller和Unmarshaller介面,它需要上下文路徑,你可以通過contextPath屬性設定。上下文路徑是一個以冒號分割的java包名列表,這些包包含從模板派生的類。它還提供了一個classestobe繫結屬性,允許你設定為類的陣列,這些類要被編組器所支援。通過向bean指定一個或多個模式資源來進行模式驗證,如下所示:

<beans>
    <bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="classesToBeBound">
            <list>
                <value>org.springframework.oxm.jaxb.Flight</value>
                <value>org.springframework.oxm.jaxb.Flights</value>
            </list>
        </property>
        <property name="schema" value="classpath:org/springframework/oxm/schema.xsd"/>
    </bean>

</beans>

基於XML的配置

jaxb2-marshaller標籤配置為org.springframework.oxm.jaxb.Jaxb2Marshaller,如下示例:

<oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.ws.samples.airline.schema"/>

編組器繫結的類列表也可以通過class-to-be-bound子標籤提供:

<oxm:jaxb2-marshaller id="marshaller">
    <oxm:class-to-be-bound name="org.springframework.ws.samples.airline.schema.Airport"/>
    <oxm:class-to-be-bound name="org.springframework.ws.samples.airline.schema.Flight"/>
    ...
</oxm:jaxb2-marshaller>

可用的屬性有:

屬性 描述 是否必需
id 編組器的id
contextPath JAXB上下文路徑

17.6 Castor

Castor XML對映是一個開源的XML繫結框架,它允許在java物件包含的資料和XML文件之間互相轉換。預設情況下,它不需要任何進一步的配置,但是可以通過對映檔案對Castor的行為進行更多的控制。

關於Castor更多的資訊,請參閱Castor站點。Spring整合Castor的類放在org.springframework.oxm.castor包下。

17.6.1 CastorMarshaller

與JAXB類似,CastorMarshaller也同時實現了Marshaller 和Unmarshaller 介面,它可以通過如下方式配置:

<beans>
    <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller" />
    ...
</beans>

17.6.2 對映器

儘管可以依賴Castor的預設編組行為,但是可能需要對它進行更多的控制,可以使用Castor對映檔案來進行控制,更多的資訊,請參考Castor XML對映

對映器可以通過mappingLocation屬性來設定,可以通過如下的classpath資源來表示:

<beans>
    <bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller" >
        <property name="mappingLocation" value="classpath:mapping.xml" />
    </bean>
</beans>

XML配置

通過castor-marshaller標籤配置org.springframework.oxm.castor.CastorMarshaller,下面是示例:

<oxm:castor-marshaller id="marshaller" mapping-location="classpath:org/springframework/oxm/castor/mapping.xml"/>

編組器例項可以通過兩種方式配置,一種是指定對映檔案的地址(通過mapping-location屬性),另一種是通過識別java POJOs(通過target-class或target-package屬性)對應的XML描述類。第二種方法通常與XML模板生成的XML程式碼結合使用。

可用的屬性如下:

屬性 描述 是否必需
id 編組器的id
encoding XML解碼的編碼格式
target-class Java類名,一個可以使用XML類描述符的POJO(通過程式碼生成)
target-package java包名,標識包含POJO及其相應的Castor XML描述類的包
mapping-location Castor XML對映檔案的路徑

17.7 JiBX

JiBX框架提供了與Hibernate提供的ORM類似的解決方案:定義了Java物件和XML相互轉換的規則。在準備好繫結和編譯類之後,JiBX繫結編譯器會增強類檔案,並新增程式碼來處理類和XML相互轉化的例項。

更多的資訊,請參考JiBX web site,Spring整合JiBX的類放在org.springframework.oxm.jibx包下。

17.7.1 JibxMarshaller

JibxMarshaller類同時實現了Marshaller和Unmarshaller介面,使用時,需要注入類名,可以通過targetClass或者bindingName屬性進行設定。下面的列子中,我們繫結類Flights:

<beans>
    <bean id="jibxFlightsMarshaller" class="org.springframework.oxm.jibx.JibxMarshaller">
        <property name="targetClass">org.springframework.oxm.jibx.Flights</property>
    </bean>
    ...
</beans>

JibxMarshaller配置為了單個類,如果想配置多個類,需要通過不同的targetClass屬性,配置多個JibxMarshaller。

XML配置

jibx-marshaller標籤配置了一個org.springframework.oxm.jibx.JibxMarshaller類,示例如下:

<oxm:jibx-marshaller id="marshaller" target-class="org.springframework.ws.samples.airline.schema.Flight"/>

可用的屬性如下:

屬性 描述 是否必需
id 編組器的id
target-class 編組器的目標類
bindingName 編組器使用的繫結名稱

17.8 XStream

XStream是一個簡單類庫,用於在物件和XML之間轉換。它不需要任何對映器就可以生成XML。

更多資訊,請參閱XStream web,Spring整合XStream的類在org.springframework.oxm.xstream包下。

17.8.1 XStreamMarshaller

XStreamMarshaller不需要任何配置,可以直接在上下文中配置。可以通過設定analiasMap屬性進一步設定XML,它由類的字串別名組成。

<beans>
    <bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
        <property name="aliases">
            <props>
                <prop key="Flight">org.springframework.oxm.xstream.Flight</prop>
            </props>
        </property>
    </bean>
    ...
</beans>

預設情況下,XStream允許任意類被解組,這可能導致安全漏洞。因此,不建議使用XStreamMarshaller從外部資源(即Web資源)中對XML進行解組,因為這會導致安全漏洞。如果確實使用了XStreamMarshaller從外部資源解組,那需要在XStreamMarshaller上設定supportedClasses屬性,如下:

<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
    <property name="supportedClasses" value="org.springframework.oxm.xstream.Flight"/>
    ...
</bean>

這能保證只有註冊類才可以進行解組。另外,還可以註冊自定義的轉換器,確保只有支援的類可以被解組。除了支援應該支援的轉換器外,你可能想要新增CatchAllConverter作為列表最後一個轉換器,預設的XStream轉換器具有較低的優先順序和安全漏洞,因此不會被呼叫。

注意,XStream是一個XML序列化庫,而不是一個數據繫結庫。因此,它僅支援有限的名稱空間,因此,它不適合在Web服務中使用。