<!-- 提取技术文章 -->
XMLBeans 1.0.3最强大的功能之一是可通过提供配置文件定制代码生成。在灵活性、可重用性、简化代码和维护方面,
XMLBeans的功能带来了许多益处。因为任何企业应用程序相当大部分的成本都在于维护方面,所以从长远来看,XMLBeans的配置功能能够代表对成本和时间的节省。
本文通过一系列的示例对这些功能进行了说明。我们假设您已熟悉XMLBeans。对于入门者,参见下面列出的介绍XMLBeans的文章。本文中提及的示例代码和其他文件可以下载。示例已在Apache XMLBeans 1.0.3、Java 1.4.2_02和Microsoft Windows 2000上通过测试。
运行XMLBeans编译器时,可以指定可选配置文件来修正XMLBeans生成器的行为。默认情况下,该配置文件应当具有.xsdconfig扩展名。配置文件的结构遵循以下模式:
<?xml version="1.0" encoding="UTF-8"?>
<xb:config
xmlns:xb="http://xml.apache.org/xmlbeans/2004/02/xbean/config">
...
</xb:config>
正如您所看到的,根config元素必须位于http://xml.apache.org/xmlbeans/2004/02/xbean/config名称空间中。真正的配置功能必须位于config元素之内,可按任何次序排放。
有了模式文件(比如someSchema.xsd)和配置文件(比如someConfig.xsdconfig)之后,下一步即是生成一系列代表该模式的XMLBeans类。以下为命令行用法示例:
scomp -out output_XmlBeans.jar someSchema.xsd someConfig.xsdconfig
注意配置文件只是简单添加到scomp的附加参数。这将确保生成器使用您所指定的任何扩展功能。以下部分对这些功能依次进行了检查。
向已生成的类名添加前缀或后缀
XMLBeans提供了简单配置功能,用于从特定的名称空间中为模式类型创建的XMLBeans类名添加前缀名及后缀名。该功能对于将从特定名称空间中生成的模式类型的XMLBeans类同其他XMLBeans和在工程中使用的JavaBeans区别开来很有用。
这只是最简单的功能。我们在介绍模式示例时将还会使用这一部分。清单1显示了贯穿本文使用的localInfo.xsd模式。它描述了包含一个地点(基于邮编)天气信息的XML文档。
清单1. localInfo.xsd
<
xsd:schema
targetNamespace
="http://www.localInfo.com/LI"
...
xmlns:ns1
="http://www.helloWeather.com/weather"
>

<
xsd:import
namespace
="http://www.helloWeather.com/weather"
schemaLocation
="weather.xsd"
/>

<
xsd:element
name
="localInfo"
>
<
xsd:complexType
>
<
xsd:sequence
>
<
xsd:element
ref
="ns1:localWeather"
/>
</
xsd:sequence
>
<
xsd:attribute
ref
="Zipcode"
/>
</
xsd:complexType
>
</
xsd:element
>

<
xsd:attribute
name
="Zipcode"
type
="xsd:string"
/>

</
xsd:schema
>
该localInfo.xsd 模式导入 weather.xsd,如清单2所示。
清单 2. weather.xsd
<
xsd:schema
targetNamespace
=
"http://www.helloWeather.com/weather"
....
>

<
xsd:element
name
="localWeather"
>
<
xsd:complexType
>
<
xsd:sequence
>
<
xsd:element
ref
="Temperature"
/>
<
xsd:element
ref
="Humidity"
/>
<
xsd:element
ref
="Visibility"
/>
<
xsd:element
ref
="Datetime"
/>
<
xsd:element
ref
="image"
minOccurs
="1"
maxOccurs
="1"
/>
</
xsd:sequence
>
<
xsd:attribute
ref
="Zipcode"
/>
</
xsd:complexType
>
</
xsd:element
>

<
xsd:element
name
="Temperature"
type
="xsd:float"
/>
<
xsd:element
name
="Humidity"
type
="xsd:float"
/>
<
xsd:element
name
="Visibility"
type
="xsd:float"
/>
<
xsd:element
name
="Datetime"
type
="xsd:dateTime"
/>
<
xsd:element
name
="image"
type
="imageType"
/>
<
xsd:attribute
name
="Zipcode"
type
="xsd:string"
/>

<
xsd:complexType
name
="imageType"
>
<
xsd:simpleContent
>
<
xsd:extension
base
="xsd:base64Binary"
>
<
xsd:attribute
name
="type"
use
="optional"
type
="xsd:string"
/>
</
xsd:extension
>
</
xsd:simpleContent
>
</
xsd:complexType
>
最后,清单3显示了一个简单完整的配置文件(localInfo.xsdconfig),它说明了XMLBeans前缀、后缀功能的用法。
清单3:localInfo.xsdconfig:前缀和后缀的配置
<
xb:config
xmlns:xb
=
"http://xml.apache.org/xmlbeans/2004/02/xbean/config"
>
<
xb:namespace
uri
="##any"
>
<
xb:suffix
>
XmlBean
</
xb:suffix
>
<
xb:prefix
>
pre
</
xb:prefix
>
</
xb:namespace
>
</
xb:config
>
prefix和suffix元素分别指定了从名称空间中为模式类型创建的XMLBeans类名的前缀和后缀,其中名称空间由namespace元素中的uri属性值定义。在以上localInfo.xsdconfig列表中,我们希望不论模式类型代表什么名称空间,都为所有从XML模式创建的XMLBeans类名添加前缀“XmlBean”。为此示例,我们不使用前缀功能。
下一步是创建一个代表localInfo.xsd的一组XMLBeans类。在从示例存档提取文件的工作目录中提示处,输入下列行:
scomp-outlocalinfo_XmlBeans
.
jarlocalInfo
.
xsdlocalInfo
.
xsdconfig
使用配置文件的结果是,生成的XMLBean类将具有适当的后缀。例如,为Zipcode属性生成的XMLBean将被称作ZipcodeAttributeXmlBean.java,而不是原来默认情况下的ZipcodeAttribute。
XML到Java名映射和名称空间URI到包名映射
XMLBeans具有将XML名映射为Java名称和将名称空间URI映射为Java包名称的配置能力。这允许开发人员在XML名称或名称空间URI改变的情况下,避免重写Java代码,从而使其基于XMLBeans的应用程序更具灵活性。
作为一个示例,考虑以下配置文件,它为创建模式时我们所使用的每一个名称空间都分配了一个包名称:
<
xb:config
xmlns:xb
=
"http://xml.apache.org/xmlbeans/2004/02/xbean/config"
xmlns:NS1
="http://www.helloWeather.com/weather"
>

...

<
xb:namespace
uri
="http://www.localInfo.com/LI"
>
<
xb:package
>
com.localInfo
</
xb:package
>
</
xb:namespace
>

<
xb:namespace
uri
="http://www.helloWeather.com/weather"
>
<
xb:package
>
com.weather
</
xb:package
>
</
xb:namespace
>

<
xb:qname
name
="NS1:image"
javaname
="MapXmlBean"
/>
...
</
xb:config
>
记得要为使用的名称空间声明前缀来使元素成为配置文件中的一部分。这里我们已经为config元素中的http://www.helloWeather.com/weather名称空间声明了前缀NS1。
从该示例可以看出,namespace和package元素用于将名称空间URI映射为应当生成的Java包名称。下面的namespace元素将http://www.localInfo.com/LI名称空间映射为包名com.localInfo。
<
xb:namespace
uri
="http://www.localInfo.com/LI"
>
<
xb:package
>
com.localInfo
</
xb:package
>
</
xb:namespace
>
这样使用的结果是,为模式类型生成的、属于http://www.localInfo.com/LI名称空间的XMLBean类的包名称是com.localInfo,而不是原来默认情况下的com.localInfo.li。
qname元素用于将模式类型名映射为生成的相应的XMLBeans Java类名称。Qname元素的Name属性对于模式类型是限定的名称,同时javaname属性值代表了相应的生成的XMLBeans类名。
在weather.xsd中声明的image元素代表了一个位置的映射。在前面的示例中我们使用qname扩展名来将image元素映射为名为MapXmlBean的Java类,使image元素的Java名更加直观。注意:qname比namespace具有更高优先权,所以Java名称指定为MapXmlBean而不只是Map,这样就能与其他XMLBeans类名具有一致性。
接口扩展
接口扩展功能对于用和它代表的模式类型的结构和属性无关的方法来扩展XMLBean很有用。例如,具有天气状况细节的XML文档中的温度数据用华氏温度表示,但是一些使用相应XMLBeans的客户端应用程序需要摄氏温度表示的数据来作进一步处理。使用接口扩展功能,能将一个转换函数float getTemperatureInCelsius()添加到XMLBean中,它将华氏温度数据转换为摄氏温度。
这里是使用extension元素来完成此功能的配置文件的代码摘录:
<
xb:extension
for
=
"com.weather.LocalWeatherDocumentXmlBean$LocalWeather"
>
<
xb:interface
name
="com.extension.weatherExtension"
>
<
xb:staticHandler
>
com.extension.weatherExtensionHandler
</
xb:staticHandler
>
</
xb:interface
>
</
xb:extension
>
for属性能够接收按空间划分的XMLBeans Java接口列表,或者使用“*”来包含扩展中的所有接口。这种做法的结果是,生成的XMLBeans接口LocalWeatherDocumentXmlBean$LocalWeather将扩展指定的接口,在本例中将扩展为com.extension.weatherExtension。该例中,我们为接口声明了两种方法:
public
interface
weatherExtension
...
{
floatgetTemperatureInCelsius();
floatgetVisibilityInKilometers();
}
对于com.extension.weatherExtension接口的实现方法将自动在相应的XMLBeans实现类LocalWeatherDocumentXmlBeanImpl$LocalWeatherImpl中生成。这些实现方法将委托给特定的扩展处理程序的com.extension.weatherExtensionHandler方法。
扩展处理程序类com.extension.weatherExtensionHandler必须包括一个与接口方法有相同名称的public static方法,但是第一个参数必须为XmlObject类型,接下来是接口方法的参数,如下:
public
static
float
getTemperatureInCelsius

(XmlObjectxo)
...
{

.....
}

public
static
float
getVisibilityInKilometers

(XmlObjectxo)
...
{

.....
}
每次LocalWeatherDocumentXmlBean$LocalWeather实例的扩展方法被调用时,都将调用以上两个方法。
因为XMLBeans根据模式生成文档类,所以这些文档类必须在其他任何依靠它们的扩展类编译前进行构建。由于这种循环的依赖性,建构所有部分需要一点技巧。
通过接口扩展功能构建XMLBeans分为三步。
- 将对extension元素的XML注释置于localInfo.xsdconfig文件中。然后,在从示例存档提取文件的工作目录的提示处,输入下列行:
scomp-outlocalinfo_XmlBeans
.
jarlocalInfo
.
xsdlocalInfo
.
xsdconfig
- 使用localinfo_XmlBeans.jar和xbean.jar编译扩展接口和处理程序类。
SET
CLASSPATH
=
%CLASSPATH%
;
localinfo_XmlBeans
.
jar
;.;
c:
\
xmlbeans-
1.0
.
3
\
lib
\
xbean
.
jar
javac-d
.
weatherExtension
.
java
javac-d
.
weatherExtensionHandler
.
java
删除localInfo.xsdconfig文件中围绕extension元素的XML注释。然后通过localInfo.xsd文件和localInfo.xsdconfig文件再次运行scomp,确保已编译的扩展类在classpath上。
-
scomp-outlocalinfo_XmlBeans
.
jarlocalInfo
.
xsdlocalInfo
.
xsdconfig
哇!我们已经准备好了编译并运行包含在示例存档中的基于XMLBeans的客户端应用程序extensionClient.java。
javacextensionClient
.
java
javaextensionClientlocalInfo_68154
.
xml
通过使用接口扩展功能,我们已经将附加逻辑从客户端应用程序移至XMLBean Java代码上。现在的代码已集中化并可重用。同时,XMLBean的客户端应用程序变得更简洁精练。
PrePost扩展
使用prepost扩展功能时,在指定的XMLBeans的所有setter方法开始和结束时将生成对扩展处理程序的pre和post调用。
我们说我们不希望我们的基于XMLBeans的客户端应用程序对由image元素的内容代表的映射进行修改。为了实行该约束,将下列extension元素添加到localInfo.xsdconfig文件中:
<
xb:extension
for
="
com.weather.LocalWeatherDocumentXmlBean$LocalWeather
"
>
<
xb:prePostSet
>
<
xb:staticHandler
>
com.extension.MapPrePostSetHandler
</
xb:staticHandler
>
</
xb:prePostSet
>
</
xb:extension
>
extension元素的for属性指定了按空间划分的XMLBeans的列表,其中setter方法按照下面LocalWeatherDocumentXmlBean$LocalWeather类所展示的那样调用preSet()方法和postSet()方法:
/***/
/**
*Setsthe"image"element
*/
public
void
setMapXmlBean
(com.weather.ImageTypeXmlBeanmapXmlBean)

...
{
synchronized(monitor())

...{
check_orphaned();
if(com.extension.MapPrePostSetHandler.preSet
(1,this,MAPXMLBEAN$8,false,-1)
)

...{

....CodetosetImageTypeXmlBeaninstance
}
com.extension.MapPrePostSetHandler.postSet
(1,this,MAPXMLBEAN$8,false,-1);
}
}


/***/
/**
*Appendsandreturnsanewempty"image"element
*/
public
com.weather.ImageTypeXmlBean
addNewMapXmlBean()

...
{
synchronized(monitor())

...{
check_orphaned();
com.weather.ImageTypeXmlBeantarget=null;
if(com.extension.MapPrePostSetHandler.preSet
(2,this,MAPXMLBEAN$8,false,-1)
)

...{
...
Codetocreateandreturnanemptyinstance
ofImageTypeXmlBean
...
}
com.extension.MapPrePostSetHandler.postSet
(2,this,MAPXMLBEAN$8,false,-1);
returntarget;
}
}
以上代码是使用PrePost扩展将生成什么结果的一个示例。注意在set方法和append方法中都调用了pre和post方法。
以下是一些来自MapPrePostSetHandler.java的代码,说明了怎样实现阻止客户端应用程序修改image元素内容的preSet()和postSet()方法。
public
static
boolean
preSet(
int
operationType,
XmlObjectxo,QNamepropertyName,

boolean
isAttr,
int
indexOfItem)
...
{

javax.xml.namespace.QNameimageQName=
newjavax.xml.namespace.QName
("http://www.helloWeather.com/weather","image");

if((xoinstanceofLocalWeather)
&&(propertyName.equals(imageQName)))
