将自定义资源包安装为扩展

原文: https://docs.oracle.com/javase/tutorial/i18n/serviceproviders/resourcebundlecontrolprovider.html

Customizing Resource Bundle Loading 部分向您展示了如何更改资源包的加载方式。这涉及从类 ResourceBundle.Control 派生一个新类,然后通过调用以下方法检索资源包:

  1. ResourceBundle getBundle(
  2. String baseName,
  3. Locale targetLocale,
  4. ResourceBundle.Control control)

参数controlResourceBundle.Control的实现。

java.util.spi.ResourceBundleControlProvider 接口使您可以更改以下方法加载资源包的方式:

  1. ResourceBundle getBundle(
  2. String baseName,
  3. 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)中,由以下文件组成:

以下步骤说明如何重新创建文件RBCPTest.zip的内容,RBCPTest示例的工作方式以及如何运行它:

  1. 创建 ResourceBundle.Control 类的实现。
  2. 实现 ResourceBundleControlProvider 接口。
  3. 在您的应用程序中,调用 ResourceBundle.getBundle 方法。
  4. 通过创建配置文件注册服务提供商。
  5. 将供应器,其必需的类和配置文件打包在 JAR 文件中。
  6. 运行 RBCPTest 程序。

RBCPTest.java 样例使用ResourseBundle.Control的两个实现:

XML 属性文件

使用属性文件备份 ResourceBundle 一节中所述,属性文件是简单的文本文件。它们在每一行包含一个键值对。 XML 属性文件就像属性文件一样:它们包含键值对,除了它们具有 XML 结构。以下是 XML 属性文件XmlRB.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE properties [
  3. <!ELEMENT properties ( comment?, entry* ) >
  4. <!ATTLIST properties version CDATA #FIXED "1.0">
  5. <!ELEMENT comment (#PCDATA) >
  6. <!ELEMENT entry (#PCDATA) >
  7. <!ATTLIST entry key CDATA #REQUIRED>
  8. ]>
  9. <properties>
  10. <comment>Test data for RBCPTest.java</comment>
  11. <entry key="type">XML</entry>
  12. </properties>

以下是属性文本文件等效:

  1. # Test data for RBCPTest.java
  2. type = XML

所有 XML 属性文本文件都具有相同的结构:

  • 指定文档类型定义(DTD)的 DOCTYPE 声明:DTD 定义 XML 文件的结构。 注意:您可以在 XML 属性文件中使用以下 DOCTYPE 声明:

    1. &lt;!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"&gt;

    导出或导入属性时不访问系统 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.javaXMLResourceBundleControlProvider.java。如果资源包的基本名称以resources.RBControl开头,则PropertiesResourceBundleControlProvider.getBundle方法返回PropertiesResourceBundleControl的实例(在此示例中,所有资源文件都包含在包resources中):

  1. package rbcp;
  2. import java.util.ResourceBundle;
  3. import java.util.spi.ResourceBundleControlProvider;
  4. public class PropertiesResourceBundleControlProvider
  5. implements ResourceBundleControlProvider {
  6. static final ResourceBundle.Control PROPERTIESCONTROL =
  7. new PropertiesResourceBundleControl();
  8. public ResourceBundle.Control getControl(String baseName) {
  9. System.out.println("Class: " + getClass().getName() + ".getControl");
  10. System.out.println(" called for " + baseName);
  11. // Throws a NPE if baseName is null.
  12. if (baseName.startsWith("resources.RBControl")) {
  13. System.out.println(" returns " + PROPERTIESCONTROL);
  14. return PROPERTIESCONTROL;
  15. }
  16. System.out.println(" returns null");
  17. System.out.println();
  18. return null;
  19. }
  20. }

类似地,如果资源包的基本名称以resources.Xml开头,则方法XMLResourceBundleControlProvider.getControl返回XMLResourceBundleControl的实例。

:您可以创建ResourceBundleControlProvider接口的一个实现,它根据基本名称返回PropertiesResourceBundleControlXMLResourceBundleControl的实例。

RBCPTest使用方法 ResourceBundle.getBundle 检索资源包:

  1. import java.io.*;
  2. import java.net.*;
  3. import java.util.*;
  4. public class RBCPTest {
  5. public static void main(String[] args) {
  6. ResourceBundle rb = ResourceBundle.getBundle(
  7. "resources.XmlRB", Locale.ROOT);
  8. String type = rb.getString("type");
  9. System.out.println("Root locale. Key, type: " + type);
  10. System.out.println();
  11. rb = ResourceBundle.getBundle("resources.XmlRB", Locale.JAPAN);
  12. type = rb.getString("type");
  13. System.out.println("Japan locale. Key, type: " + type);
  14. System.out.println();
  15. test(Locale.CHINA);
  16. test(new Locale("zh", "HK"));
  17. test(Locale.TAIWAN);
  18. test(Locale.CANADA);
  19. }
  20. private static void test(Locale locale) {
  21. ResourceBundle rb = ResourceBundle.getBundle(
  22. "resources.RBControl", locale);
  23. System.out.println("locale: " + locale);
  24. System.out.println(" region: " + rb.getString("region"));
  25. System.out.println(" language: " + rb.getString("language"));
  26. System.out.println();
  27. }
  28. }

请注意,此类中不会出现ResourceBundle.ControlResourceBundleControlProvider的实现。由于ResourceBundleControlProvider接口使用 Java 扩展机制,因此运行时环境会查找并加载这些实现。但是,ResourceBundleControlProvider实现和使用 Java 扩展机制安装的其他服务供应器使用 ServiceLoader 类加载。使用此类意味着您必须使用配置文件注册服务供应器,这将在下一步中介绍。

配置文件的名称是供应器实现的接口或类的完全限定名称。配置文件包含供应器的完全限定类名。文件 java.util.spi.ResourceBundleControlProvider包含PropertiesResourceBundleControlProviderXMLResourceBundleControlProvider的完全限定名称,每行一个名称:

  1. rbcp.XMLResourceBundleControlProvider
  2. rbcp.PropertiesResourceBundleControlProvider

编译源文件。从包含文件build.xml的目录中,运行以下命令:

  1. 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 后,请按照下列步骤操作:

  1. 编辑文件 build.xml并将${JAVAC}更改为 Java 编译器的完整路径名,javac${JAVA}为 Java 运行时的完整路径名可执行文件,java

  2. 从包含文件build.xml的同一目录运行以下命令:

    1. ant jar

    此命令编译 Java 源文件,并将它们以及所需的资源和配置文件打包到lib目录中的 JAR 文件rbcontrolprovider.jar中。

在命令提示符处,从包含build.xml文件的目录运行以下命令:

  1. java -Djava.ext.dirs=lib -cp build RBCPTest

此命令假定以下内容:

  • 包含 RBCPTest 示例的已编译代码的 JAR 文件位于lib目录中。
  • 已编译的类RBCPTest.class位于build目录中。

或者,使用 Apache Ant 并从包含build.xml文件的目录运行以下命令:

  1. ant run

安装 Java 扩展时,通常将扩展的 JAR 文件放在 JRE 的lib/ext目录中。但是,此命令指定包含具有系统属性java.ext.dirs的 Java 扩展的目录。

RBCPTest程序首先尝试检索基本名称为resources.XmlRB和语言环境Locale.ROOTLocal.JAPAN的资源包。检索这些资源包的程序输出类似于以下内容:

  1. Class: rbcp.XMLResourceBundleControlProvider.getControl
  2. called for resources.XmlRB
  3. returns rbcp.XMLResourceBundleControl@16c1857
  4. Root locale. Key, type: XML
  5. Class: rbcp.XMLResourceBundleControlProvider.getControl
  6. called for resources.XmlRB
  7. returns rbcp.XMLResourceBundleControl@16c1857
  8. Japan locale. Key, type: Value from Japan locale

程序成功获取XMLResourceBundleControl的实例并访问属性文件XmlRB.xmlXmlRB_ja.xml

RBCPTest程序尝试检索资源包时,它会调用配置文件java.util.spi.ResourceBundleControlProvider中定义的所有类。例如,当程序检索具有基本名称resources.RBControl和区域设置Locale.CHINA的资源包时,它将打印以下输出:

  1. Class: rbcp.XMLResourceBundleControlProvider.getControl
  2. called for resources.RBControl
  3. returns null
  4. Class: rbcp.PropertiesResourceBundleControlProvider.getControl
  5. called for resources.RBControl
  6. returns rbcp.PropertiesResourceBundleControl@1ad2911
  7. locale: zh_CN
  8. region: China
  9. language: Simplified Chinese