Spring 3.0 引入 OXM (Object/XML Mapping) 抽象層,統一了 Castor、JAXB、JiBX、XStream、XMLBeans 等主流映射框架的 API。本文通過一個完整的 Maven 風格項目,演示如何利用 Spring OXM + Castor 實現 POJO 與 XML 的編組與解組,涵蓋配置、映射文件、依賴管理、異常處理等工程化細節。
src/
├── main/
│ ├── java/
│ │ └── com/xyz/
│ │ ├── SimpleBean.java # POJO
│ │ └── OXMExample.java # 演示入口
│ └── resources/
│ ├── applicationContext.xml # Spring 配置
│ └── mapping.xml # Castor 映射文件
└── test/
└── java/... # 單元測試
package com.xyz;
public class SimpleBean {
private int age;
private boolean executive;
private String jobDescription;
private String name;
// Getter/Setter 省略
// 建議生成 toString() 便於驗證
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 演示 Bean,注入 marshaller/unmarshaller -->
<bean id="oxmExample" class="com.xyz.OXMExample">
<property name="marshaller" ref="castorMarshaller" />
<property name="unmarshaller" ref="castorMarshaller" />
</bean>
<!-- Castor 實現,指定映射文件 -->
<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller">
<property name="mappingLocation" value="classpath:mapping.xml" />
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<mapping>
<class name="com.xyz.SimpleBean">
<map-to xml="simplebean"/> <!-- XML 根元素 -->
<field name="age" type="integer">
<bind-xml name="age" node="element"/>
</field>
<field name="executive" type="boolean">
<bind-xml name="is-executive" node="element"/>
</field>
<field name="jobDescription" type="string">
<bind-xml name="job" node="element"/>
</field>
<field name="name" type="string">
<bind-xml name="name" node="element"/>
</field>
</class>
</mapping>
package com.xyz;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.Unmarshaller;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
public class OXMExample {
private static final String FILE = "simplebean.xml";
private SimpleBean simpleBean;
private Marshaller marshaller;
private Unmarshaller unmarshaller;
// Setter 注入
public void setMarshaller(Marshaller m) { this.marshaller = m; }
public void setUnmarshaller(Unmarshaller u) { this.unmarshaller = u; }
public void save() throws IOException {
try (FileOutputStream os = new FileOutputStream(FILE)) {
marshaller.marshal(simpleBean, new StreamResult(os));
}
}
public void load() throws IOException {
try (FileInputStream is = new FileInputStream(FILE)) {
simpleBean = (SimpleBean) unmarshaller.unmarshal(new StreamSource(is));
}
}
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
OXMExample ex = (OXMExample) ctx.getBean("oxmExample");
ex.simpleBean = createBean();
ex.save();
ex.load();
System.out.println("name: " + ex.simpleBean.getName());
System.out.println("job: " + ex.simpleBean.getJobDescription());
System.out.println("age: " + ex.simpleBean.getAge());
System.out.println("executive: " + ex.simpleBean.isExecutive());
}
private static SimpleBean createBean() {
SimpleBean b = new SimpleBean();
b.setName("Mister Jones");
b.setJobDescription("Janitor");
b.setAge(35);
b.setExecutive(false);
return b;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<simplebean>
<age>35</age>
<is-executive>false</is-executive>
<job>Janitor</job>
<name>Mister Jones</name>
</simplebean>
name: Mister Jones
job: Janitor
age: 35
executive: false
<dependencies>
<!-- Spring OXM 核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>3.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.0.0.RELEASE</version>
</dependency>
<!-- Castor -->
<dependency>
<groupId>org.codehaus.castor</groupId>
<artifactId>castor-core</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>org.codehaus.castor</groupId>
<artifactId>castor-xml</artifactId>
<version>1.3.3</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
XmlMappingException 及子類 (MarshallingFailureException、UnmarshallingFailureException 等),皆為運行時異常,無需強制捕獲,異常鏈保留根因。applicationContext.xml 的 castorMarshaller bean 為 Jaxb2Marshaller / JibxMarshaller 等,業務代碼零修改。bind-xml node="attribute"、集合映射、嵌套對象class="org.springframework.oxm.jaxb.Jaxb2Marshaller" + contextPath 或 classesToBeBoundStaxEventItemReader/Writer (Spring Batch) 結合 OXMValidationEventHandler + XSD Schema 驗證Spring OXM 以極薄的抽象層,讓 Java 開發者在保持代碼整潔的同時,擁有自由切換 XML 映射引擎的能力。從今天起,拋棄硬編碼的 XML 處理,擁抱聲明式 OXM 吧!