文档

Java™ 教程
隐藏目录
将自定义资源包安装为扩展
路径:国际化
课程:国际化的服务提供者

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

章节自定义资源束加载展示了如何改变资源束的加载方式。这涉及到从类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类(定义了默认的资源束加载过程)。因此,ResourceBundleControlProvider接口使你可以使用任何自定义的ResourceBundle.Control类,而无需对应用程序的源代码进行任何额外的更改。此外,此接口使你能够编写应用程序,而无需引用任何自定义的ResourceBundle.Control类。

示例展示了如何实现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属性文件

使用属性文件支持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>RBCPTest.java的测试数据</comment>
    <entry key="type">XML</entry>
</properties>

以下是属性文本文件的等效内容:

# 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开头(在此示例中,所有资源文件都包含在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("类: " + getClass().getName() + ".getControl");
        System.out.println("    调用了 " + baseName);

        // 如果baseName为null,将抛出NPE。
        if (baseName.startsWith("resources.RBControl")) {
            System.out.println("    返回 " + PROPERTIESCONTROL);
            return PROPERTIESCONTROL;
        }
        System.out.println("    返回 null");
        System.out.println();
        return null;
    }
}

同样地,方法XMLResourceBundleControlProvider.getControl如果资源包的基本名称以resources.Xml开头,则返回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("根地区。键,类型: " + type);
        System.out.println();

        rb = ResourceBundle.getBundle("resources.XmlRB", Locale.JAPAN);
        type = rb.getString("type");
        System.out.println("日本地区。键,类型: " + 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);
        System.out.println("    区域: " + rb.getString("region"));
        System.out.println("    语言: " + rb.getString("language"));
        System.out.println();
    }
}

注意,这个类中没有ResourceBundle.ControlResourceBundleControlProvider的实现。因为ResourceBundleControlProvider接口使用了Java扩展机制,运行时环境会找到并加载这些实现。然而,ResourceBundleControlProvider的实现以及其他使用Java扩展机制安装的服务提供者是使用ServiceLoader类加载的。使用这个类意味着您需要在配置文件中注册服务提供者,这将在下一步中进行说明。

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文件的更多信息,请参见在JAR文件中打包程序

或者,您可以下载并安装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.ext.dirs指定包含Java扩展的目录。

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

Class: rbcp.XMLResourceBundleControlProvider.getControl
    called for resources.XmlRB
    returns rbcp.XMLResourceBundleControl@16c1857
根区域设置。键,类型:XML

Class: rbcp.XMLResourceBundleControlProvider.getControl
    called for resources.XmlRB
    returns rbcp.XMLResourceBundleControl@16c1857
日本区域设置。键,类型:来自日本区域设置的值

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

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

类: rbcp.XMLResourceBundleControlProvider.getControl
    调用 resources.RBControl
    返回 null

类: rbcp.PropertiesResourceBundleControlProvider.getControl
    调用 resources.RBControl
    返回 rbcp.PropertiesResourceBundleControl@1ad2911
区域: zh_CN
    国家/地区: 中国
    语言: 简体中文

上一页: 国际化的服务提供商
下一页: 结束之处