将自定义资源包安装为扩展
原文: https://docs.oracle.com/javase/tutorial/i18n/serviceproviders/resourcebundlecontrolprovider.html
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 使您能够创建可扩展的应用程序,这些应用程序可以轻松扩展而无需修改其原始代码库。有关详细信息,请参阅创建可扩展应用程序。
要使用 SPI,首先要实现像ResourceBundleControlProvider
这样的 SPI 来创建服务提供者。实现 SPI 时,指定它将如何提供服务。 ResourceBundleControlProvider
SPI 提供的服务是在应用程序调用方法ResourceBundle.getBundle(String baseName, Locale targetLocale)
时获取适当的ResourceBundle.Control
实例。使用 Java 扩展机制将服务供应器打包为已安装的扩展。运行应用程序时,不要在类路径中命名扩展名;运行时环境查找并加载这些扩展。
已安装的ResourceBundleControlProvider
SPI 实现取代了默认的ResourceBundle.Control
类(定义了默认的 bundle 加载过程)。因此,ResourceBundleControlProvider
接口使您可以使用任何自定义ResourceBundle.Control
类,而无需对应用程序的源代码进行任何其他更改。此外,此接口使您无需引用任何自定义ResourceBundle.Control
类即可编写应用程序。
RBCPTest.java
示例显示了如何实现ResourceBundleControlProvider
接口以及如何将其打包为已安装的扩展。此样例包装在 zip 文件[RBCPTest.zip](examples/rbcpsample/RBCPTest.zip)
中,由以下文件组成:
src
java.util.spi.ResourceBundleControlProvider
RBCPTest.java
rbcp
resources
lib
build
:包含rbcontrolprovider.jar
中打包的所有文件以及类文件RBCPTest.class
build.xml
以下步骤说明如何重新创建文件RBCPTest.zip
的内容,RBCPTest
示例的工作方式以及如何运行它:
- 创建 ResourceBundle.Control 类的实现。
- 实现 ResourceBundleControlProvider 接口。
- 在您的应用程序中,调用 ResourceBundle.getBundle 方法。
- 通过创建配置文件注册服务提供商。
- 将供应器,其必需的类和配置文件打包在 JAR 文件中。
- 运行 RBCPTest 程序。
RBCPTest.java
样例使用ResourseBundle.Control
的两个实现:
PropertiesResourceBundleControlProvider.java
:这与自定义资源包加载中定义的ResourceBundle.Control
实现相同。XMLResourceBundleControl.java
:此ResourceBundle.Control
实现使用方法Properties.loadFromXML
加载基于 XML 的包。
XML 属性文件
如使用属性文件备份 ResourceBundle 一节中所述,属性文件是简单的文本文件。它们在每一行包含一个键值对。 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 扩展机制,因此运行时环境会查找并加载这些实现。但是,ResourceBundleControlProvider
实现和使用 Java 扩展机制安装的其他服务供应器使用 ServiceLoader
类加载。使用此类意味着您必须使用配置文件注册服务供应器,这将在下一步中介绍。
配置文件的名称是供应器实现的接口或类的完全限定名称。配置文件包含供应器的完全限定类名。文件 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 文件的更多信息,请参阅 JAR 文件中的打包程序。
或者,下载并安装 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
此命令假定以下内容:
- 包含 RBCPTest 示例的已编译代码的 JAR 文件位于
lib
目录中。 - 已编译的类
RBCPTest.class
位于build
目录中。
或者,使用 Apache Ant 并从包含build.xml
文件的目录运行以下命令:
ant run
安装 Java 扩展时,通常将扩展的 JAR 文件放在 JRE 的lib/ext
目录中。但是,此命令指定包含具有系统属性java.ext.dirs
的 Java 扩展的目录。
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