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

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

ResourceBundle getBundle(
  String baseName,
  Locale targetLocale,
  ResourceBundle.Control control)

参数control是您对ResourceBundle.Control的实现。

java.util.spi.ResourceBundleControlProviderinterface使您可以更改以下方法加载资源束的方式:

ResourceBundle getBundle(
  String baseName,
  Locale targetLocale)

请注意,此版本的ResourceBundle.getBundle方法不需要ResourceBundle.Control类的实例。 ResourceBundleControlProvider是服务提供商interface(SPI)。 SPI 使您能够创建可扩展的应用程序,这些应用程序可以轻松扩展而无需修改其原始代码库。有关更多信息,请参见创建可扩展的应用程序

要使用 SPI,您首先要通过实现ResourceBundleControlProvider之类的 SPI 创建服务提供商。实现 SPI 时,请指定其将如何提供服务。 ResourceBundleControlProvider SPI 提供的服务是在您的应用程序调用方法ResourceBundle.getBundle(String baseName, Locale targetLocale)时获得适当的ResourceBundle.Control实例。您将Java 扩展机制作为已安装的扩展程序打包到服务提供商。运行应用程序时,不要在 Classpath 中命名 extensions。运行时环境查找并加载这些扩展。

ResourceBundleControlProvider SPI 的已安装实现将替换默认的ResourceBundle.Control类(定义了默认的 Binding 包加载过程)。因此,通过ResourceBundleControlProviderinterface,您可以使用任何自定义ResourceBundle.Control类,而无需对应用程序的源代码进行任何其他更改。另外,该interface使您无需引用任何自定义ResourceBundle.Control类即可编写应用程序。

RBCPTest.java示例显示了如何实现ResourceBundleControlProviderinterface以及如何将其打包为已安装的扩展。此示例打包在 zip 文件RBCPTest.zip中,由以下文件组成:

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

1.创建 ResourceBundle.Control 类的实现。

RBCPTest.java示例使用ResourseBundle.Control的两种实现:

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 属性文本文件都具有相同的结构:

  • DOCTYPE 声明,用于指定文档类型定义(DTD):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类。

2.实现 ResourceBundleControlProvider interface。

此interface包含一个方法,即ResourceBundle.Control getControl(String baseName)方法。参数baseName是资源束的名称。在getBundle的方法定义中,指定给定资源包名称的应返回的ResourceBundle.Control实例。

RBCPTest示例包含ResourceBundleControlProviderinterface的两种实现PropertiesResourceBundleControlProvider.javaXMLResourceBundleControlProvider.java。如果资源束的基本名称以resources.RBControl开头(在此示例中,所有资源文件都包含在resources包中),则方法PropertiesResourceBundleControlProvider.getBundle返回PropertiesResourceBundleControl的实例:

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的实例。

注意 :您可以创建ResourceBundleControlProviderinterface的一种实现,该实现根据基本名称返回PropertiesResourceBundleControlXMLResourceBundleControl的实例。

3.在您的应用程序中,调用方法 ResourceBundle.getBundle。

类别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.ControlResourceBundleControlProvider的实现。因为ResourceBundleControlProviderinterface使用 Java 扩展机制,所以运行时环境将找到并加载这些实现。但是,使用ServiceLoader类加载了与 Java 扩展机制一起安装的ResourceBundleControlProvider实现和其他服务提供者。使用此类意味着您必须向配置文件注册服务提供商,这将在下一步中进行描述。

4.通过创建配置文件注册服务提供商。

配置文件的名称是提供程序实现的interface或类的标准名称。配置文件包含提供程序的完全限定的类名。文件java.util.spi.ResourceBundleControlProvider包含PropertiesResourceBundleControlProviderXMLResourceBundleControlProvider的完全限定名称,每行一个名称:

rbcp.XMLResourceBundleControlProvider
rbcp.PropertiesResourceBundleControlProvider

5.将提供程序,其必需的类和配置文件打包到 JAR 文件中。

编译源文件。在包含文件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中。

6.运行 RBCPTest 程序。

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

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

该命令假定以下内容:

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

  • 编译后的类RBCPTest.classbuild目录中。

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

ant run

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

RBCPTest程序首先try检索具有基本名称resources.XmlRB以及语言环境Locale.ROOTLocal.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.xmlXmlRB_ja.xml

RBCPTest程序try检索资源束时,它将调用配置文件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