servlet&jsp高级:第二部分

时间:2023-03-08 23:48:49
servlet&jsp高级:第二部分
servlet&jsp高级:第二部分
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4289401.html

Servlet事件监听器... 19

监听域对象的创建和销毁... 19

ServletContextListener接口... 19

HttpSessionListener接口... 19

ServletRequestListener接口... 20

监听域对象的属性变化... 20

attributeAdded. 20

attributeRemoved. 20

attributeReplaced. 20

感知Session绑定的事件监听器... 22

HttpSessionBindingListener接口... 22

HttpSessionActivationListener接口... 22

示例... 22

国际化... 23

与国际化相关的Java类... 23

Locale类... 23

DateFormat类... 25

获得DateFormat类实例对象... 25

日期/时间的格式化和解析... 26

示例... 26

NumberFormat类... 28

获得NumberFormat类实例对象... 28

数值的格式化和解析... 28

示例... 28

MessageFormat类... 30

简单示例... 30

在应用程序中动态设置占位符的格式类型和模式... 31

直接在占位符中设置占位符的格式类型和模式... 32

ResourceBundle类... 34

装载资源包与读取资源信息... 34

ResourceBundle类的扩展应用... 35

自定义ResourceBundle的子类... 35

ListResourceBundle类... 37

Web应用的国际化... 38

获取Web应用中的本地信息... 38

Web应用的国际化示例... 38

Servlet事件监听器

按监听的对象划分,Servlet 2.4规范中定义的事件监听器可以分为如下三种类型:

l  用于监听应用程序环境对象(ServletContext)的事件监听器;

l  用于监听用户会话对象(HttpSession)的事件监听器;

l  用于监听请求消息对象(ServletRequest)的事件监听器.

按照监听的事件类型进行划分,Servlet 2.4规范中定义的事件监听器又可以分为)三种类型:

l  用于监听域对象自身的创建和销毁的事件监听器;

l  用于监听域对象中的属性的增加和删除的事件监听器;

l  用于监听绑定到HttpSession域中的某个对象的状态的事件监听器;

web.xml中配置:

<listener>

<listener-class>xx.MyListener</listener-class>

</listener>

监听域对象的创建和销毁

ServletContextListener接口

用于监听ServletContext对象的创建和销毁的事件。

contextInitialized(ServletContextEvent sce)

contextDestroyed(ServletContextEvent sce)

HttpSessionListener接口

用于监听用户会话对象HttpSession的创建和销毁事件。

sessionCreated(HttpSessionEvent se)

sessionDestroyed(HttpSessionEvent se)

ServletRequestListener接口

用于监听ServletRequest对象的创建和销毁事件。

requestInitialized(ServletRequestEvent sre)

requestDestroyed(ServletRequestEvent sre)

监听域对象的属性变化

这些监听器接口分别是:用于监听ServletContext对象中的属性变更信息的ServletContextAttributeListener接口,用于监听HttpSession对象中的属性变更信息的HttpSessionAttributeListener接口,用于监听ServletRequest对象中的属性变更信息的ServletRequestAttributeListener接口。这三个接口中都定义了三个方法来处理被监听对象中的属性的增加、删除和替换的事件,同一个事件在这三个接口中对应的方法名称完全相同,只是接受的参数类型不同,它们的参数与发生属性更改的域对象相对应。

attributeAdded

向域对象中增加一个属性时调用。

ServletContextAttributeListener.attributeAdded(ServletContextAttributeEvent scab)

HttpSessionAttributeListener.attributeAdded(HttpSessionBindingEvent se)

ServletRequestAttributeListener.attributeAdded(ServletRequestAttributeEvent srae)

attributeRemoved

从域对象中删除一个属性时调用。

ServletContextAttributeListener.attributeRemoved(ServletContextAttributeEvent scab)

HttpSessionAttributeListener.attributeRemoved(HttpSessionBindingEvent se)

ServletRequestAttributeListener.attributeRemoved(ServletRequestAttributeEvent srae)

attributeReplaced

从域对象中替换一个属性时调用。

ServletContextAttributeListener.attributeReplaced(ServletContextAttributeEvent scab)

HttpSessionAttributeListener.attributeReplaced(HttpSessionBindingEvent se)

ServletRequestAttributeListener.attributeReplaced(ServletRequestAttributeEvent srae)

public class MyAttributeListener implements ServletContextAttributeListener,

HttpSessionAttributeListener, ServletRequestAttributeListener {

public void attributeAdded(ServletContextAttributeEvent scae) {

System.out.println("servletContext对象中增加了一个名为" + scae.getName()

+ "的属性,该属性值为" + scae.getValue());

}

public void attributeRemoved(ServletContextAttributeEvent scae) {

System.out.println("servletContext对象中的" + scae.getName() + "属性被删除了");

}

public void attributeReplaced(ServletContextAttributeEvent scae) {

System.out.println("servletContext对象中" + scae.getName() + "的属性值被替换成了"

+ scae.getValue());

}

public void attributeAdded(HttpSessionBindingEvent hbe) {

System.out.println("httpSession对象中增加了一个名为" + hbe.getName()

+ "的属性,该属性值为" + hbe.getValue());

}

public void attributeRemoved(HttpSessionBindingEvent hbe) {

System.out.println("httpSession对象中的" + hbe.getName() + "属性被删除了");

}

public void attributeReplaced(HttpSessionBindingEvent hbe) {

System.out.println("httpSession对象中" + hbe.getName() + "的属性值被替换成了"

+ hbe.getValue());

}

public void attributeAdded(ServletRequestAttributeEvent srae) {

System.out.println("servletRequest对象中增加了一个名为" + srae.getName()

+ "的属性,该属性值为" + srae.getValue());

}

public void attributeRemoved(ServletRequestAttributeEvent srae) {

System.out.println("servletRequest对象中的" + srae.getName() + "属性被删除了");

}

public void attributeReplaced(ServletRequestAttributeEvent srae) {

System.out.println("servletRequest对象中" + srae.getName() + "的属性值被替换成了"

+ srae.getValue());

}

感知Session绑定的事件监听器

保存到Session域中的对象可以有多种状态:绑定(保存)到Session域中、从Session域中解除绑定、随Session对象持久化(钝化)到一个存储设备中,随Session对象从一个存储设备中恢复(活化)。在Servlet规范中还定义了两个特殊的监听器接口来帮助JavaBean对象了解自己在Session域中的这些状态,这两个接口的名称分别为HttpSessionBindingListener和HttpSessionActivationListener,实现这两个接口的类不需要在web.xml文件中注册。只有要绑定到Session域中的Java对象才可能需要实现这两个接口,实现了HttpSessionBindingListener接口的JavaBean对象可以感知自己被绑定到Session中和从Session中删除的事件,实现了HttpSessionActivationListener接口的JavaBean对象可以感知自己被活化和钝化的事件。实现了这两个接口的JavaBean对象可以完成一些特殊功能,例如,可以在自己被钝化时释放与数据库的连接,在活化时重新与数据库建立连接。

HttpSessionBindingListener接口

当JavaBean对象被绑定到HttpSession对象中和从HttpSession对象中解除绑定时分别调用以下两个方法。

valueBound(HttpSessionBindingEvent event)

valueUnbound(HttpSessionBindingEvent event)

HttpSessionActivationListener接口

sessionWillPassivate(HttpSessionEvent se):HttpSession对象钝化之前被容器调用

sessionDidActivate(HttpSessionEvent se):HttpSession对象激活之后被容器调用

示例

public class MyBean implements HttpSessionBindingListener,

HttpSessionActivationListener, Serializable {

// 该方法被调用时,打印出对象将要被绑定的信息

public void valueBound(HttpSessionBindingEvent hbe) {

System.out.println("当前Session的ID标识为" + hbe.getSession().getId());

System.out.println("对象被绑定到这个Session对象中的" + hbe.getName() + "属性上");

}

// 该方法被调用时,打印出对象将要被解除绑定的信息

public void valueUnbound(HttpSessionBindingEvent hbe) {

System.out.println("当前Session的ID标识为" + hbe.getSession().getId());

System.out.println("对象将要从这个Session对象中的" + hbe.getName() + "属性上解除绑定");

}

public void sessionWillPassivate(HttpSessionEvent hse) {

System.out.println("对象将被持久化到文件系统中");

}

public void sessionDidActivate(HttpSessionEvent hse) {

System.out.println("对象从文件系统中恢复了");

}

}

国际化

与国际化相关的Java

servlet&jsp高级:第二部分

Locale

Locale(String language)

Locale(String language, String country)

Locale(String language, String country, String variant)

语言参数(language)是一个有效的 ISO 语言代码。这些代码是由 ISO-639 定义的小写两字母代码。在许多网站上都可以找到这些代码的完整列表,如:http://www.loc.gov/standards/iso639-2/php/English_list.php

国家/地区参数(country)是一个有效的 ISO 国家/地区代码。这些代码是由 ISO-3166 定义的大写两字母代码。在许多网站上都可以找到这些代码的完整列表,如:http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm

变量参数(variant)是特定于供应商或浏览器的代码。例如,用 WIN 代表 Windows、MAC 代表 Macintosh 和 POSIX 代表 POSIX。有两个变量时,把它们用下划线区分开来,把最重要的放在前面。例如,一个传统的西班牙排序规则可能用下列语言、国家/地区和变量参数来构造一个语言环境:"es"、"ES"、"Traditional_WIN"。

JDK中的一些本地敏感的类也提供了方法来返回它们所支持的所有本地信息对应时Locale实例对象的数组,例如,java.text.DateFormat类和java.text.NumberFormat类中都定义了一个getAvailableLocales静态方法,用于返回它们所支持的所有本地信息对应的Locale实例对象的数组。

Locale 类提供了一些方便的常量,可用这些常量为常用的语言环境创建 Locale 对象。例如,下面的内容为美国创建了一个 Locale 对象: Locale.US

String getDisplayName():返回适合向用户显示的语言环境名。这将把 getDisplayLanguage()、getDisplayCountry() 和 getDisplayVariant() 返回的值组合到单个字符串中。

创建完 Locale 后,就可以查询有关其自身的信息。使用 getCountry 可获取 ISO 国家/地区代码,使用 getLanguage 则获取 ISO 语言代码。可用使用 getDisplayCountry 来获取适合向用户显示的国家/地区名。同样,可用使用 getDisplayLanguage 来获取适合向用户显示的语言名。有趣的是,getDisplayXXX 方法本身是语言环境敏感的,它有两个版本:一个使用默认的语言环境作为参数,另一个则使用指定的语言环境作为参数。

Locale类提供了获得ISO语言代码和ISO国家代码的静态方法,例如,静态方法getISOLanguages用于返回包含ISO-639定义的所有的语言代码的数组,静态方法getISOCountries用于返回包含ISO-3166定义的所有的国家代码的数组。

Java 2 平台提供了多个可执行语言环境敏感操作的类:例如,NumberFormat 类以语言环境敏感的方式格式化数值、货币或百分比:

NumberFormat.getInstance(myLocale)

NumberFormat.getCurrencyInstance(myLocale)

NumberFormat.getPercentInstance(myLocale)

使用getDefault静态方法可以返回代表操作系统当前设置的本地信息的Locale实例对象。

示例:

public class LocaleExam {

public static void main(String[] args) {

Locale locale = new Locale("de", "CH");

System.out.println("德文地区的ISO语言代码:" + locale.getLanguage());

System.out.println("德文中的“德文”:" + locale.getDisplayLanguage(locale));

System.out.println("中文中的“德文”:" + locale.getDisplayLanguage(Locale.CHINA));

System.out.println("德文(瑞士)的Locale对象按操作系统的" + "默认本地方式显示的名称为:"

+ locale.getDisplayName());

System.out.println("德文(瑞士)的Locale对象按德文(瑞士)的" + "本地方式显示的信息为:"

+ locale.getDisplayName(locale));

}

}

德文地区的ISO语言代码:de

德文中的“德文”:Deutsch

中文中的“德文”:德文

德文(瑞士)的Locale对象按操作系统的默认本地方式显示的名称为:德文 (瑞士)

德文(瑞士)的Locale对象按德文(瑞士)的本地方式显示的信息为:Deutsch (Schweiz)

DateFormat

DateFormat类定义了一些用于描述日期/时间的显示模式的int类型的常量,包括FULL、LONG、MEDIUM、DEFAULTSHORT,这些常量用于描述显示日期/时间的字符串的长度,其中种格式,FormatStyle表示FormatType指定的格式类型下的某种模式,它的设置值如下:

l  如果FormatType的值为number,则FormatStyle的值可以是integer、currency、percent 3种标准模式,除此之外,FormatStyle的值也可以是用户自定义的表示数值格式的模式文本,例如,“#,##0.0#”表示数值的整数部分最多显示4位,并按3位进行分组,小数部分最多显示两位,如果整数部分为0,则显示一个0,如果小数据部分为0,则也显示一个0;“#”和“0”都表示显示一个数值位,如果数值中不存在“#”对应的位时,则什么也不显示,如果数值中不存在“0”对应的位时,则显示字符0。关于数值格式的自定的模式更多信息,请参数DecimalFormat类的帮助文档。

l  如果FormatType的值为date或time,则FormatStyle值可以是short、medium、long、full 4种标准模式,除此之外,FormatStyle的值也可以是用户自定表示日期/时间格式的模式文本,如果“yyyy-MM-dd HH:mm:ss”。关于日期时间格式的自定的模式具体信息,请参数SimpleDateFormat类的帮助文档。

public class MessageFormatExam4 {

public static void main(String args[]) throws Exception {

String pattern = "At {0, time, short} on {0, date}, {1} destroyed "

+ "{2,number,#,##0.00#} houses and caused {3, number, currency} of damage.";

MessageFormat msgFmt = new MessageFormat(pattern);

String datetimeString = "Jul 3, 1998 12:30 PM";

Date date = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,

DateFormat.SHORT, Locale.US).parse(datetimeString);

String event = "a hurricance";

Object[] msgArgs = { date, event, new Integer(9999999), new Double(1E7) };

String result = msgFmt.format(msgArgs);

System.out.println(result);

}

}

At 下午12:30 on 1998-7-3, a hurricance destroyed 9,999,999.00 houses and caused ¥10,000,000.00 of damage.

l  对于更复杂的模式,可以使用 ChoiceFormat 来生成正确的单数和复数形式:

public static void main(String args[]) throws Exception {

MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");

double[] filelimits = { 0, 1, 2 };

//在ChoiceFormat模式占位符中指定模式

String[] filepart = { "no files", "one file", "{0,number} files" };

ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);

form.setFormatByArgumentIndex(0, fileform);

int fileCount = 1273;

String diskName = "MyDisk";

Object[] testArgs = { new Long(fileCount), diskName };

System.out.println(form.format(testArgs));

}

The disk "MyDisk" contains 1,273 files.

如上例所示,可以以编程方式来创建 ChoiceFormat,或使用模式创建。有关更多信息,请参阅 ChoiceFormat:

public static void main(String args[]) throws Exception {

double[] filelimits = { 0, 1, 2 };

//ChoiceFormat中占位符中未指定格式

String[] filepart = { "are no files", "is one file", "are {2} files" };

ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);

//通过编程方式来指定

Format[] testFormats = { fileform, null, NumberFormat.getInstance() };

MessageFormat pattform = new MessageFormat("There {0} on {1}");

pattform.setFormats(testFormats);

Object[] testArgs = { null, "ADisk", null };

for (int i = 0; i < 4; ++i) {

testArgs[0] = new Integer(i);

testArgs[2] = testArgs[0];

System.out.println(pattform.format(testArgs));

}

}

There are no files on ADisk

There is one file on ADisk

There are 2 files on ADisk

There are 3 files on Adisk

或者是这样也可以:

public class MessageFormatExam4 {

public static void main(String args[]) throws Exception {

MessageFormat form = new MessageFormat(

"There {0,choice,0#are no files|1#is one file|1<are {0,number,integer} files} on {1}.");

String diskName = "ADisk";

Object[] testArgs = { null, diskName };

for (int i = 0; i < 4; ++i) {

testArgs[0] = new Integer(i);

System.out.println(form.format(testArgs));

}

}

}

结果与前面程序输出一样。

ResourceBundle

资源文件完全遵循java.util.Properties类要求的文件格式,它要求资源文件中的字符必须全部为有效的ASCII字符,如果资源文件中要包含非ASCII的字符,必须将它们转换成“\uXXXX”形式的转义序列,其中,XXXX是该字符的Unicode编码的十六进制数值。

native2ascii -encoding gb2312 temp.properties MyResources_zh_CN.properties

装载资源包与读取资源信息

ResourceBundle类提供了存放和管理资源包的功能,调用ResourceBundle类的静态方法getBundle可以获得ResourceBundle类的实例对象,getBundle方法有如下三种重载的形式:

public static final ResourceBundle getBundle(String baseName)

public static final ResourceBundle getBundle(String baseName, Locale locale)

public static ResourceBundle getBundle(String baseName, Locale locale, ClassLoader loader)

getBundle 使用基本名称、指定的语言环境和默认语言环境(从 Locale.getDefault 获得)来生一系列要查找的资源文件的名称,然后依次查找这些资源文件,直到找到第一个最匹配的资源文件为止。如果指定语言环境的语言、国家和变量都是空字符串,则基本名称就是惟一的候选包名称。否则,从指定语言环境 (language1, country1, and variant1) 和默认语言环境 (language2, country2, and variant2) 的属性值生成下列查找序列:

baseName + "_" + language1 + "_" + country1 + "_" + variant1

baseName + "_" + language1 + "_" + country1

baseName + "_" + language1

baseName + "_" + language2 + "_" + country2 + "_" + variant2

baseName + "_" + language2 + "_" + country2

baseName + "_" + language2

baseName

假设pak1.pak2包下有 a.properties 资源文件,则可以使用以下几种方式来加载:

ResourceBundle rb =ResourceBundle.getBundle("pak1.pak2.a");

ResourceBundle rb =ResourceBundle.getBundle("pak1/pak2/a");

ResourceBundle rb =ResourceBundle.getBundle("pak1/pak2.a");

如果传递给getBundle方法的baseName参数中包含了“.”字符,则将被全部替换成“/”字符,用作目录路径分隔符。注,路径不能以“/”开头。

ResourceBundle是一个抽象类,ResourceBundle.getBundle方法返回的实际上是ResourceBundle的一个具体子类的实例对象。JDK中提供的PropertyResourceBundle类是ResourceBundle类的一个具体子类,它可以从Properties格式的文件中加载每项资源信息。如果getBundle方法找到了合适的资源文件,将以该资源文件中的内容创建一个PropertyResourceBundle实例对象并返回这个实例对象。如果在整个资源包中没有找到任何一个合适的资源文件,ResourceBundle.getBundle方法将抛出异常。

ResourceBundle类的扩展应用

ResourceBundle类提供了存放和管理一个资源包中的资源信息的功能。ResourceBundle是一个抽象类,ResourceBundle.getBundle方法返回的是ResourceBundle的一个具体子类的实例对象。JDK中的PropertyResourceBundle类提供了管理资源信息的简单方式,如果这个类不能满足应用程序的需求,用户还可以自己定义ResourceBundle的具体子类来管理资源信息,并让ResourceBundle.getBundle返回用户自己定义的ResourceBundle具体子类的实例对象。

其实,ResourceBundle.getBundle方法在查找和加载某个资源包中的某个本地环境对应的资源文件时,它并不是只加载与该名称对应的Properties文件,而是首先加载与资源名对应的ResourceBundle的具体子类,当该名称的ResourceBundle的具体子类不存在时,getBundle方法才去加载与该资源名对应的Properties文件。如果getBundle方法找到与资源名对应的ResourceBundle的具体子类,则创建该类的一个实例对象并返回这个实例对象;否则,getBundle方法接着查找与这个资源名对应的Properties文件,以该Properties文件中的内容创建一个PropertyResourceBundle实例对象并返回这个实例对象。如果在整个资源包中没有找到任何一个合适的资源文件,ResourceBundle.getBundle方法将抛出异常。

自定义ResourceBundle的子类

ResourceBuudle类中定义了getKeys和handleGetObject两个抽象方法,getKeys用于返回包含该资源包中的所有资源项名称的集合,handleGetObject用于返回某个资源项名称所对应的值内容。自定义的ResourceBundle的具体子类必须覆盖handleGetObject和getKeys

这两个抽象方法,handleGetObject方法返回的类型为Object,也就是说,资源包中的资源项不仅限于字符串,而可以是任意的对象。所以,ResourceBundle类中除了定义getString方法来获得字符串类型的资源项外,还定义了获取其他类型的资源项的方法,包括:

.getStringArray:获取字符串数组类型的资源项的值

.getObject:获取任意对象类型的资源项的值

由于getObject方法的返回值为Object类型,所以,使用getObject方法获取资源项的值后,必须将得到的结果转换成资源项的实际类型。

===================MyResources.java===================

public class MyResources extends ResourceBundle {

private String[] keys = { "OkKey", "CancelKey" };

static {

System.out.println("缺省资源正在被装载!");

}

public Object handleGetObject(String key) {

if (key.equals("OkKey")) {

return "Ok";

} else if (key.equals("CancelKey")) {

return "Cancel";

}

return null;

}

public Enumeration getKeys() {

return Collections.enumeration(Arrays.asList(keys));

}

}

ResourceBundle.getBundle方法装载一个资源包时,一定会装载其中的默认资源,而不管是否使用。

===================MyResources_de.java===================

public class MyResources_de extends ResourceBundle {

private String[] keys = { "OkKey", "CancelKey" };

static {

System.out.println("德文资源正在被装载!");

}

public Object handleGetObject(String key) {

       if (key.equals("OkKey")) {

           return "Ok";

       } else if (key.equals("CancelKey")) {

return "Abschafung";

}

return null;

}

public Enumeration getKeys() {

return Collections.enumeration(Arrays.asList(keys));

}

}

===================测试===================

public static void main(String args[]) {

Locale locale = Locale.GERMANY;// 德国人

ResourceBundle myResources = ResourceBundle.getBundle("MyResources",

locale);

System.out.println("OkKey:" + myResources.getString("OkKey"));

System.out.println("CancelKey:" + myResources.getString("CancelKey"));

}

ResourceBundle.getString方法读取某个资源项的值时,如果当前的ResourceBundle对象没有该资源,getString方法将从默认资源中读取该资源项。由于上面的MyResources_de类与MyResources类中的资源项“OkKey”的值完全相同,所以,即使MyResources_de类中的handleGetObject方法不返回资源项“OkKey”的值,程序的运行结果也完全一样。

由于MyResources_de类与MyResources类的getKeys方法的代码完全相同,所以,如果让MyResources_de类继承MyResources,而不是直接继承ResourceBundle类,这样,MyResources_de类只需要覆盖handleGetObject方法即可,将共用的资源或默认的资源写在MyResources类中,而涉及到具体某国资源时,写在其子类中:

public class MyResources_de extends MyResources {

public Object handleGetObject(String key) {

if (key.equals("CancelKey")) {

return "Abschafung";

}

return null;

}

}

如果此时编写一个MyResources_de.properties 属性文件,并将上面的MyResources_de.class文件删除掉,则上面测试程序会从该属性文件中读取,否则还是从相应的.class读取:

=================== MyResources_de.properties==================

OkKey=Ok

CancelKey=Abschafung

总结:

l  ResourceBundle.getBundle方法加载资源文件时,某个特定本地环境所对应的 .class资源文件的优先级高于该本地环境所对应的 .properties 资源文件,如果两者同时存在,则getBundle方法加载.class文件中的内容。

l  ResourceBundle.getBundle方法在装载某个资源包时,总是装载资源包中的默认资源,而不是在特定本地环境所对应的资源文件中找不到某个资源项时才去装载默认资源;如果在特定本地环境所对应的资源文件中找不到指定的资源项,将在默认资源中查找该资源项的值。

l  如果资源包中所有资源信息的值都是字符串,则应用程序中可以使用properties文件管理资源信息,也可以使用自定义的 ResourceBundle 的具体子类来管理资源信息,前者更灵活方便。

ListResourceBundle

为了简化自定义的ResourceBundle子类的编写,JDK中定义了一个ListResouseBundle类,ListResourceBundle类是ResourceBundle类的一个抽象子类,它实现了ResourceBundle类的 getKeys 和 handleGetObject 这两个方法。ListResourceBundle 类实现的这两个方法内部调用了ListResourceBundle类中定义的一个getContents抽象方法,getContents方法返回一个包含所有资源项的名称和值的二维数组,ListResourceBundle类的子类只需要实现getContents方法,就自然实现了getKeys和handleGetObject这两个方法,这使得ListResourcBundle类的子类可以使用一个简单的包含所有资源项的名称和值的二维数组来管理资源信息:

public class fdas extends ListResourceBundle {

static final Object[][] contents = { { "OkKey", "Ok" },

{ "CancelKey", "Cancel" } };

public Object[][] getContents() {

return contents;

}

}

Web应用的国际化

实现Web应用的国际化有两种方式:

l  针对不同语言和地区的用户开发出不同的JSP网页版本,当用户请求资源时,根据请求消息中携带的本地信息为用户提供合适的版本。

l  将对本地环境敏感的资源数据(例如,错误提示信息、菜单或按妞上的标题文字)从网页中分离出来,放在.properties属性资源文件中。对于应用中的数值、货币和日期/时间等本地敏感的数据,可以通过占位符的方式设置它们的格式类型和格式模式。

如果整个Web应用或Web应用中有大量数据需要根据用户的本地信息进行显示,则可以采用第一种方式;如果Web应用中只有少量数据需要根据用户的本地信息进行显示,则采用第二种方式可以大大提高开发效率。事实上,这两种方式往往结合使用。

获取Web应用中的本地信息

要实现Web应用的国际化,首先要获得客户端浏览器的本地信息。大多数Web浏览器通常会在HTTP请求消息中通过Accept-Language消息头附带自己的本地信息,Web容器则可以根据HTTP请求消息中附带的Accept-Language头信息自动创建出标识客户端本地信息的Locale对象。在Servlet程序中,调用HttpServletRequest对象的getLocale方法和getLocales方法,就可以获得代表客户端本地信息的Locale对象。getLocale方法和getLocales方法都可以获得HTTP请求消息中的Accept-Language头字段中设置的本地信息,其中getLocale方法返回代表客户端的首选本地信息的Locale对象,getLocales方法返回一个包含客户端支持的所有本地信息的Locale对象的Enumeration集合对象,这些Locale对象按照客户端支持的所有本地信息的优先级在集合中依次排列。如果客户端的HTTP请求消息中没有提供Accept-Language头字段,则getLocale方法返回代表服务器端的默认本地信息的Locale对象,getLocales方法返回的Enumeration对象中也只包含这个代表服务器端的默认本地信息的Locale对象。

Web应用的国际化示例

=================== mypakg.applicationRes_en_US.properties ===================

i18n.title=The i18n of the Web

i18n.heading=Hello World! A i18n program

i18n.message=Today is {0, date, long}, time is {0, time, medium}. I bought {1} books, and spent {2, number, currency}.

=================== application_temp.properties ===================

i18n.title=Web应用的国际化

i18n.heading=你好!一个国际化应用程序

i18n.message=今天是{0, date, long},现在时间是{0, time, medium}。我今天买了{1}本书,共花了{2, number, currency}.

D:\eclipse_workspace\servlet_jsp\src>native2ascii -encoding gb2312 application_t emp.properties application_zh.properties

=================== mypakg.application_zh_CN.properties ===================

i18n.title=Web\u5e94\u7528\u7684\u56fd\u9645\u5316

i18n.heading=\u4f60\u597d\uff01\u4e00\u4e2a\u56fd\u9645\u5316\u5e94\u7528\u7a0b\u5e8f

i18n.message=\u4eca\u5929\u662f{0, date, long}\uff0c\u73b0\u5728\u65f6\u95f4\u662f{0, time, medium}\u3002\u6211\u4eca\u5929\u4e70\u4e86{1}\u672c\u4e66\uff0c\u5171\u82b1\u4e86{2, number, currency}.

=================== webi18n.jsp ===================

<%@ page contentType="text/html;charset=gb2312" %>

<%@ page import="java.util.ResourceBundle, java.text.MessageFormat" %>

<%@ page import="java.util.Date" %>

<%

ResourceBundle bundle = ResourceBundle.

getBundle("mypakg.applicationRes", request.getLocale());

%>

<html>

<head>

<title><%=bundle.getString("i18n.title")%></title>

</head>

<body>

<%=bundle.getString("i18n.heading")%><br /><br />

<%

String message = bundle.getString("i18n.message");

MessageFormat msgFmt = new MessageFormat(message, request.getLocale());

Date dateTime = new Date();

Object[] msgArgs = {dateTime, new Integer(10), new Double(550.8)};

%>

<%=msgFmt.format(msgArgs) %>

</body>

</html>