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.javaResourceBundleControlProvider 接口以及如何将其打包为安装型扩展。此示例包装在 zip 文件 RBCPTest.zip 中,包含以下文件:
src
java.util.spi.ResourceBundleControlProvider RBCPTest.javarbcp
resources
lib
build: Contains all files packaged in rbcontrolprovider.jar as well as the class file RBCPTest.classbuild.xml 以下步骤说明如何重新创建文件 RBCPTest.zip 的内容,RBCPTest 示例的工作方式以及如何运行它:
示例使用 RBCPTest.javaResourseBundle.Control 的两个实现:
:这是 Customizing Resource Bundle Loading 中定义 PropertiesResourceBundleControlProvider.javaResourceBundle.Control 的相同实现。
:此 XMLResourceBundleControl.javaResourceBundle.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.javaresources.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.ResourceBundleControlProviderPropertiesResourceBundleControlProvider 和 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.ResourceBundleControlProviderrbcp
PropertiesResourceBundleControl.classPropertiesResourceBundleControlProvider.classXMLResourceBundleControl.classXMLResourceBundleControlProvider.classresources
RBControl.propertiesRBControl_zh.propertiesRBControl_zh_CN.propertiesRBControl_zh_HK.propertiesRBControl_zh_TW.propertiesXmlRB.xmlXmlRB_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