本文示例源代碼或素材下載
apache XMLBeans 是一種開源的、與 XML 和 Java™ 綁定的工具,可用來從 XML 模式生成 Java 類和接口。使用生成的 beans,就可以解析或生成遵循模式的 XML 文檔。因此,這種綁定會緊密地將生成的 Java 類和 XML 模式耦合在一起。在對 XML 模式執行或大或小的修改時,將重新生成 bean 並使用與修改後的 XML 模式對應的新 bean。至少,它正設法實現這一點。不幸的是,應用程序有時需要支持多個模式版本。例如,如果將 XML 用作數據交換標准,應用程序必須提供向前和向後兼容性,以支持較新或較舊的版本標准。
環境設置
考慮一個雇員數據管理應用程序,其中應用程序使用 XML 文件的形式保存雇員信息,並使用 apache XMLBeans 進行處理。雇員數據一直由 XSD 定義,如清單 1 所示。模式使用 XMLBeans 模式編譯器編譯。應用程序然後使用生成的 Java 類和接口處理傳入的 XML 文檔,這個文檔遵循 XSD,名稱空間為 com.ibm.sample.employee:1。
清單 1. 雇員 XML 模式的第一個版本
<?XML version="1.0" encoding="UTF-8"?>
<schema targetNamespace="com.ibm.sample.employee:1" elementFormDefault="qualifIEd"
xmlns="http://www.w3.org/2001/XMLSchema" XMLns:tns="com.ibm.sample.employee:1">
<element name="employee" type="tns:employeeType"></element>
<complexType name="employeeType">
<sequence>
<element name="firstName" type="string"></element>
<element name="lastName" type="string"></element>
<element name="department" type="string"></element>
<element name="phone" type="string"></element>
<element name="eEmail" type="string"></element>
</sequence>
</complexType>
</schema>
清單 2 展示了處理雇員 XML 的應用程序,清單 3 展示了使用它的客戶機應用程序:
清單 2. XMLBeans 應用程序
public class EmployeeApplication {
public void parseXML(String XMLFileName){
EmployeeDocument employeeDoc =
EmployeeDocument.Factory.parse(new File(XML));
EmployeeType employee = employeeDoc.getEmployee();
System.out.println(employee.getFirstName());
// Do something more useful with the employee data
}
}
清單 3. 客戶機
EmployeeApplication app = new EmployeeApplication();
app.parseXML(XMLFilePath);
模式變更簡介
現在假設模式被更改,為舊模式生成的 Java bean 再也不能使用新模式處理 XML。清單 4 展示了修改後的模式,其中添加了一些新元素,並將名稱空間改為 com.ibm.sample.employee:2,表示這是第二個版本。
清單 4. 雇員 XML 模式的第二個版本
<?XML version="1.0" encoding="UTF-8"?>
<schema targetNamespace="com.ibm.sample.employee:2" elementFormDefault="qualifIEd"
xmlns="http://www.w3.org/2001/XMLSchema" XMLns:tns="com.ibm.sample.employee:2">
<element name="employee" type="tns:employeeType"></element>
<complexType name="employeeType">
<sequence>
<element name="firstName" type="string"></element>
<element name="lastName" type="string"></element>
<element name="dept" type="string"></element>
<element name="eMail" type="string"></element>
<element name="phone" type="string"></element>
<element name="address" type="tns:addressType"></element>
</sequence>
</complexType>
<complexType name="addressType">
<sequence>
<element name="streetName" type="string"></element>
<element name="houseNumber" type="string"></element>
<element name="city" type="string"></element>
<element name="state" type="string"></element>
<element name="zipCode" type="string"></element>
</sequence>
</complexType>
</schema>
要確保向前兼容性,您不希望修改客戶機應用程序。但是,輸入 XML 的數據源已升級到新的模式,因此應用程序必須能夠處理傳入的具有任何名稱空間的雇員 XML 數據。但是 XMLBeans 無法管理沖突的模式,您應該如何處理這個問題?
克服處理限制
要想應用程序能夠處理新的 XML 模式,您將需要生成一組與新模式對應的新 Java bean。因為不希望修改應用程序,因此需要確保 apache XMLBeans 生成的新 Java bean 具有和舊 bean 相同的包名。這樣,應用程序不需要修改代碼就可以使用這些 bean。將 com.ibm.sample.employee:1 和 com.ibm.sample.employee:2 名稱空間映射到一個單個 Java 包,比如 com.ibm.sample.employee(映射不屬於本文討論范圍;更多信息請參閱 參考資料)。
將不同 Java bean 集合對應到不同模式後,您只需根據傳入的 XML 加載合適的 bean。可以使用動態類加載技術確保加載正確的 bean 集合。以下小節展示如何使用代理模式和 Java 類加載來加載與每個模式對應的多個 XMLBeans 版本。
Java 類加載
在 Java 編程中,類加載器是一級對象,並且具有自己的名稱空間。類加載器實例加載的類由加載器的名稱空間和類名惟一標識。例如,假設您有一個名為 MyClassLoader 的類加載器。如果這個類加載器的一個實例 myLoaderA 加載了類 Foo,並且另一個實例 myLoaderB 加載了相同的類 Foo,那麼 JVM 將類 Foo 視為兩個不同的類並執行兩次加載(參見圖 1)。
圖 1. Java 類加載
Java 類加載使用一個委托模型來加載類。每個類加載器有一個父類加載器,在嘗試加載類本身時,先將類搜索和類加載委托給它的父加載器,即父加載器優先(parent-first)委托。當類加載器存在一個層次結構時,根類加載器(即引導類加載器)將首先嘗試加載類。如果不行的話,再由系統類加載器嘗試加載,等等(更多有關 Java 類加載的信息,請參閱 參考資料)。
要處理兩種不同的模式版本,可以創建不同的類加載器實例,並且讓每個實例加載與 XML 模式對應的應用程序和 Java bean 版本。
代理
接著,將創建一個代理,使用公共接口作為原始的應用程序或 bean。代理將為每個 XML 模式版本創建一個類實例。類加載器的父類加載器將是當前線程上下文的類加載器。根據正在處理的 XML 模式,這個代理使用合適的類加載器實例加載與每個 XSD 對應的 Java bean 和應用程序本身。多次加載應用程序是因為當 JVM 嘗試加載應用程序使用的所有類時,它將在相同的名稱空間中查找。如果沒有找到的話,它將拋出 ClassNotFound 異常。圖 2 展示了代理的工作方式:
圖 2. 代理動態加載 XMLBeans
代理可以使用接收的 XML 的名稱空間確定要加載哪個 XMLBeans,或者 XML 可以使用一個元素表示 XSD 的版本。代理然後可以使用一個簡單 SAX 解析器檢索信息。
處理多個模式版本的樣例應用程序
重新看一下雇員數據管理應用程序。清單 5 展示了代理代碼:
清單 5. 代理
/* These are class variables as only one set of instances of the
* class loaders are required.
* Using instance variables will only proliferate the class loaders and
* the classes loaded by them.
*/
private static URLClassLoader ccl1 = null, ccl2 = null;
private URLClassLoader createURLClassLoader(URL url){
ClassLoader parent = Thread.currentThread().getContextClassLoader();
URLClassLoader ucl = new URLClassLoader(new URL[]{applicationJarURL,url},
parent);
return ucl;
}
private URLClassLoader loadXMLBeans(int choice) {
try{
switch (choice){
case 1:
if ( ccl1 == null){
ccl1 = createURLClassLoader(
new URL("file",null,jarPath+"employee1.jar"));
}
return ccl1;
// Load the latest by default
default :
case 2:
if ( ccl2 == null){
ccl2 = createURLClassLoader(
new URL("file",null,jarPath+"employee2.jar"));
}
return ccl2;
}
public void parseXML(String XMLFileName){
try {
// Determine which version of XML schema is being used.
URLClassLoader ccl = loadXMLBeans(choice);
Class c = ccl.loadClass(
"com.ibm.sample.application.employee.EmployeeApplication");
Class s = String.class;
Object obj = c.newInstance();
Method m = c.getDeclaredMethod("parseXML", new Class[]{s});
m.invoke(obj,new Object[]{XMLFileName});
}
在這個清單中,名稱空間為 com.ibm.sample.employee:1 的 XSD 的 Java beans 被歸檔到名為 employee1.jar 的 JAR 文件中;而名稱空間為 com.ibm.sample.employee:2 的 XSD 的 Java bean 被歸檔到名為 employee2.jar 的 JAR 文件中。在運行時,JAR 文件或是應用程序本身的類文件都不會包含在類路徑中,因為它們將由 URLClassLoader 的實例從代理中加載。如果這些類在類路徑中可用,URLClassLoader 實例的父加載器(即系統類加載器)將加載它們。同樣,不能加載這些 JAR 文件的多個版本,因為類都位於系統類加載器名稱空間中。
代理決定輸入 XML 使用哪個 XML 模式版本;然後創建一個 URLClassLoader 實例,並將相應 JAR 文件的 URL 添加到 URLClassLoader 的類路徑中。代理包含了類加載器的相應實例之後,它將使用 Java 反射來調用原始應用程序(或 bean)的相應方法。這裡,我們使用反射的原因是因為我們已經使用非系統類加載器加載了應用程序。
現在雇員數據管理應用程序可以處理遵守任何 XML 模式的雇員數據。清單 6 展示了將使用代理調用應用程序的代碼:
清單 6. 修改後的客戶機應用程序
// Create an instance of the proxy instead of the employee application
EmployeeApplicationProxy app = new EmployeeApplicationProxy();
app.parseXML(XMLFilePath);
在這個示例中,應用程序保持不變。但是在某些情況下,可能需要修改應用程序,因為需要使用某些新的處理流程來處理遵循新模式(比如地址)的 XML 數據。因此,需要管理兩個應用程序代碼集。在這種情況下,您可以加載不同的應用程序版本(或一部分)和特定 XSD 版本的 Java bean。這種方法的優點是您可以最小化應用程序的更改,並在頻繁修改模式的情況下輕松地管理應用程序。同樣,可以輕松地添加或刪除模式版本支持。
結束語
由此可見,即使不能支持多個模式,您也可以使用 XMLBeans 處理 XML,並且仍然能夠管理模式變體。希望您在自己的工作中嘗試這種技術。