文档

Java™ 教程-Java Tutorials 中文版
将自定义资源包安装为扩展
Trail: Internationalization
Lesson: Service Providers for Internationalization

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

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

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

参数 controlResourceBundle.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 中,包含以下文件:

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

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

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

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

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

有关 XML 属性文件的更多信息,请参见 Properties 类。

2. 实现 ResourceBundleControlProvider 接口。

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

RBCPTest 示例包含 ResourceBundleControlProvider 接口的两个实现,PropertiesResourceBundleControlProvider.javaXMLResourceBundleControlProvider.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 接口的一个实现,该实现返回 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 的实现。由于 ResourceBundleControlProvider 接口使用 Java 扩展机制,因此运行时环境会查找并加载这些实现。但是,使用 ServiceLoader 类加载 ResourceBundleControlProvider 实现和使用 Java 扩展机制安装的其他服务提供程序。使用此类意味着你必须使用配置文件注册服务提供程序,这将在下一步中介绍。

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

配置文件的名称是提供程序实现的接口或类的完全限定名称。配置文件包含提供者的完全限定类名。文件 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 文件,其中包含以下目录结构中的已编译类文件,资源文件和配置文件:

请注意,配置文件 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 后,请按照下列步骤操作:

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

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

    ant jar

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

6. 运行 RBCPTest 程序。

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

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

此命令假定以下内容:

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

ant run

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

RBCPTest 程序首先尝试获取基本名称为 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 程序尝试获取资源包时,它会调用配置文件 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

Previous page: Service Providers for Internationalization
Next page: End of Trail