Java 教程是为 JDK 8 编写的。本页中描述的示例和实践未利用在后续版本中引入的改进。
Customizing Resource Bundle Loading 部分向你展示了如何更改资源包的加载方式。这涉及从类 ResourceBundle.Control
派生一个新类,然后通过调用以下方法获取资源包:
ResourceBundle getBundle( String baseName, Locale targetLocale, ResourceBundle.Control control)
参数 control
是 ResourceBundle.Control
的实现。
java.util.spi.ResourceBundleControlProvider
接口使你可以更改以下方法加载资源包的方式:
ResourceBundle getBundle( String baseName, Locale targetLocale)
请注意,此版本的 ResourceBundle.getBundle
方法不需要 ResourceBundle.Control
类的实例。ResourceBundleControlProvider
是服务提供者接口(SPI)。SPI 使你能够创建可扩展的应用程序,这些应用程序可以轻松扩展而无需修改其原始代码源。有关详细信息,请参阅 Creating Extensible Applications。
要使用 SPI,首先要通过实现类似 ResourceBundleControlProvider
的 SPI 来创建服务提供者。实现 SPI 时,指定它将如何提供服务。ResourceBundleControlProvider
SPI 提供的服务是在应用程序调用方法 ResourceBundle.getBundle(String baseName, Locale targetLocale)
方法时获取适当的 ResourceBundle.Control
实例。使用 Java Extension Mechanism 将服务提供程序打包为安装型扩展。运行应用程序时,不要在类路径中命名扩展名;运行时环境查找并加载这些扩展。
安装型的 ResourceBundleControlProvider
SPI 实现替换了默认的 ResourceBundle.Control
类(定义了默认的 bundle 加载过程)。因此,ResourceBundleControlProvider
接口使你可以使用任何自定义 ResourceBundle.Control
类,而无需对应用程序的源代码进行任何其他更改。此外,此接口使你可以编写应用程序,而无需引用任何自定义 ResourceBundle.Control
类。
示例显示了如何实现 RBCPTest.java
ResourceBundleControlProvider
接口以及如何将其打包为安装型扩展。此示例包装在 zip 文件 RBCPTest.zip
中,包含以下文件:
src
java.util.spi.ResourceBundleControlProvider
RBCPTest.java
rbcp
resources
lib
build
: Contains all files packaged in rbcontrolprovider.jar
as well as the class file RBCPTest.class
build.xml
以下步骤说明如何重新创建文件 RBCPTest.zip
的内容,RBCPTest
示例的工作方式以及如何运行它:
示例使用 RBCPTest.java
ResourseBundle.Control
的两个实现:
:这是 Customizing Resource Bundle Loading 中定义 PropertiesResourceBundleControlProvider.java
ResourceBundle.Control
的相同实现。
:此 XMLResourceBundleControl.java
ResourceBundle.Control
实现使用方法 Properties.loadFromXML
加载基于 XML 的包。
如 Backing a ResourceBundle with Properties Files 一节中所述,属性文件是简单的文本文件。它们在每一行包含一个键值对。XML 属性文件就像属性文件一样:它们包含键值对,除了它们具有 XML 结构。以下是 XML 属性文件 XmlRB.xml
:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties [ <!ELEMENT properties ( comment?, entry* ) > <!ATTLIST properties version CDATA #FIXED "1.0"> <!ELEMENT comment (#PCDATA) > <!ELEMENT entry (#PCDATA) > <!ATTLIST entry key CDATA #REQUIRED> ]> <properties> <comment>Test data for RBCPTest.java</comment> <entry key="type">XML</entry> </properties>
以下是等效属性文本文件:
# Test data for RBCPTest.java type = XML
所有 XML 属性文本文件都具有相同的结构:
指定文档类型定义(DTD)的 DOCTYPE 声明:DTD 定义 XML 文件的结构。注意:你可以在 XML 属性文件中使用以下 DOCTYPE 声明:
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
导出或导入属性时,不访问系统 URI(http://java.sun.com/dtd/properties.dtd
);它是一个唯一标识 XML 属性文件的 DTD 的字符串。
根元素 <properties>
:此元素包含所有其他元素。
任意数量的 <comment>
元素:这些元素用于注释。
任意数量的 <entry>
元素:使用属性 key
指定键;在 <entry>
标签之间指定键的值。
有关 XML 属性文件的更多信息,请参见 Properties
类。
此接口包含一个方法,ResourceBundle.Control getControl(String baseName)
方法。参数 baseName
是资源包的名称。在 getBundle
的方法定义中,指定应在给定资源包名称的情况下返回的 ResourceBundle.Control
的实例。
RBCPTest
示例包含 ResourceBundleControlProvider
接口的两个实现,
和 PropertiesResourceBundleControlProvider.java
。如果资源包的基本名称以 XMLResourceBundleControlProvider.java
resources.RBControl
开头,则方法 PropertiesResourceBundleControlProvider.getBundle
将返回 PropertiesResourceBundleControl
的实例(在此示例中,全部资源文件包含在包 resources
中:
package rbcp; import java.util.ResourceBundle; import java.util.spi.ResourceBundleControlProvider; public class PropertiesResourceBundleControlProvider implements ResourceBundleControlProvider { static final ResourceBundle.Control PROPERTIESCONTROL = new PropertiesResourceBundleControl(); public ResourceBundle.Control getControl(String baseName) { System.out.println("Class: " + getClass().getName() + ".getControl"); System.out.println(" called for " + baseName); // Throws a NPE if baseName is null. if (baseName.startsWith("resources.RBControl")) { System.out.println(" returns " + PROPERTIESCONTROL); return PROPERTIESCONTROL; } System.out.println(" returns null"); System.out.println(); return null; } }
类似地,如果资源包的基本名称以 resources.Xml
开头,则方法 XMLResourceBundleControlProvider.getControl
将返回 XMLResourceBundleControl
的实例。
注意:你可以创建 ResourceBundleControlProvider
接口的一个实现,该实现返回 PropertiesResourceBundleControl
或 XMLResourceBundleControl
的实例,具体取决于基本名称。
类 RBCPTest
使用方法 ResourceBundle.getBundle
获取资源包:
import java.io.*; import java.net.*; import java.util.*; public class RBCPTest { public static void main(String[] args) { ResourceBundle rb = ResourceBundle.getBundle( "resources.XmlRB", Locale.ROOT); String type = rb.getString("type"); System.out.println("Root locale. Key, type: " + type); System.out.println(); rb = ResourceBundle.getBundle("resources.XmlRB", Locale.JAPAN); type = rb.getString("type"); System.out.println("Japan locale. Key, type: " + type); System.out.println(); test(Locale.CHINA); test(new Locale("zh", "HK")); test(Locale.TAIWAN); test(Locale.CANADA); } private static void test(Locale locale) { ResourceBundle rb = ResourceBundle.getBundle( "resources.RBControl", locale); System.out.println("locale: " + locale); System.out.println(" region: " + rb.getString("region")); System.out.println(" language: " + rb.getString("language")); System.out.println(); } }
请注意,此类中不会出现 ResourceBundle.Control
或 ResourceBundleControlProvider
的实现。由于 ResourceBundleControlProvider
接口使用 Java 扩展机制,因此运行时环境会查找并加载这些实现。但是,使用 ServiceLoader
类加载 ResourceBundleControlProvider
实现和使用 Java 扩展机制安装的其他服务提供程序。使用此类意味着你必须使用配置文件注册服务提供程序,这将在下一步中介绍。
配置文件的名称是提供程序实现的接口或类的完全限定名称。配置文件包含提供者的完全限定类名。文件
包含 java.util.spi.ResourceBundleControlProvider
PropertiesResourceBundleControlProvider
和 XMLResourceBundleControlProvider
的完全限定名称,每行一个名字:
rbcp.XMLResourceBundleControlProvider rbcp.PropertiesResourceBundleControlProvider
编译源文件。从包含文件 build.xml
的目录中,运行以下命令:
javac -d build src/java.* src/rbcp/*.java
此命令将编译 src
目录中包含的源文件,并将类文件放在 build
目录中。在 Windows 上,请确保使用反斜杠(\
)分隔目录和文件名。
创建一个 JAR 文件,其中包含以下目录结构中的已编译类文件,资源文件和配置文件:
META-INF
services
java.util.spi.ResourceBundleControlProvider
rbcp
PropertiesResourceBundleControl.class
PropertiesResourceBundleControlProvider.class
XMLResourceBundleControl.class
XMLResourceBundleControlProvider.class
resources
RBControl.properties
RBControl_zh.properties
RBControl_zh_CN.properties
RBControl_zh_HK.properties
RBControl_zh_TW.properties
XmlRB.xml
XmlRB_ja.xml
请注意,配置文件 java.util.spi.ResourceBundleControlProvider
必须打包在目录 /META-INF/services
中。此示例将这些文件打包在 lib
目录中的 JAR 文件 rbcontrolprovider.jar
中。
有关创建 JAR 文件的更多信息,请参阅 Packaging Programs in JAR Files。
或者,下载并安装 Apache Ant,这是一个使你能够自动化构建过程的工具,例如编译 Java 文件和创建 JAR 文件。确保 Apache Ant 可执行文件位于 PATH
环境变量中,以便你可以从任何目录运行它。安装 Apache Ant 后,请按照下列步骤操作:
编辑文
并将 build.xml
${JAVAC}
更改为 Java 编译器,javac
,的完整路径名,将 ${JAVA}
更改为 Java 运行时可执行文件,java
,的完整路径名。
从包含文件 build.xml
的同一目录运行以下命令:
ant jar
此命令编译 Java 源文件,并将它们以及所需的资源和配置文件打包到 lib
目录中的 JAR 文件 rbcontrolprovider.jar
中。
在命令提示符处,从包含 build.xml
文件的目录运行以下命令:
java -Djava.ext.dirs=lib -cp build RBCPTest
此命令假定以下内容:
lib
中。RBCPTest.class
位于 build
目录中。或者,使用 Apache Ant 并从包含 build.xml
文件的目录运行以下命令:
ant run
安装 Java 扩展时,通常将扩展的 JAR 文件放在 JRE 的 lib/ext
目录中。但是,此命令指定包含 Java 扩展的目录,使用系统属性 java.ext.dirs
。
RBCPTest
程序首先尝试获取基本名称为 resources.XmlRB
的资源包,并且语言环境 Locale.ROOT
和 Local.JAPAN
。获取这些资源包的程序输出类似于以下内容:
Class: rbcp.XMLResourceBundleControlProvider.getControl called for resources.XmlRB returns rbcp.XMLResourceBundleControl@16c1857 Root locale. Key, type: XML Class: rbcp.XMLResourceBundleControlProvider.getControl called for resources.XmlRB returns rbcp.XMLResourceBundleControl@16c1857 Japan locale. Key, type: Value from Japan locale
程序成功获取 XMLResourceBundleControl
的实例,并访问属性文件 XmlRB.xml
和 XmlRB_ja.xml
。
当 RBCPTest
程序尝试获取资源包时,它会调用配置文件 java.util.spi.ResourceBundleControlProvider
中定义的所有类。例如,当程序获取具有基本名称 resources.RBControl
和语言环境 Locale.CHINA
的资源包时,它将打印以下输出:
Class: rbcp.XMLResourceBundleControlProvider.getControl called for resources.RBControl returns null Class: rbcp.PropertiesResourceBundleControlProvider.getControl called for resources.RBControl returns rbcp.PropertiesResourceBundleControl@1ad2911 locale: zh_CN region: China language: Simplified Chinese