本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.ResourceBundleControlProvider
RBCPTest.java
rbcp
resources
lib
build
:包含所有打包在rbcontrolprovider.jar
中的文件以及类文件RBCPTest.class
build.xml
以下步骤向您展示如何重新创建文件RBCPTest.zip
的内容,RBCPTest
示例的工作原理以及如何运行它:
示例使用了两个RBCPTest.java
ResourseBundle.Control
的实现:
:这是与自定义资源束加载中定义的PropertiesResourceBundleControlProvider.java
ResourceBundle.Control
实现相同的实现。
:这个XMLResourceBundleControl.java
ResourceBundle.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.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
接口的类,根据基本名称返回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.ResourceBundleControlProvider
PropertiesResourceBundleControlProvider
和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.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
中。
在命令提示符下,从包含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 国家/地区: 中国 语言: 简体中文