使用 XML Schema 的 Java 配置

一个样本 XML Schema 和 Java 类,将向您演示……

Comments

当启动一个新项目时,开发者通常深思的一个问题是他们需要采用用来编写具有良好参数的代码的策略。在运行时,可以通过设置参数来影响具有良好参数代码的行为。例如,指定几个参数值,图形格式转换器就可以实现许多不同的转换。在运行时,这个应用程序需要两个保存原始图像和已转换图像的文件名。这个应用程序可以从附加的输入文件中读取这些转换参数。

将参数与其它输入数据源区分开来有一些明显的好处:同一个参数集可以应用于许多次不同的运行中,或者工作参数的规范可能需要应用程序内部结构和算法的深入知识。举一个极端的例子,可能没有对参数编制文档,而代码与缺省的参数集一起分发。通过这种方法,作者可以通过分发不同参数集来启用应用程序的不同行为。

在复杂应用程序的代码中,应该有可能清晰地确定出与给定参数规范相关的作用域或环境。对于 Java 应用程序,一种自然的解决方案是将参数与其所使用的 程序作用域相联系起来。然后,名称应该反映参数在层次结构中的位置,该层次结构包括:

  • 应用程序(Application)
  • 包(Package)
  • 类(Class)

将假定无环境限定符作为名称前缀的参数有全局作用域,并且指的是层次结构中的较高层(应用程序)。

可以通过普通的 Java 命名规则构造那些应用于代码中特定环境(Package 或 Class)的参数。例如,

solver.rk3.p1

solver.rk3.timestep.c1

可以是用于标识同一个包中与 solver.rk3 包相关的 p1 参数和与类 timestep 相关的 c1 参数的名称。

这种命名策略可以应用于任何类型的嵌套逻辑环境,而不只是程序单元环境。这对于维护一个避免名称冲突的有序名称空间,是很有帮助的。与代码环境不相关的逻辑环境被称作为 主题。主题可以包含任意数量的嵌套主题。在名称层次结构的较高层(应用程序),名称是没有限定符的。所以,在应用程序层次,程序作用域参数和主题参数是没有区别的。以主题为作用域的参数有一个带有主题名称(按包含内容的降序从左到右排列)的限定名,并用“|”字符分隔。例如:

Literature|Classics|Odyssey|author

可以是用于标识 author 参数的名称,该参数是在图书馆名称环境,按最常用到最不常用的顺序,其主题是 LiteratureClassicsOdyssey

这些参数都有相关的 类型。下面八种基本类型对应于 Java 内置类型,同样,这些类型在 XML Schema 标准中被定义成简单类型:

  • Boolean
  • Byte
  • Short
  • Integer
  • Long
  • Float
  • Double
  • String

属于这八种基本类型的参数对于其规范只需要两个字符串:一个用于名称,另一个用于值的表示。需要更复杂的构造来定义一般的 Java 对象。

用于配置文件的 XML Schema

XML 是用于表示参数空间层次性结构的自然选择。XML 模式文件是用于正式定义程序环境( PackageClass )和 topic 环境之间包含内容的关系的文件。该模式还定义了复杂类型,这种复杂类型对于定义任意 Java 类的实例是必需的。

在所提议模式的根上,是 Application 元素,而映射到这八种类型( BooleanString 和 Number 类型)的元素是在底层的。由 Parameter 复杂类型在程序作用域( PackageClass )或在 Topic 中定义了基本类型参数。如图 1 所示。

Parameter 类型

Parameter 规范包含的基本类型元素定义了当前环境中相应类型的新对象。 PackageClassTopic 是可以包含 Parameter 规范的有效环境。可以从图 1 中看出, Parameter 可以包含单个元素,该元素的类型可以是这八种基本类型中的任何一种或 Reference 类型。后者(Reference 类型)提供了通过名称来引用以前定义的对象的能力。该规范的语义是应当创建已引用对象的一个副本,并指定另一个名称。当同一个复杂对象(值)在应用程序的不同部分以不同名称使用时,这种方法是很有用的。请注意,引用的对象可以是任何 Java 类型,而不仅仅是这八种基本类型中某一种。

图 1. Parameter 类型
Parameter 类型
Parameter 类型

Object 类型

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

图 2. Object 类型
Object 类型
Object 类型

ClassMethod 和 InstanceMethod 类型

类构造器的调用不是创建 Java 类的实例的唯一方法。一般来讲,可以通过类方法或实例方法的调用来获得对象引用。对于 Object 类型, ClassMethodInstanceMethod 规范定义了指定类和匹配序列值的有效方法的签名。然而,并非所有方法都返回对象引用。在配置文件中定义方法调用的能力是很重要的,不应该将这种能力限制于返回对象引用的方法。例如,配置文件可以指定辅助 Log 对象的创建,代码中所有日志记录的方法将按名称引用。 Log 对象可以要求其自己的、由 init() 实例方法执行的初始化。在 XML 配置文件处理期间,可以执行完整的初始化,其中 Log 对象的 Object 类型定义将后跟 InstanceMethod 定义,InstanceMethod 定义会导致在以前定义的 Log 对象上执行 init() 方法。

总之, ClassMethodInstanceMethod 规范的语义暗示了方法应该在配置文件进行解析期间被调用。如果由该方法返回对象,则会在当前的命名环境( PackageClassTopic )中引入新的名称-对象关系。

Property 类型

人们设计一些 Java 类来从由 System.getProperties() 返回的 Properties 对象中检索运行时信息。在配置文件中的 Property 元素规范导致在由 System 类管理的 Properties 表中设置新的名称-值属性(name-value property)。在那些使用它们的 Java 类的程序环境(Package)内定位 Property 元素定义是很便利的。然而,这个定义不会在程序和主题名称空间里创建新的命名的对象。改变配置文件中的 Property 元素的位置只影响处理该元素对于其它元素的相对次序。

Application 元素

Application 是模式的根元素。在 Application 元素内直接定义的 ParameterObject 元素有全局作用域;其指定的名称不继承来自容器元素的任何前缀限定。 Application 元素,如图 3 所示,还可以包含 Property 规范和嵌套的容器(Topic 和 Package)。

图 3. Application 元素
Application 元素
Application 元素

图 3 中图形符号表示 Application 元素包含不受限制的元素序列,以及每个被包含元素是 ParameterObjectProperty 等等的类型。 TopicPackage 元素定义各自的名称空间层次结构。使用不同的分隔符(“|”表示主题名称,“.”表示 Package/Class 名称)来区分这些名称空间。定义了所允许的 TopicsPackageClass 元素嵌套的规则,如图 4 到图 6 中所示。在 附录 A中重现了完整的模式,而在 附录 B中重现了样本配置文件。

Topic 类型

如下图所示, Topic 类型的一个元素包含一个不受限制的元素序列。通过添加“|”分隔符和将 Topic 的名称属性添加到父环境的全限定名来构造 Topic 的全限定名。( Application 是根,它有一个空的限定名。)每个包含的元素可以是:

  • 嵌套的 Topic
  • ParameterObjectClassMethodInstanceMethod 类型的元素,其定义引入了相对于当前 Topic 的名称-对象关联。通过添加“|”分隔符和将对象的名称属性添加到当前 Topic 的全限定名来构造新对象的全限定名。
  • Property 类型元素引入了 System Properties 散列表内的名称-对象关联。属性的名称没有将当前 Topic 的全限定名作为前缀。
图 4. Topic 类型
Topic 类型
Topic 类型

Package 类型

如下图所示, Package 类型的结构类似于 Topic 类型的结构。唯一区别在于 PackagePackage 元素外可以包含嵌套的 Class 元素。通过将“.”分隔符和 Package 的名称属性添加到父环境的全限定名来构造 Package 的全限定名。每个包含的元素可以是:

  • 嵌套的 PackageClass
  • ParameterObjectClassMethodInstanceMethod 类型的元素,其定义引入了相对于当前 Package 的名称-对象关联。将“.”分隔符和对象的名称属性添加到当前 Package 的全限定名来构造新对象的全限定名。
  • 在 System Properties 散列表内引入名称-对象关联的 Property 类型元素。属性的名称没有将当前 Package 的全限定名作为前缀。
图 5. Package 类型
Package 类型
Package 类型

Class 类型

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

图 6. Class 类型
Class 类型
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 对象(第二个签名)或从该类的实例对象(第三个签名)在内部计算前缀,这个前缀是获得全限定参数名所需的。以下是第三种形式的样本用法,即,通过类的实例方法检索类型 Doubleyy 参数:

double y = ((Double)getClassParameter(this, "yy")).doubleValue();

实现了其它一些便利方法,通过其全限定名来检索 numericboolean 类型的对象实例。

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 对象的后续实例化中引用该对象:确实已经十分接近于面向对象脚本语言的强大功能!


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML
ArticleID=20885
ArticleTitle=使用 XML Schema 的 Java 配置
publish-date=11012001