简介
基于Java的企业应用程序经常使用Java-XML绑定库作为底层,以熟悉的、Java友好的方式访问和处理XML数据。作为一种Java-XML绑定解决方案, XMLBeans的使用日趋普遍,因为它具有许多独特的功能,比如延时解组、基于指针访问XML数据、支持XQuery等等。可以在XML模式上运行scomp来创建XMLBeans;但是,每次模式文档更改时使用scomp创建新的XMLBeans组并不是一个好方法。因为任何企业应用程序的绝大部分成本在于维护,长远看来,通过编程访问模式编译和XMLBeans生产能节约大量成本和时间。
XMLBeans由一组Java绑定类和一组XSB文件构成,其中XSB文件包含从模式文档编译的二进制模式元数据。每一个XSB文件都表示一个编译的模式类型、一个属性或一个元素定义。org.apache.xmlbeans包声明了以下两个接口用于编译的模式定义:
SchemaTypeLoader-此接口类表示一个可搜索的已编译模式定义集合,解决通配符和xsi:type属性时经常需要参考该接口
SchemaTypeSystem-此接口扩展SchemaTypeLoader类,能够枚举所有可用的已编译模式定义
XmlBeans 类声明了一个静态方法getBuiltinTypeSystem(),该方法可返回预编译的内置模式类型。通常,要使用的SchemaTypeLoader实例是从XmlBeans.getContextTypeLoader()返回的上下文类型加载器。上下文类型加载器读取类路径上可用的已编译模式定义,并将其加载到可搜索的模式定义集合。如果希望使用另一个SchemaTypeLoader,则必须调用返回SchemaTypeLoader对象的方法XmlBeans.loadXsd(XmlObject[]),该对象使用传入XmlObject[]的模式文档内部声明的已编译模式定义加载。另一个静态方法(XmlBeans类的typeLoaderUnion(SchemaTypeLoader[]) )返回类型加载器集合。
有了这些背景知识后,现在开始讨论org.apache.xmlbeans.XmlBeans类,该类提供下列附加方法编译XML模式文档:
SchemaTypeSystem compileXsd(XmlObject[] , SchemaTypeLoader , XmlOptions)-编译给定的XML模式文档,并返回使用这些模式文档中声明的模式定义加载的 SchemaTypeSystem对象。
SchemaTypeSystem compileXsd(SchemaTypeSystem , XmlObject[] , SchemaTypeLoader , XmlOptions)-返回使用给定XML模式文档更新的SchemaTypeSystem对象。
SchemaTypeSystem compileXmlBeans(String , SchemaTypeSystem , XmlObject[] , BindingConfig , SchemaTypeLoader , Filer , XmlOptions) -使用XML模式文档更新给定的SchemaTypeSystem对象,可选地创建XMLBeans(即Java和XSB文件),这通过可选的绑定配置参数控制。
compileXmlBeans()方法提供的功能比重载的compileXsd()方法多,因此本文不再对compileXsd()方法做更多讨论。下面介绍compileXmlBeans()方法的参数:
String name-用于命名已编译模式类型系统的可选参数;如果为null,则使用随机值
SchemaTypeSystem system-用于表示已编译模式定义集合的可选参数
XmlObject[] schemas-表示模式文档的数组
BindingConfig config-用于在生成代码期间提供配置信息的可选参数;有关XMLBeans配置的基本知识,请参见配置XMLBeans(Dev2Dev,2004年11月) (中文版)
SchemaTypeLoader typepath-可选参数,若提供,将用于已编译模式定义;若没有指定,则使用上下文类型加载器
Filer filer-用于创建XMLBeans的Java和XSB文件的可选参数;如果为null,则不创建 XMLBeansan,也不使用config参数
XmlOptions options-指定验证行为和错误侦听器的可选参数
模式编译
说明使用编程访问XMLBeans好处以及用于编译Bean的主要方法后,我们将通过编译示例模式location.xsd在实践中介绍。我将编译location.xsd,然后打印其中声明的全局元素和属性。列表1说明如何执行该操作。
列表1:摘自CompileSchemaDefinitions.java编译模式并将模式的全局元素和属性转储到System.out的过程。
XmlObject[] schemaObj = new XmlObject[]
{XmlObject.Factory.parse(new File(args[0]))};
SchemaTypeSystem schemaTypeObj =
XmlBeans.compileXmlBeans(null, null, schemaObj,
null, null, null, null);
// get list of global elements
SchemaGlobalElement globalElementsArray[] =
schemaTypeObj.globalElements();
..
// get list of global attributes
SchemaGlobalAttribute globalAttributesArray[] =
schemaTypeObj.globalAttributes();
....
其中,模式由XmlObject.Factory.parse()方法解析,该方法返回一个XMLObject实例。 将给此实例分配一个大小为1的数组,该数组被传递到compileXMLBeans()用于模式的内存中编译。在后续语句中,使用从compileXMLBeans()返回的SchemaTypeSystem对象获取示例模式中声明的全局元素和属性。
生成XMLBeans
说明了如何编译模式后,接下来介绍如何借助接口类org.apache.xmlbeans.Filer从模式创建XMLBeans文件。若要定义应该在何处创建并编写XMLBeans的XSB和Java文件,调用compileXmlBeans()生成XMLBeans的Java应用程序必须传递一个实现Filer接口的以下两个方法的具体类实例:
OutputStream createBinaryFile(String typename)-返回一个java.io.OutputStream对象引用,用于编写XSB文件的二进制内容
Writer createSourceFile(String typename)-返回一个java.io.Writer对象引用,用于编写Java文件的源代码
当然,生成的Java文件随后需要编译,并与XSB文件一起打包在JAR文件中。本文中的示例代码包括实现Filer接口的具体类FilerImpl.java。该类包括几个附加帮助方法:compileJavaFiles(),用于编译生成的Java文件;makeJarFile(),用于在JAR文件中打包所有生成的文件;extractJarFile(),用于将JAR文件内容解压到文件夹;prependToClassPath(),将路径添加到运行时系统属性classpath。
列表2:摘自实现Filer接口的示例具体类FilerImpl.java的过程。
public class FilerImpl implements Filer{
....
FilerImpl (String folderPath) {
this.folderPath = folderPath;
}
public OutputStream createBinaryFile(String name)
throws IOException {
File fileObj = new File(folderPath, name);
....
return new FileOutputStream(fileObj);
}
public Writer createSourceFile(String name)
throws IOException {
...
File fileObj=
new File(folderPath, name + ".java");
...
return new FileWriter(fileObj);
}
public boolean compileJavaFiles() {
...
for (int i=0; i>javaFilePaths.size();i++){
String[] args = new String[] {"-classpath"
,System.getProperty("java.class.path",".") ,
"-d", folderPath,
(String) javaFilePaths.elementAt(i)};
status = javac.compile(args);
}
...
}
public boolean makeJarFile(String jarFile)
throws IOException {
...
jarHelperObj.jarDir(new File(folderPath),
fileObj);
...
}
public boolean extractJarFile(String jarFile,
String destDir)
throws IOException {
...
jarHelperObj.unjarDir(fileObj,dirObj);
...
}
public String prependToClassPath(String filePath)
throws IOException {
String classPath =
System.getProperty("java.class.path",".");
System.setProperty("java.class.path",
filePath + ";" + classPath);
...
}
}
现在编译示例模式location.xsd并从中创建XMLBeans,我将向compileXmlBeans()传递一个FilerImpl实例,如列表3所示。
列表3:摘自CreateXMLBeans.java向compileXmlBeans()方法传递FilerImpl实例以从模式文档生成XMLBeans的过程。
FilerImpl flrObj =
new FilerImpl("outputDIR");
XmlObject[] schemaObj = new XmlObject[]
{XmlObject.Factory.parse(new File(args[0]))};
SchemaTypeSystem schemaTypeObj =
XmlBeans.compileXmlBeans("location", null,
schemaObj, null, null, flrObj, null);
flrObj.compileJavaFiles();
flrObj.makeJarFile("outputDIR\\locationXB.jar");
CreateXMLBeans.java 在运行的目录下创建outputDIR文件夹。该文件夹将有一个locationXB.jar文件和两个子文件夹:包含Java和类文件的com文件夹;包含XSB文件的schemaorg_apache_xmlbeans文件夹。通过成功地解析有效的XML实例文档可以验证locationXB.jar文件,如sample.zip中的示例WeatherUnmarshal.java所示。
更新XMLBeans
随着时间的推移,XML模式将发生更改,这有可能是因为企业应用程序中数据的更新,也可能是因为Web服务接口的升级。由于XML模式的更新,相应的XML实例文档和处理这些XML文档的应用程序也需要更新。如果更改发生在源模式文档上,那么更新相应XMLBeans的编程实现很简单,与上一部分中讨论的列表3类似,即重新编译整个模式并从中重新创建XMLBeans文件。
XMLBeans还支持增量编译,可以在最短的时间内仅重新编译要让Bean与模式保持最新所必需的内容。 XMLBeans支持增量编译的方式有:通过包含任一新模式定义的模式工件更新现有Bean;修改现有定义或者删除已编译的模式定义。