本Java教程是针对JDK 8编写的。本页面描述的示例和实践不利用后续版本中引入的改进,并可能使用不再可用的技术。
请参阅Java语言变更了解Java SE 9及其后续版本中更新的语言功能的摘要。
请参阅JDK发行说明了解有关所有JDK版本的新功能、增强功能以及已删除或已弃用选项的信息。
章节自定义资源束加载展示了如何改变资源束的加载方式。这涉及到从类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中,包含以下文件:
src
java.util.spi.ResourceBundleControlProviderRBCPTest.javarbcp
resources
lib
build:包含所有打包在rbcontrolprovider.jar中的文件以及类文件RBCPTest.classbuild.xml以下步骤向您展示如何重新创建文件RBCPTest.zip的内容,RBCPTest示例的工作原理以及如何运行它:
示例使用了两个RBCPTest.javaResourseBundle.Control的实现:
:这是与自定义资源束加载中定义的PropertiesResourceBundleControlProvider.javaResourceBundle.Control实现相同的实现。
:这个XMLResourceBundleControl.javaResourceBundle.Control实现使用Properties.loadFromXML方法加载基于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属性文本文件都具有相同的结构:
一个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类。
该接口包含一个方法,即ResourceBundle.Control getControl(String baseName)方法。参数baseName是资源包的名称。在getBundle的方法定义中,指定应返回的ResourceBundle.Control实例,给定资源包的名称。
RBCPTest示例包含了ResourceBundleControlProvider接口的两个实现,和PropertiesResourceBundleControlProvider.java。如果资源包的基本名称以XMLResourceBundleControlProvider.javaresources.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接口的类,根据基本名称返回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("根地区。键,类型: " + 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.Control或ResourceBundleControlProvider的实现。因为ResourceBundleControlProvider接口使用了Java扩展机制,运行时环境会找到并加载这些实现。然而,ResourceBundleControlProvider的实现以及其他使用Java扩展机制安装的服务提供者是使用ServiceLoader类加载的。使用这个类意味着您需要在配置文件中注册服务提供者,这将在下一步中进行说明。
配置文件的名称是提供者实现的接口或类的完全限定名。配置文件包含了提供者的完全限定类名。文件包含了java.util.spi.ResourceBundleControlProviderPropertiesResourceBundleControlProvider和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.ResourceBundleControlProviderrbcp
PropertiesResourceBundleControl.classPropertiesResourceBundleControlProvider.classXMLResourceBundleControl.classXMLResourceBundleControlProvider.classresources
RBControl.propertiesRBControl_zh.propertiesRBControl_zh_CN.propertiesRBControl_zh_HK.propertiesRBControl_zh_TW.propertiesXmlRB.xmlXmlRB_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
此命令假设以下内容:
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
根区域设置。键,类型:XML
Class: rbcp.XMLResourceBundleControlProvider.getControl
called for resources.XmlRB
returns rbcp.XMLResourceBundleControl@16c1857
日本区域设置。键,类型:来自日本区域设置的值
该程序成功获取了一个XMLResourceBundleControl的实例,并访问了属性文件XmlRB.xml和XmlRB_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
国家/地区: 中国
语言: 简体中文