使用 XML Schema 的 Java 配置
一个样本 XML Schema 和 Java 类,将向您演示……
当启动一个新项目时,开发者通常深思的一个问题是他们需要采用用来编写具有良好参数的代码的策略。在运行时,可以通过设置参数来影响具有良好参数代码的行为。例如,指定几个参数值,图形格式转换器就可以实现许多不同的转换。在运行时,这个应用程序需要两个保存原始图像和已转换图像的文件名。这个应用程序可以从附加的输入文件中读取这些转换参数。
将参数与其它输入数据源区分开来有一些明显的好处:同一个参数集可以应用于许多次不同的运行中,或者工作参数的规范可能需要应用程序内部结构和算法的深入知识。举一个极端的例子,可能没有对参数编制文档,而代码与缺省的参数集一起分发。通过这种方法,作者可以通过分发不同参数集来启用应用程序的不同行为。
在复杂应用程序的代码中,应该有可能清晰地确定出与给定参数规范相关的作用域或环境。对于 Java 应用程序,一种自然的解决方案是将参数与其所使用的 程序作用域相联系起来。然后,名称应该反映参数在层次结构中的位置,该层次结构包括:
- 应用程序(Application)
- 包(Package)
- 类(Class)
将假定无环境限定符作为名称前缀的参数有全局作用域,并且指的是层次结构中的较高层(应用程序)。
可以通过普通的 Java 命名规则构造那些应用于代码中特定环境(Package 或 Class)的参数。例如,
solver.rk3.p1
和
solver.rk3.timestep.c1
可以是用于标识同一个包中与
solver.rk3
包相关的
p1
参数和与类
timestep
相关的
c1
参数的名称。
这种命名策略可以应用于任何类型的嵌套逻辑环境,而不只是程序单元环境。这对于维护一个避免名称冲突的有序名称空间,是很有帮助的。与代码环境不相关的逻辑环境被称作为 主题。主题可以包含任意数量的嵌套主题。在名称层次结构的较高层(应用程序),名称是没有限定符的。所以,在应用程序层次,程序作用域参数和主题参数是没有区别的。以主题为作用域的参数有一个带有主题名称(按包含内容的降序从左到右排列)的限定名,并用“|”字符分隔。例如:
Literature|Classics|Odyssey|author
可以是用于标识
author
参数的名称,该参数是在图书馆名称环境,按最常用到最不常用的顺序,其主题是
Literature
、
Classics
和
Odyssey
。
这些参数都有相关的 类型。下面八种基本类型对应于 Java 内置类型,同样,这些类型在 XML Schema 标准中被定义成简单类型:
Boolean
Byte
Short
Integer
Long
Float
Double
String
属于这八种基本类型的参数对于其规范只需要两个字符串:一个用于名称,另一个用于值的表示。需要更复杂的构造来定义一般的 Java 对象。
用于配置文件的 XML Schema
XML 是用于表示参数空间层次性结构的自然选择。XML 模式文件是用于正式定义程序环境(
Package
和
Class
)和 topic 环境之间包含内容的关系的文件。该模式还定义了复杂类型,这种复杂类型对于定义任意 Java 类的实例是必需的。
在所提议模式的根上,是
Application
元素,而映射到这八种类型(
Boolean
、
String
和 Number 类型)的元素是在底层的。由
Parameter
复杂类型在程序作用域(
Package
或
Class
)或在
Topic
中定义了基本类型参数。如图 1 所示。
Parameter 类型
由
Parameter
规范包含的基本类型元素定义了当前环境中相应类型的新对象。
Package
、
Class
和
Topic
是可以包含
Parameter
规范的有效环境。可以从图 1 中看出,
Parameter
可以包含单个元素,该元素的类型可以是这八种基本类型中的任何一种或
Reference
类型。后者(Reference 类型)提供了通过名称来引用以前定义的对象的能力。该规范的语义是应当创建已引用对象的一个副本,并指定另一个名称。当同一个复杂对象(值)在应用程序的不同部分以不同名称使用时,这种方法是很有用的。请注意,引用的对象可以是任何 Java 类型,而不仅仅是这八种基本类型中某一种。
图 1. Parameter 类型

Object 类型
Object
是由模式定义的复杂 XML 类型。它创建了一个外部指定的 Java 类新实例。该类型实际定义了指定用于类和值的匹配序列的构造器的签名。在解析时,利用 Java 内省特性来确定与指定签名及其调用匹配的类构造器。如图 2 所示,将这个签名指定为一个序列。序列中每个元素的类型可以是这八种基本类型中的某一种,或者是
Reference
类型。
图 2. Object 类型

ClassMethod 和 InstanceMethod 类型
类构造器的调用不是创建 Java 类的实例的唯一方法。一般来讲,可以通过类方法或实例方法的调用来获得对象引用。对于
Object
类型,
ClassMethod
或
InstanceMethod
规范定义了指定类和匹配序列值的有效方法的签名。然而,并非所有方法都返回对象引用。在配置文件中定义方法调用的能力是很重要的,不应该将这种能力限制于返回对象引用的方法。例如,配置文件可以指定辅助
Log
对象的创建,代码中所有日志记录的方法将按名称引用。
Log
对象可以要求其自己的、由
init()
实例方法执行的初始化。在 XML 配置文件处理期间,可以执行完整的初始化,其中
Log
对象的
Object
类型定义将后跟
InstanceMethod
定义,InstanceMethod 定义会导致在以前定义的
Log
对象上执行
init()
方法。
总之,
ClassMethod
或
InstanceMethod
规范的语义暗示了方法应该在配置文件进行解析期间被调用。如果由该方法返回对象,则会在当前的命名环境(
Package
、
Class
或
Topic
)中引入新的名称-对象关系。
Property 类型
人们设计一些 Java 类来从由
System.getProperties()
返回的
Properties
对象中检索运行时信息。在配置文件中的
Property
元素规范导致在由
System
类管理的 Properties 表中设置新的名称-值属性(name-value property)。在那些使用它们的 Java 类的程序环境(Package)内定位
Property
元素定义是很便利的。然而,这个定义不会在程序和主题名称空间里创建新的命名的对象。改变配置文件中的
Property
元素的位置只影响处理该元素对于其它元素的相对次序。
Application 元素
Application
是模式的根元素。在
Application
元素内直接定义的
Parameter
或
Object
元素有全局作用域;其指定的名称不继承来自容器元素的任何前缀限定。
Application
元素,如图 3 所示,还可以包含 Property 规范和嵌套的容器(Topic 和 Package)。
图 3. Application 元素

图 3 中图形符号表示
Application
元素包含不受限制的元素序列,以及每个被包含元素是
Parameter
、
Object
和
Property
等等的类型。
Topic
和
Package
元素定义各自的名称空间层次结构。使用不同的分隔符(“|”表示主题名称,“.”表示 Package/Class 名称)来区分这些名称空间。定义了所允许的
Topics
、
Package
和
Class
元素嵌套的规则,如图 4 到图 6 中所示。在
附录 A中重现了完整的模式,而在
附录 B中重现了样本配置文件。
Topic 类型
如下图所示,
Topic
类型的一个元素包含一个不受限制的元素序列。通过添加“|”分隔符和将
Topic
的名称属性添加到父环境的全限定名来构造
Topic
的全限定名。(
Application
是根,它有一个空的限定名。)每个包含的元素可以是:
- 嵌套的
Topic
。 Parameter
、Object
、ClassMethod
或InstanceMethod
类型的元素,其定义引入了相对于当前Topic
的名称-对象关联。通过添加“|”分隔符和将对象的名称属性添加到当前Topic
的全限定名来构造新对象的全限定名。Property
类型元素引入了 System Properties 散列表内的名称-对象关联。属性的名称没有将当前Topic
的全限定名作为前缀。
图 4. Topic 类型

Package 类型
如下图所示,
Package
类型的结构类似于
Topic
类型的结构。唯一区别在于
Package
除
Package
元素外可以包含嵌套的
Class
元素。通过将“.”分隔符和
Package
的名称属性添加到父环境的全限定名来构造
Package
的全限定名。每个包含的元素可以是:
- 嵌套的
Package
或Class
。 Parameter
、Object
、ClassMethod
或InstanceMethod
类型的元素,其定义引入了相对于当前Package
的名称-对象关联。将“.”分隔符和对象的名称属性添加到当前Package
的全限定名来构造新对象的全限定名。- 在 System Properties 散列表内引入名称-对象关联的
Property
类型元素。属性的名称没有将当前Package
的全限定名作为前缀。
图 5. Package 类型

Class 类型
由于
Class
不能包含嵌套的容器元素,所以
Class
类型终结了程序作用域的层次结构。否则,
Class
可以包含在
Package
中找到的相同元素并且对于限定名的构造应用同样的规则。
图 6. Class 类型

XMLConfigurator:配置实用类
XMLConfigurator
类被设计用来为处理符合
附录 A 中重现的 XML 模式的配置文件提供便利的接口。该类实现了一个静态初始化方法,这个方法对一个或多个配置文件进行解析,并将名称-对象关联构建到
TreeMap
对象中。这个初始化过程还可以引入 System Properties 散列表中的名称-对象关联。提供了其它静态方法以允许用户类方便地从
TreeMap
检索对象。
可以从
参考资料所提供的链接中下载
XMLConfigurator
Java 样本代码、模式和样本配置文件。
XMLConfigurator 类扩展了
org.xml.sax.helpers.DefaultHandler
,并且它实现了一些处理 SAX 解析事件的方法的定制版本。最重要的方法有
startDocument()
、
startElement()
和
endElement()
。对于其它事件处理方法,由保留
DefaultHandler
提供缺省实现。
还提供静态方法以通过参数对象的名称来检索参数对象:
public static Object getParameter (String key){} public static Object getClassParameter(Class obj, String pname){} public static Object getClassParameter(Object obj, String pname){}
第一个方法提供了最大程度的通用性,这需要输入字符串包含要返回的参数对象的全限定名。下面这段代码显示了如何检索与主题
TopicA
的子主题
subTopicB
有关的字符串参数
pp
:
String p = (String)getParameter("TopicA|subTopicB|pp");
通过下面这段代码获得与
myapp.util.UtilClass
类有关的整数参数
xx
:
int x = ((Integer)getParameter("myapp.util.UtilClass.xx")).intValue();
提供的第二种和第三种形式方便了对与特定类相关联的程序单元作用域参数的检索。在这两种形式中,第二个输入参数(文本字符串)包含不带限定的参数名。从输入
Class
对象(第二个签名)或从该类的实例对象(第三个签名)在内部计算前缀,这个前缀是获得全限定参数名所需的。以下是第三种形式的样本用法,即,通过类的实例方法检索类型
Double
的
yy
参数:
double y = ((Double)getClassParameter(this, "yy")).doubleValue();
实现了其它一些便利方法,通过其全限定名来检索
numeric
和
boolean
类型的对象实例。
public static boolean booleanValue(String key) public static byte byteValue(String key) {} public static double doubleValue(String key) {} public static float floatValue(String key) {} public static int intValue(String key) {} public static long longValue(String key) {} public static short shortValue(String key) {}
这样做的主要好处是,在方法内部完成捕获异常和执行对原始类型的转换。
这个示例显示了对封装在
Double
类型参数中双精度型原始值的检索,其全限定名是
mycompany.myproduct.myguess
:
用样本初始化文件运行代码
XMLConfigurator
类利用日志记录工具 Log4j,可以从 jakarta.apache.org(请参阅
参考资料)下载该工具。Log4j 本身可以利用 XML 文件来进行其本身的配置,所以,在
XMLConfigurator
类静态初始化的最初阶段让日志记录工作,并且确保没有“鸡-蛋”问题是很有趣的。日志记录是一个需要在初始化阶段激活的关键功能。所以,让日志记录本身启动并由
XMLConfigurator
来初始化,这似乎是很自然的选择。该样本代码是用 Log4j 版本 1.1.3 开发的。
有一行代码使这个类只与 Apache XML 解析器一起工作。当然,这可以改变。然而,这个样本代码是用 Xerces 版本 1.4.3 开发的,可以从 xml.apache.org(请参阅 参考资料)下载 Xerces 版本 1.4.3 。
一旦同时安装了 Log4j 和 Xerces,并且将下载的 zip 文件(请参阅
参考资料)解开之后,就可以查看
Resources\Export
路径下的
run.bat
这个样本批处理文件。应该更改该文件以与 Log4j 和 Xerces 的安装路径一致。
样本代码也存在 VisualAge for Java 版本 4.0 资源库的形式。在 VisualAge 中使用该样本代码也需要在那个环境中有 Log4j 和 Xerces。可以通过为这两个包中的每个创建一个项目以及将 jar 文件导入各自的项目中来做到这一点。(告诫:不能同时在工作区中装入 IBM XML Parser for Java 和 Xerces 1.4.3)。
附录 B 中的样本 XML 配置文件指定一个
Locale
对象的创建,会在
ResourceBundle
对象的后续实例化中引用该对象:确实已经十分接近于面向对象脚本语言的强大功能!
下载资源
- Sample code, XML Schema, and configuration file (x-jschema-code.zip | 67KB)
相关主题
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文.
- 请参加关于本文的 论坛。
- 请下载
XMLConfigurator
的 Java 样本代码、XML Schema 和样本配置文件。 - 从 jakarta.apache.org 上的 Log4j 项目页面获取 Log4j。
- 从 xml.apache.org 上的 Xerces Java Parser 1.4.4 页面获取 Xerces。
- 请阅读 使用 XML Schema 来定义元素的基础知识中有关使用 XML Schema 的概述。
- WebSphere Studio Application Developer 支持使用一些用于开发的技术,其中包括 XML 和 Java。 XML 和 WebSphere Studio Application Developer,第一部分:开发 XML Schema讲述了 XML Schema Editor,该工具是用于构建符合“XML Schema 建议书规范(XML Schema Recommendation Specification)”的 XML Schema。
- IBM 已经将 Java 开发者工具箱移植到其最受欢迎的平台上。对于最新的 JDK,可以到由 IBM Centre for Java Technology Development 主管的 JDK 页面。