﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Java Stop Here-文章分类-FrameWork</title><link>http://www.blogjava.net/faithwind/category/7322.html</link><description>Love Java ,because you are my first lady !^_^</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 21:46:34 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 21:46:34 GMT</pubDate><ttl>60</ttl><item><title>创建国际化的 JSP 应用程序</title><link>http://www.blogjava.net/faithwind/articles/30445.html</link><dc:creator>黑咖啡</dc:creator><author>黑咖啡</author><pubDate>Mon, 13 Feb 2006 06:10:00 GMT</pubDate><guid>http://www.blogjava.net/faithwind/articles/30445.html</guid><wfw:comment>http://www.blogjava.net/faithwind/comments/30445.html</wfw:comment><comments>http://www.blogjava.net/faithwind/articles/30445.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/faithwind/comments/commentRss/30445.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faithwind/services/trackbacks/30445.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width="100%">
<P id=subtitle>了解服务器端应用程序所面临的独特挑战</P><IMG class=display-img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=1></TD>
<TD class=no-print width=192></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD width="100%">
<TABLE class=no-print cellSpacing=0 cellPadding=0 width=160 align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small></TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8> 
<FORM name=email action=https://www-130.ibm.com/developerworks/secure/email-it.jsp>
<SCRIPT language=JavaScript type=text/javascript>
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
</SCRIPT>
&nbsp;
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD width=16><IMG height=16 alt=将此页作为电子邮件发送 src="http://www.ibm.com/i/v14/icons/em.gif" width=16 vspace=3></TD>
<TD width=122>
<P><A class=smallplainlink href="javascript:document.email.submit();"><B><FONT color=#5c81a7 size=2>将此页作为电子邮件发送</FONT></B></A></P></TD></TR><NOSCRIPT>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="//www.ibm.com/i/c.gif" width=8></TD>
<TD width=16><IMG height=16 alt="" src="//www.ibm.com/i/c.gif" width=16></TD>
<TD class=small width=122>
<P><SPAN class=ast>未显示需要 JavaScript 的文档选项</SPAN></P></TD></TR></NOSCRIPT></FORM>
<TR vAlign=top>
<TD width=8><FONT color=#5c81a7 size=2><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></FONT></TD>
<TD width=16><FONT color=#5c81a7 size=2><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/dn.gif" width=16 vspace=3 border=0></FONT></TD>
<TD width=122>
<P><A class=smallplainlink href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#download"><B><FONT color=#996699 size=2>样例代码</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width=150 border=0>
<TBODY>
<TR>
<TD class=v14-header-1-small>对此页的评价</TD></TR></TBODY></TABLE>
<TABLE class=v14-gray-table-border cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD class=no-padding width=150>
<TABLE cellSpacing=0 cellPadding=0 width=143 border=0>
<TBODY>
<TR vAlign=top>
<TD width=8><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=8></TD>
<TD><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" width=16 vspace=3 border=0></TD>
<TD width=125>
<P><A class=smallplainlink href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#rate"><B><FONT color=#996699 size=2>帮助我们改进这些内容</FONT></B></A></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR></TD></TR></TBODY></TABLE>
<P>级别: 初级<BR></P>
<P>2005 年 3 月 29 日</P>
<BLOCKQUOTE>为国际客户设计 Java Server Pages（JSP）应用程序更像是一门艺术，而不像是科学，但所涉及的内容不仅仅能满足眼球。成功的关键是理解与国际化有关的独一无二的服务器端问题。Java 开发人员 Sing Li 将阐述这个重要问题，并给出两个经过考验确实有效的解决方案。 </BLOCKQUOTE>
<P>世界经济日益全球化推动了人们对基于 Web 的软件的需求，因为许多国家的用户都能访问 Web 软件。这些用户使用的语言、显示、数据录入、表示和文化需求等都可能存在很大的差异。国际化（缩写为 <I>i18n</I>）是一种创建为分散用户群工作的应用程序的艺术。</P>
<P>也许有点让人感到惊讶，当不作任何定制修改就在服务器端使用时，J2SE 对国际化的内建支持会表现出一些不足。一般来说，服务器端的国际化仍然是一门艺术，而不是一项科学，它常常涉及一些专用的或用户自主开发的解决方案。</P>
<P>本文把服务器端基于 JSP 的应用程序的国际化需求与 J2SE 应用程序的国际化需求区别开来。本文将介绍导致服务器端需求明显不同的各种客户机/服务器技术。然后，还将查看实际代码，这些代码展示了用来解决基本问题的、广泛采用的两个解决方案。 </P>
<P><A name=N10078><SPAN class=atitle><FONT face=Arial size=4>超越 J2SE 地区</FONT></SPAN></A></P>
<P>J2SE 用<I>地区（locale）</I> 的概念（请参阅<A href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#notation"><FONT color=#996699>Notation for a locale</FONT></A>）进行国际化。在一台机器上，地区代表用户选择的显示语言（例如，英语或西班牙语），以及日期、时间、货币等方面的格式化约定。通常，由底层操作系统管理地区选项设置，并在运行的时候把它传递给 J2SE。 </P>
<P>如果运行的服务器在本地机器上，或者是在局域网上，那么特定于机器的地区的概念就很好用。包含的所有客户机和服务器都在同一地区，所以它们都使用相同的显示语言、日期约定等。这些场景都不会带来麻烦的国际化问题。但是如果想用同一台服务器为多个国际位置上的用户提供服务的话，那么情况就变得很复杂。 </P><BR>
<P><A name=N1008C><SPAN class=atitle><FONT face=Arial size=4>服务器端国际化问题</FONT></SPAN></A></P>
<P>当为了进行国际化访问而部署服务器应用程序时，该应用程序必须同时为不同的地区提供支持。图 1 显示了一种可能的场景。每台机器 —— 服务器以及可在任何时间访问服务器的客户机 —— 都可能有自己的地区设置，而这些地区是不同的。 </P>
<TABLE cellSpacing=0 cellPadding=0 width="40%" align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD bgColor=#eeeeee><A name=notation><B>地区的概念</B></A><BR>
<P>地区是以语言代码后跟着的国家代码的形式指定的。（请参阅<A href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#resources"><FONT color=#996699>参考资料</FONT></A>，以获得这些代码的更多信息。）标准格式是 xx_YY，其中 xx 两个小写字母表示的语言代码，YY 是两个大写字母表示的国家代码。也有可能看见在语言和国家代码之间使用连字符，或者把国家代码写成小写形式的变体。把语言从国家分出来是必需的，因为可能不止一个国家要使用相同的语言，但是日期、时间、货币格式等可能有不同的表达习惯。 </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><A name=N100A8><B>图 1. 为不同地区的用户提供服务的服务器</B></A><BR><IMG height=457 alt=为不同地区的用户提供服务的服务器 src="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/figure1.gif" width=425> <BR>
<P>在图 1 中，服务器机器位于 San Francisco，特定于机器的地区是 en_US（美国英语）。来自纽约和达拉斯的用户都使用 en_US 地区，所以没有额外的国际化需求。但是，来自汉城的用户期望看到用韩语表示的应用程序提示，他们的地区是 ko_KR。同时，来自上海的用户想看到中文表示的应用程序文本，他们的地区是 zh_CN。来自东京的用户期望用日语显示应用程序，他们的地区是 ja_JP。所有这些用户的需求都必须通过运行在 San Francisco 服务器上的 JSP 应用程序来得到满足。 </P>
<P>在服务器端，您对于服务器机器自己的地区拥有全部控制权，但您无法改变客户机的地区或强行将它转换成某个特定地区。相反，应用程序必须识别出用户的地区，并保证 JSP 页面以正确的本地化形式出现（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#detect"><FONT color=#996699>Detecting client locale</FONT></A>）。 </P>
<TABLE cellSpacing=0 cellPadding=0 width="40%" align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD bgColor=#eeeeee><A name=#detect><B>检测客户机地区</B></A><BR>
<P>通过检查从用户浏览器传入的 HTTP 头，可以自动检测用户的地区。但是，用户或操作系统可能没有正确地设置这条信息。再加上这样的事实，即一些浏览器处理地区的信息存在 bug，有了这些，您就不难理解，为什么让应用程序显式询问用户首选地区是一个更能接受的确定地区值的方法。 </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P><A name=N100CD><SPAN class=smalltitle><STRONG><FONT face=Arial>更多地区判断的复杂情况</FONT></STRONG></SPAN></A></P>
<P>就在您以为可以安全地判断客户机地区，并据此呈现 JSP 时，新的问题出现了。请考虑以下这些非常现实的场景。 </P>
<P>来自东京的一位访问人员工正在使用位于中国上海的销售办公室的机器，机器的地区是 zh_CN。因为不熟悉书面中文，所以她想用日语访问 Web 应用程序。关于这种情况的说明，参见图 2（a）。 </P><BR><A name=figure2><B>图 2. 用户期望的地区与客户端机器上的地区不同</B></A><BR><IMG height=389 alt=用户期望的地区与客户端机器上的地区不同 src="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/figure2.gif" width=421> <BR>
<P>在图 2（a），San Francisco 服务器的地区是 en_US。客户端机器在上海，地区是 zh_CN。但是 JSP 应用程序需要用 ja_JP 地区显示，对用户才有用。 </P>
<P>再看另外一个更加怪异但并非不可能的场景。国际化 JSP 应用程序的开发人员正在一项调试。在一个地区为 en_US 的客户端系统上，打开了三个运行 JSP 应用程序的浏览器实例。服务器机器在局域网上，地区也是 en_US。但是，现在要为中文和日语用户测试应用程序的处理能力。所以在一台 en_US 客户端机器上，一个浏览器实例用的是英文（en_US），一个用的是日语（ja_JP），还有一个用的是中文（zh_CN）。<A href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#figure2"><FONT color=#996699>图 2</FONT></A>（b）演示了这种情况。 </P>
<P>目前为止，基本的国际化问题应当很清楚了：在处理显示国际化应用程序的任意特殊实例所需的实际地区时，客户机地区和服务器地区都有些力不从心。只有用户才能说清要用哪个地区显示页面。 </P>
<P>但是谢天谢地，通常可以肯定地假设在使用应用程序期间，用户不会改变显示语言。所以通常可以把地区与会话关联起来。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=N100FB><SPAN class=atitle><FONT face=Arial size=4>解决特定于地区的语言的显示问题 </FONT></SPAN></A></P>
<P>对于 JSP 应用程序，至少有两种处理不同语言的显示问题的普遍接受方法可以使用： </P>
<UL>
<LI>存储多组 JSP，每组 JSP 都用不同的语言编码，然后根据用户的地区选择在这些 JSP 之间切换。 
<LI>分离所有使用的字符串，代之以从资源绑定获得一个特定于地区的字符串。（这种方式使用了 J2SE 特定于地区的资源绑定处理方式。） </LI></UL>
<P>本文提供了示例代码的两个版本，分别对应上面的两种方法。示例应用程序是一个叫做 developerWorks Email 的虚构的电子邮件服务的登录屏幕。首先，会用语言选择屏幕提示电子邮件系统的用户，让他确定所需的当前会话地区。可以进行的选择包括英语、韩语、日语和中文，如图 3 所示。如果想试试代码的效果，请用 http://<I>&lt;server address&gt;</I>/dwi18n/multdir/index.jsp 这个 URL 访问该页面。 </P>
<TABLE cellSpacing=0 cellPadding=0 width="40%" align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD bgColor=#eeeeee><A name=images><B>用图片进行初始的语言选择</B></A><BR>
<P>通常没有可靠的方法可以预先知道什么样的字体支持引入的客户机。为了节约存储和内存，默认情况下，大多数现代操作系统不会预先安装对所有 Unicode 字符的所有字体的支持。有些浏览器会在第一次访问包含必要字体的 Web 页面时，尝试下载和安装这些字体。用图片来显示外国字符或者国旗是表示语言选择屏幕的一种可行方式。 </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><A name=N10122><B>图 3. 进行显式地区选择的语言选择屏幕</B></A><BR><IMG height=281 alt=四种不同的地区在同一台机器上 src="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/figure3.gif" width=490> <BR>
<P>在图 3 中，语言选择屏幕用 4 个图片表示 4 个地区选择（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#images"><FONT color=#996699>Using images for initial language selection</FONT></A>）。登录屏幕使用用户在这里选择的语言进行显示。图 4 显示了日语的登录屏幕。 </P><BR><A name=figure4><B>图 4. 日语登录屏幕</B></A><BR><IMG height=276 alt=日语登录屏幕 src="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/figure4.gif" width=427> <BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=redundant><SPAN class=atitle><FONT face=Arial size=4>使用多组冗余的特定于语言的 JSP 集</FONT></SPAN></A></P>
<P>示例的第一个版本位于示例代码发行包的 webapps/dwi18n/multdir/ 目录中，它运用了多组 JSP 页面。图 5 显示了这个应用程序的目录结构。 </P><BR><A name=N10154><B>图 5. dwi18n/multdir 的目录结构显示了特定于地区的目录</B></A><BR><IMG height=138 alt="dwi18n/multdir 的目录结构显示了特定于地区的目录" src="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/figure5.gif" width=237> <BR>
<P>在图 5 中，每个地区都有对应的子目录。与 en_EN 地区对应的用英文编码的 JSP 在 en 子目录中。与 ko_KR 地区对应的用韩语编码的 JSP 在 ko 子目录中。对于 ja_JP 地区，用日语编码的 JSP 在 ja 子目录中。zh_CN 地区则用 zh 子目录中用中文编码的 JSP 表示。每个子目录都既包含登录屏幕的 JSP（login.jsp），也包含数据确认 JSP（confirm.jsp）。 </P>
<P>数据确认 JSP 只显示在这个简单的示例中输入的数据。例如，如果在中文登录屏幕中输入了信息，然后单击按钮，那么数据确认 JSP 就会显示输入的数据，如图 6 所示。 </P><BR><A name=figure6><B>图 6. 中文的确认页面</B></A><BR><IMG height=290 alt=中文的确认页面 src="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/figure6.gif" width=436> <BR>
<P><A name=N1017A><SPAN class=smalltitle><STRONG><FONT face=Arial>编写 JSP 代码</FONT></STRONG></SPAN></A></P>
<P>地区选择 JSP 叫做 index.jsp，它直接链接到特定于语言的一组 JSP。这个版本的 index.jsp 的代码在清单 1 中： </P><BR><A name=N10187><B>清单 1. 地区选择 JSP 直接链接到特定于语言的页面 </B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">				
&lt;%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Select a language&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td colspan=4 bgcolor="black"&gt;
&lt;br/&gt;
&lt;center&gt;&lt;font face="arial" size=+2 color="white"&gt;
    &lt;b&gt;&lt;i&gt;developer&lt;/i&gt;Works Email&lt;/b&gt;&lt;/font&gt;
&lt;/center&gt;
&lt;br/&gt;

&lt;/td&gt;
&lt;/tr&gt;
 &lt;tr&gt;&lt;td&gt;
 &lt;c:url value="en/login.jsp" var="englishURL"/&gt;
 &lt;a href="${englishURL}"&gt;
    &lt;img src="english.gif"/&gt;
 &lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;
 &lt;c:url value="ja/login.jsp" var="japaneseURL"/&gt;
  &lt;a href="${japaneseURL}"&gt;
       &lt;img src="japanese.gif"/&gt;
  &lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;
  &lt;c:url value="ko/login.jsp" var="koreanURL"/&gt;
  &lt;a href="${koreanURL}"&gt;
       &lt;img src="korean.gif"/&gt;
  &lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;
  &lt;c:url value="zh/login.jsp" var="chineseURL"/&gt;
  &lt;a href="${chineseURL}"&gt;
       &lt;img src="chinese.gif"/&gt;
  &lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;

</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>注意，清单 1 中使用了来自 JSP 标准标签库（JSTL）的 <CODE>&lt;c:url&gt;</CODE> 标签来创建链接 URL。这可以确保会话管理得到恰当的处理。（请参阅<A href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#resources"><FONT color=#996699>参考资料</FONT></A>，以了解关于 JSTL 和 <CODE>&lt;c:url&gt;</CODE> 标签的更多信息。） </P>
<P>每组 login.jsp 和 confirm.jsp 都用特定于地区的语言编写代码。清单 2 显示了 ja_JP 地区的 login.jsp（与 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#figure4"><FONT color=#996699>图 4 </FONT></A>对应）： </P><BR><A name=N101A8><B>清单 2. ja_JP 地区的登录页面（login.jsp）</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">				
&lt;%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html;charset=utf-8" &gt;
&lt;title&gt;developerWorks 電子メール&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;c:url value="confirm.jsp" var="actionURL"/&gt;
&lt;form action="${actionURL}" method="post"&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td colspan=2 bgcolor="black"&gt;
&lt;br/&gt;
&lt;center&gt;&lt;font face="arial" size=+2 color="white"&gt;
    &lt;b&gt;&lt;i&gt;developer&lt;/i&gt;Works 電子メール&lt;/b&gt;&lt;/font&gt;
&lt;/center&gt;
&lt;br/&gt;

&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;ユーザ ID&lt;/td&gt;
&lt;td&gt;&lt;input type="text" name="userid" size="40"/&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;パスワード&lt;/td&gt;
&lt;td&gt;&lt;input type="password" name="pass" size="40"/&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td colspan="2" align="center"&gt;
&lt;input type="submit" value="ログイン"/&gt;&lt;/td&gt;
&lt;/tr&gt;


&lt;/table&gt;

&lt;/form&gt;

&lt;/body&gt;
&lt;/html&gt;

</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>与此类似，对于 zh_CN 地区，confirm.jsp（与 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#figure6"><FONT color=#996699>图 6</FONT></A> 对应）是用中文编码的，如清单 3 所示： </P><BR><A name=N101BA><B>清单 3. zh_CN 地区的数据确认页面（confirm.jsp）</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">				
&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html;charset=utf-8" &gt;
&lt;title&gt;developerWorks 电邮&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;table border="1"&gt;
&lt;tr&gt;
&lt;td colspan=2 bgcolor="black"&gt;
&lt;br/&gt;
&lt;center&gt;&lt;font face="arial" size=+2 color="white"&gt;
     &lt;b&gt;&lt;i&gt;developer&lt;/i&gt;Works 电邮&lt;/b&gt;&lt;/font&gt;
&lt;/center&gt;
&lt;br/&gt;

&lt;/td&gt;
&lt;/tr&gt;


&lt;tr&gt;
&lt;td&gt;用户帐号&lt;/td&gt;
&lt;td&gt;${param.userid}&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;密码&lt;/td&gt;
&lt;td&gt;${param.pass}&lt;/td&gt;
&lt;/tr&gt;




&lt;/table&gt;


&lt;/body&gt;
&lt;/html&gt;


</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>前面介绍的多冗余集（multiple-redundant-set）方法对于以下这类应用程序是一种可行的解决方案： </P>
<UL>
<LI>主要用一种语言访问，偶尔从其他地区访问。 
<LI>底层表示层的 JSP 变化不是很频繁。 </LI></UL><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=N101CD><SPAN class=atitle><FONT face=Arial size=4>使用 J2SE 资源绑定 </FONT></SPAN></A></P>
<P>前面一节 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#redundant"><FONT color=#996699>Using multiple redundant sets of language-specific JSPs</FONT></A> 中的解决方案的最大不足在于：当需要更新特定于语言的 JSP 集时，所有冗余编码的 JSP 集都必须同时更新。对于一个中等规模的项目而言，这会造成冗长的、容易出错的更新。 </P>
<P>而现在要介绍的解决方案的外观和作用都与前一个类似，但是在这个案例中，只有一套 login.jsp 和 confirm.jsp。这个解决方案利用了 J2SE 在资源绑定中对地区的支持，只在需要的时候才采用特定于地区的文本字符串（请参阅<A href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#resources"><FONT color=#996699>参考资料</FONT></A>，以了解更多关于 J2SE 资源绑定的信息）。这个解决方案的示例代码位于 webapps\dwi18n\javares 目录中。如果部署了示例代码，那么请使用 http://<I>&lt;server address&gt;</I>/dwi18n/javares/index.jsp 这个 URL。</P>
<P>图 7 显示了运行在同一台客户端机器上的 4 个浏览器会话，每个会话请求的都是不同的地区。 </P><BR><A name=N101EA><B>图 7. 同一台机器上的 4 个不同地区</B></A><BR><IMG height=364 alt="同一台机器上的 4 个不同地区" src="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/figure7.gif" width=525> <BR>
<P>在图 7 中，可以清楚地看到国际化的 JSP 应用程序可以同时处理多个地区。 </P>
<P><A name=N101FC><SPAN class=smalltitle><STRONG><FONT face=Arial>编写 JSP 代码</FONT></STRONG></SPAN></A></P>
<P>在这个案例中，index.jsp 略有不同，因为它现在链接到单独的 login.jsp。清单 4 显示了这个版本的 index.jsp 的代码： </P><BR><A name=N10209><B>清单 4. 链接到单独 login.jsp 的地区选择页面</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">				
&lt;%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Select Language&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td colspan=4 bgcolor="black"&gt;
&lt;br/&gt;
&lt;center&gt;&lt;font face="arial" size=+2 color="white"&gt;
    &lt;b&gt;&lt;i&gt;developer&lt;/i&gt;Works Email&lt;/b&gt;&lt;/font&gt;
    &lt;/center&gt;
&lt;br/&gt;

&lt;/td&gt;
&lt;/tr&gt;
 &lt;tr&gt;&lt;td&gt;

 &lt;c:url value="login.jsp" var="englishURL"&gt;
   &lt;c:param name="locale" value="en_US"/&gt;
 &lt;/c:url&gt;

 &lt;a href="${englishURL}"&gt;
    &lt;img src="english.gif"/&gt;
 &lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;
  &lt;c:url value="login.jsp" var="japaneseURL"&gt;
     &lt;c:param name="locale" value="ja_JP"/&gt;
  &lt;/c:url&gt;

  &lt;a href="${japaneseURL}"&gt;
       &lt;img src="japanese.gif"/&gt;
  &lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;
  &lt;c:url value="login.jsp" var="koreanURL"&gt;
     &lt;c:param name="locale" value="ko_KR"/&gt;
  &lt;/c:url&gt;

  &lt;a href="${koreanURL}"&gt;
       &lt;img src="korean.gif"/&gt;
  &lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;
  &lt;c:url value="login.jsp" var="chineseURL"&gt;
     &lt;c:param name="locale" value="zh_CN"/&gt;
  &lt;/c:url&gt;

  &lt;a href="${chineseURL}"&gt;
       &lt;img src="chinese.gif"/&gt;
  &lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>注意，在清单 4 中，使用 JSTL 的 <CODE>&lt;c:param&gt;</CODE> 标签设置了一个叫做 <CODE>locale</CODE> 的 URL 请求参数。当用户单击语言选择时，这个参数被传递给 login.jsp。清单 5 显示了 login.jsp 的代码： </P><BR><A name=N1021F><B>清单 5. 使用 J2SE 资源绑定的登录页面（login.jsp） </B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">				

&lt;%@ page pageEncoding="UTF-8" %&gt;
&lt;%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %&gt;
&lt;%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %&gt;
&lt;html&gt;
&lt;c:set var="loc" value="en_US"/&gt;
&lt;c:if test="${!(empty param.locale)}"&gt;
  &lt;c:set var="loc" value="${param.locale}"/&gt;
&lt;/c:if&gt;
&lt;fmt:setLocale value="${loc}" /&gt;


&lt;fmt:bundle basename="app"&gt;
&lt;head&gt;
&lt;title&gt;developerWorks &lt;fmt:message key="email"/&gt;&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;c:url value="confirm.jsp" var="formActionURL" /&gt;

&lt;form action="${formActionURL}" method="post"&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td colspan=2 bgcolor="black"&gt;
&lt;br/&gt;
&lt;center&gt;&lt;font face="arial" size=+2 color="white"&gt;&lt;b&gt;
       &lt;i&gt;developer&lt;/i&gt;Works &lt;fmt:message key="email"/&gt;
         &lt;/b&gt;&lt;/font&gt;&lt;/center&gt;
&lt;br/&gt;

&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;fmt:message key="userid"/&gt;&lt;/td&gt;
&lt;td&gt;

&lt;input type="hidden" name="locale" value="${loc}"/&gt;
&lt;input type="text" name="userid" size="40"/&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&lt;fmt:message key="password"/&gt;&lt;/td&gt;
&lt;td&gt;&lt;input type="text" name="pass" size="40"/&gt;&lt;/td&gt;
&lt;/tr&gt;


&lt;tr&gt;
&lt;td colspan="2" align="center"&gt;
&lt;input type="submit" value="&lt;fmt:message key='login'/&gt;"/&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;

&lt;/form&gt;

&lt;/body&gt;
&lt;/fmt:bundle&gt;

&lt;/html&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>清单 5 使用了 JSTL 国际化辅助标签库（请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#resources"><FONT color=#996699>参考资料</FONT></A>）。如果传入的 <CODE>param.locale</CODE> 为空，那么在默认情况下，地区设置为 en_US。在使用资源绑定的时候，可以用 <CODE>&lt;fmt:setLocale&gt;</CODE> 标签设置地区。 </P>
<P>完成地区设置之后，<CODE>&lt;fmt:message&gt;</CODE> 就会从处于绑定状态的属性文件中提取文本，该文本对应于指定的关键字。用 <CODE>&lt;fmt:bundle&gt;</CODE> JSTL 标签把绑定的基本名称设为 <CODE>app</CODE>。如果查看 dwi18n/WEB-INF/classes 目录，可以看到资源绑定中的所有文件都在那里。表 1 描述了这些文件。请参阅<A href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#resources"><FONT color=#996699>参考资料</FONT></A>，以获得使用 J2SE 资源绑定的更多信息。</P>
<P><A name=N10248><SPAN class=smalltitle><STRONG><FONT face=Arial>表 1. 资源绑定中的文件 </FONT></STRONG></SPAN></A></P>
<P>
<TABLE cellSpacing=1 cellPadding=3 width="60%" border=0>
<TBODY>
<TR vAlign=top>
<TD><STRONG>文件名</STRONG> </TD>
<TD><B>说明</B> </TD></TR>
<TR vAlign=top>
<TD>app.properties</TD>
<TD>默认使用的属性文件。对应 en_US 地区。</TD></TR>
<TR vAlign=top>
<TD>app_zh.properties</TD>
<TD>zh_CN 地区的属性文件。包含用中文编码的字符串。</TD></TR>
<TR vAlign=top>
<TD>app_ko.properties</TD>
<TD>ko_KR 地区的属性文件。包含用韩语编码的字符串。</TD></TR>
<TR vAlign=top>
<TD>app_ja.properties</TD>
<TD>ja_JP 地区的属性文件。包含用日语编码的字符串。</TD></TR>
<TR vAlign=top>
<TD>*.ucd</TD>
<TD>创建属性文件的 Unicode 源文件。</TD></TR>
<TR vAlign=top>
<TD>convacii.bat</TD>
<TD>将 ucd 文件转换成属性文件的批处理文件。</TD></TR></TBODY></TABLE></P>
<P>作为示例，清单 6 显示了<CODE>app_ko.properties</CODE> 文件的内容： </P><BR><A name=N102AF><B>清单 6. 资源绑定中的 app_ko.properties 文件</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">				
email=\uc774\uba54\uc77c
userid=\uc544\uc774\ub514
password=\ube44\ubc00\ubc88\ud638
login=\ub85c\uadf8\uc778
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>注意，在清单 6 中，所有的 Unicode 字符均被转义。必须这么做，因为 Java 的资源绑定机制只接受用 ASCII 编码的属性文件。要创建这个文件，既可以使用 IDE 中的字符串资源编辑器，也可以使用 Unicode 编辑器创建一个 Unicode 文件，然后用 JDK 的 <CODE>nativetoascii</CODE> 工具转换它。在该例中，<CODE>convascii.bat</CODE> 文件负责进行转换。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=N102C1><SPAN class=atitle><FONT face=Arial size=4>结束语</FONT></SPAN></A></P>
<P>在设置国际化 JSP 应用程序时，需要了解它的独特需求。应用程序必须做好准备，支持具有不同地区需求的用户的多个并发访问。本文介绍了用特定于地区的语言文本显示国际化应用程序这个问题的两个解决方案。但我也仅仅是触及到创建国际化服务器端应用程序的迷人艺术的表面。其他重要的问题包括处理不同的日期和货币格式、管理 GUI 布局和使用专门的输入法编辑器（IME，输入外国字符的实用软件）。请参阅<A href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#resources"><FONT color=#996699>参考资料</FONT></A>，以获得有助于进一步了解国际化的信息。 </P><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><SPAN class=atitle><A name=download><FONT face=Arial size=4>下载</FONT></A></SPAN></P>
<TABLE class=data-table-1 cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TH>描述</TH>
<TH>文件名称</TH>
<TH style="TEXT-ALIGN: right">文件大小</TH>
<TH>&nbsp;下载方法</TH></TR>
<TR>
<TD class=tb-row>Sample code for article tested on Tomcat 5.5.7</TD>
<TD noWrap>code.zip</TD>
<TD style="TEXT-ALIGN: right" noWrap>25 KB</TD>
<TD noWrap>&nbsp;<A class=fbox href="ftp://www6.software.ibm.com/software/developer/library/j-jspapp/code.zip"><B><FONT color=#5c81a7>FTP</FONT></B></A></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top>
<TD colSpan=5><FONT color=#5c81a7><IMG height=12 alt="" src="http://www.ibm.com/i/c.gif" width=12 border=0></FONT></TD></TR>
<TR>
<TD><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/fw.gif" width=16></FONT></TD>
<TD><A class=fbox href="http://www-128.ibm.com/developerworks/cn/whichmethod.html"><FONT color=#5c81a7>关于下载方法的信息</FONT></A></TD>
<TD><FONT color=#5c81a7><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=50></FONT></TD>
<TD><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/sout.gif" width=16></FONT></TD>
<TD><A class=fbox href="http://www.adobe.com/products/acrobat/readstep2.html"><FONT color=#5c81a7>Get Adobe® Reader®</FONT></A></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><FONT color=#5c81a7><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></FONT></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><FONT color=#5c81a7><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></FONT></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=resources><SPAN class=atitle><FONT face=Arial size=4>参考资料 </FONT></SPAN></A></P>
<UL>
<LI>您可以参阅本文在 developerWorks 全球站点上的 <A href="http://www-128.ibm.com/developerworks/java/library/j-jspapp/" target=_blank><FONT color=#5c81a7>英文原文</FONT></A>。<BR><BR>
<LI>单击本文顶部或底部的 <B>Code</B>（或请参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#download"><FONT color=#996699>Downloads</FONT></A>）下载本文讨论的代码 code.zip。<BR><BR>
<LI>地区中使用的两个字符的语言代码是 ISO-639 代码。<A href="http://ftp.ics.uci.edu/pub/ietf/http/related/iso639.txt"><FONT color=#5c81a7>Consult the list</FONT></A>. <BR><BR>
<LI>地区中使用的两个字符的国家代码是 ISO-3166 代码。<A href="http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html"><FONT color=#5c81a7>Consult the list</FONT></A>. <BR><BR>
<LI>要了解所有的 JSTL 标签，请参阅 developerWorks 上的 <A href="http://www-128.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=topic&amp;expand=JSTL+%E5%85%A5%E9%97%A8"><FONT color=#5c81a7><I>A JSTL 入门</I> </FONT></A>系列。其中 <A href="http://www-128.ibm.com/developerworks/cn/java/j-jstl0415/index.html"><FONT color=#5c81a7><I>表示就是一切</I> </FONT></A>与这些标签紧密相关，它介绍了一些国际化标签。<BR><BR>
<LI>通过阅读 <CODE>java.util.ResourceBundle</CODE> 类的 Javadoc，发现更多关于 J2SE 资源绑定、如何创建它们和在哪儿放置它们的消息。也可以阅读 javadoc 的在线文章 <A href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/ResourceBundle.html"><FONT color=#5c81a7>here</FONT></A>。<BR><BR>
<LI>教程 <A href="http://www.ibm.com/developerworks/edu/j-dw-javai18n-i.html?S_TACT=105AGX02"><FONT color=#5c81a7>Java internationalization basics</FONT></A>（developerWorks，2002 年 4 月）是学习所有可用的 J2SE 国际化支持机制的一个好地方，允许您创造性地在服务器端应用程序中应用它们。 <BR><BR>
<LI><A href="http://www.alphaworks.ibm.com/tech/uniime"><FONT color=#5c81a7>Unicode Input Method Editor</FONT></A> 是一个可以协助尽早在开发周期中发现全球化问题的工具，它能提供了一个简单的机制，以便很容易地重现全球化问题。<BR><BR>
<LI><A href="http://www.ibm.com/software/globalization/icu/index.jsp"><FONT color=#5c81a7>International Components for Unicode</FONT></A> 是 Unicode 支持、软件国际化、全球化的一个成熟的、广泛应用的库。ICU 是一个由 IBM 赞助、支持和使用的开源开发项目。<BR><BR>
<LI>参阅 Sing Li 撰写的书籍 <A href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=076457485X"><FONT color=#5c81a7>Beginning JavaServer Pages</FONT></A>（John Wiley &amp; Sons，2005），以便进一步研究国际化和本地化。 <BR><BR>
<LI>通过参与 <A href="http://www.ibm.com/developerworks/blogs/"><FONT color=#5c81a7>developerWorks blogs</FONT></A> 加入 developerWorks 社区。<BR><BR>
<LI>可以在 developerWorks 的 <A href="http://www.ibm.com/developerworks/cn/java/"><FONT color=#5c81a7>Java 技术专区 </FONT></A>发现关于 Java 编程的各个方面的文章。<BR><BR><BR><BR>
<LI>请参阅 <A href="http://devworks.krcinfo.com/"><FONT color=#5c81a7>Developer Bookstore</FONT></A>，以获得技术书籍的完整清单，其中包括数百本<A href="http://devworks.krcinfo.com/WebForms/ProductList.aspx?Search=Category&amp;id=1200&amp;p=Java"><FONT color=#5c81a7>Java 相关主题</FONT></A>的书籍。<BR></LI></UL><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-jspapp/#main"><B><FONT color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=author><SPAN class=atitle><FONT face=Arial size=4>关于作者</FONT></SPAN></A></P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD colSpan=2><FONT face=Arial size=4><IMG height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></FONT></TD></TR>
<TR vAlign=top align=left>
<TD>
<P><FONT face=Arial size=4><IMG height=80 alt="Author photo" src="http://www-106.ibm.com/developerworks/i/p-sing.jpg" width=64 align=left></FONT></P></TD>
<TD>
<P>Sing Li 是一位顾问和自由作者。他的著作包括 <I><A href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=076457485X"><FONT color=#5c81a7>Beginning JavaServer Pages</FONT></A> </I>、<I> <A href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=0764559028"><FONT color=#5c81a7>Professional Apache Tomcat 5</FONT></A> </I>、<I>Pro JSP - Third Edition</I>、<I>Early Adopter JXTA</I>、<I>Professional Jini</I>，以及其他大量书籍。他还是技术杂志的定期投稿人，也是 P2P 革命的积极倡导者。您可以通过 <A href="mailto:westmakaha@yahoo.com"><FONT color=#5c81a7>westmakaha@yahoo.com</FONT></A> 与他联系。</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/faithwind/aggbug/30445.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faithwind/" target="_blank">黑咖啡</a> 2006-02-13 14:10 <a href="http://www.blogjava.net/faithwind/articles/30445.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用struts框架尝试国际化程序实现</title><link>http://www.blogjava.net/faithwind/articles/30441.html</link><dc:creator>黑咖啡</dc:creator><author>黑咖啡</author><pubDate>Mon, 13 Feb 2006 05:45:00 GMT</pubDate><guid>http://www.blogjava.net/faithwind/articles/30441.html</guid><wfw:comment>http://www.blogjava.net/faithwind/comments/30441.html</wfw:comment><comments>http://www.blogjava.net/faithwind/articles/30441.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/faithwind/comments/commentRss/30441.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faithwind/services/trackbacks/30441.html</trackback:ping><description><![CDATA[<TABLE style="WIDTH: 520px; WORD-BREAK: break-all; WORD-WRAP: break-word" cellSpacing=0 cellPadding=0 width="95%" align=center border=0>
<TBODY>
<TR>
<TD><STRONG>
<P style="FONT-SIZE: 12pt; COLOR: #ff0000" align=center>用struts框架尝试国际化程序实现&nbsp;</P></STRONG></TD></TR>
<TR>
<TD>
<P class=right_color align=center><BR>&nbsp;</P></TD></TR>
<TR>
<TD align=right><BR>&nbsp;</TD></TR>
<TR>
<TD style="WIDTH: 520px; WORD-BREAK: break-all; WORD-WRAP: break-word">
<DIV id=td_content>
<TABLE class=p14 cellSpacing=0 cellPadding=2 width="96%" border=0>
<TBODY>
<TR>
<TD class=p12 align=middle><B></B></TD></TR>
<TR>
<TD class=content align=middle></TD></TR>
<TR>
<TD class=content align=middle>
<P>&nbsp;</P></TD></TR></TBODY></TABLE>
<TABLE class=p14 cellSpacing=0 cellPadding=2 width="96%" border=0>
<TBODY>
<TR>
<TD><SPAN id=tech_article_ad1></SPAN><SPAN class=myp111><FONT id=zoom>struts是一个MVC框架，据说struts可以轻松实现国际化；于是根据网上的资料，做了一个尝试，因为第一次做多语言程序，还是拐了很多弯路；但所幸，经过不断的尝试，终于成功的实现多语言版本的简单页面； <BR><BR>因为程序非常简单，所以在整个尝试过程中，全部使用手工编码，没有使用任何辅助工具； <BR><BR><B>1、 建立服务器 </B><BR><BR>我使用Tomcat4作为测试环境，建立过程(略); <BR><BR><B>2、 下载struts </B><BR><BR>可以到http://jakarta.apache.org/struts/index.html下载，下载后解压，把其中的.war文件拷贝到Tomcat的webapps目录下，启动Tomcat，如果http://localhost:8080/struts-example/ 运行没有问题，说明环境建立成功；这些.war文件在Tomcat启动后会自动展开成文件，里面有源代码，可以作为源码研究； <BR><BR><B>3、 建立工程 </B><BR><BR>在webapps目录下建立一个international文件夹，再在international目录下建立WEB-INF文件夹和WEB-INF/classes文件夹，这些都是一个JSP工程必须的； <BR><BR><B>4、 加了struts的类</B> <BR><BR>在WEB-INF目录下建立一个lib子目录，把struts-example\WEB-INF\lib目录下将所有.jar文件拷贝到该目录下；这些文件是struts的控制类库和标签类库等； <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>commons-beanutils.jar 
commons-collections.jar 
commons-digester.jar 
commons-fileupload.jar 
commons-lang.jar 
commons-logging.jar 
commons-validator.jar 
jakarta-oro.jar 
struts.jar</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR><B>5、 加入struts标签定义文件 </B><BR><BR>从struts-example\WEB-INF目录下，把.TLD文件拷贝到international的WEB-INF目录下，这些文件标签库的定义文件； <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>struts-bean.tld 
struts-html.tld 
struts-logic.tld 
struts-nested.tld 
struts-template.tld 
struts-tiles.tld</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR><B>6、 建立struts的config文件 </B><BR><BR>建立struts的config文件的struts-config.xml,内容如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;?xml version="1.0" encoding="ISO-8859-1" ?&gt; 
&lt;!DOCTYPE struts-config PUBLIC 
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" 
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd";&gt; 
&lt;struts-config&gt; 
&lt;message-resources parameter="resources.application"/&gt; 
&lt;/struts-config&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>message-resources标签是指message资源的文件，就是我们存放我们的多种语言的提示信息的文件，resources.application表是classes目录下的resources目录用来存放资源文件，默认语言文件名为application.properties,中文为application_zh_CN.properties,其他语言类似； <BR><BR><B>7、 建立web.xml文件</B> <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt; 
&lt;!DOCTYPE web-app 
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" 
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd";&gt; 
&lt;web-app&gt; 
&lt;display-name&gt;test international&lt;/display-name&gt; 
&lt;servlet&gt; 
&lt;servlet-name&gt;action&lt;/servlet-name&gt; 
&lt;servlet-class&gt;org.apache.struts.action.ActionServlet&lt;/servlet-class&gt; 
&lt;init-param&gt; 
&lt;param-name&gt;config&lt;/param-name&gt; 
&lt;param-value&gt;/WEB-INF/struts-config.xml&lt;/param-value&gt; 
&lt;/init-param&gt; 
&lt;init-param&gt; 
&lt;param-name&gt;debug&lt;/param-name&gt; 
&lt;param-value&gt;2&lt;/param-value&gt; 
&lt;/init-param&gt; 
&lt;init-param&gt; 
&lt;param-name&gt;detail&lt;/param-name&gt; 
&lt;param-value&gt;2&lt;/param-value&gt; 
&lt;/init-param&gt; 
&lt;load-on-startup&gt;2&lt;/load-on-startup&gt; 
&lt;/servlet&gt; 
&lt;servlet-mapping&gt; 
&lt;servlet-name&gt;action&lt;/servlet-name&gt; 
&lt;url-pattern&gt;*.do&lt;/url-pattern&gt; 
&lt;/servlet-mapping&gt; 
&lt;welcome-file-list&gt; 
&lt;welcome-file&gt;index.jsp&lt;/welcome-file&gt; 
&lt;/welcome-file-list&gt; 
&lt;taglib&gt; 
&lt;taglib-uri&gt;/tags/struts-bean&lt;/taglib-uri&gt; 
&lt;taglib-location&gt;/WEB-INF/struts-bean.tld&lt;/taglib-location&gt; 
&lt;/taglib&gt; 
&lt;taglib&gt; 
&lt;taglib-uri&gt;/tags/struts-html&lt;/taglib-uri&gt; 
&lt;taglib-location&gt;/WEB-INF/struts-html.tld&lt;/taglib-location&gt; 
&lt;/taglib&gt; 
&lt;taglib&gt; 
&lt;taglib-uri&gt;/tags/struts-logic&lt;/taglib-uri&gt; 
&lt;taglib-location&gt;/WEB-INF/struts-logic.tld&lt;/taglib-location&gt; 
&lt;/taglib&gt; 
&lt;taglib&gt; 
&lt;taglib-uri&gt;/tags/struts-nested&lt;/taglib-uri&gt; 
&lt;taglib-location&gt;/WEB-INF/struts-nested.tld&lt;/taglib-location&gt; 
&lt;/taglib&gt; 
&lt;taglib&gt; 
&lt;taglib-uri&gt;/tags/struts-tiles&lt;/taglib-uri&gt; 
&lt;taglib-location&gt;/WEB-INF/struts-tiles.tld&lt;/taglib-location&gt; 
&lt;/taglib&gt; 
&lt;/web-app&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>上面的web.xml定义了struts的控制类、config文件和标签，因为比较简单，所以不做解释； <BR><BR><B>8、 建立资源文件 </B><BR><BR>在classes目录下，建立一个resources目录，用来存放资源文件； <BR><BR>先建立默认的资源文件application.properties和英文(美国)的资源文件application_en_US.properties，内容为： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE># -- international test -- 
test.title=international application test 
test.body=This is a international application test</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>先建立这两个文件，中文的等下一步建立 <BR><BR><B>9、建立jsp文件</B> <BR><BR>在international目录下，建立index.jsp文件，内容为： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html;charset=UTF-8" %&gt; 
&lt;%@ taglib uri="/tags/struts-bean" prefix="bean" %&gt; 
&lt;%@ taglib uri="/tags/struts-html" prefix="html" %&gt; 
&lt;%@ taglib uri="/tags/struts-logic" prefix="logic" %&gt; 
&lt;html:html locale="true"&gt; 
&lt;head&gt; 
&lt;title&gt;&lt;bean:message key="test.title"/&gt;&lt;/title&gt; 
&lt;html:base/&gt; 
&lt;/head&gt; 
&lt;body bgcolor="white"&gt; 
&lt;p&gt;&lt;bean:message key="test.body"/&gt;&lt;/p&gt; 
&lt;/body&gt; 
&lt;/html:html&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>在这里<HTML:HTML locale="true">表示使用浏览器默认的地区和语言；key<?XML:NAMESPACE PREFIX = BEAN /><BEAN:MESSAGE key="test.title"></BEAN:MESSAGE>的意思是取对应资源文件里的test.title项目的内容； <BR><BR>启动Tomcat,在浏览器里输入http://localhost:8080/international/,查看效果，如果浏览器标题显示international application test，页面里显示This is a international application test则说明你的程序成功了；下面只要增加资源文件，你就可以在多种语言的系统里看了； <BR><BR><B>10、 建立简体中文的资源文件 </B><BR><BR>在resources目录下建立一个application_cn.properties,输入内容： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE># -- international test -- 
test.title=国际化程序测试 
test.body=这是一个国际化程序测试例子</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>因为java的国际化是通过unicode码来实现，所以要把代码转为unicode码；在Dos下，转到resources目录，执行： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>native2ascii application_cn.properties application_zh_CN.properties</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>转换后的application_zh_CN.properties文件内容为： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE># -- international test -- 
test.title=\u56fd\u9645\u5316\u7a0b\u5e8f\u6d4b\u8bd5 
test.body=\u8fd9\u662f\u4e00\u4e2a\u56fd\u9645\u5316\u7a0b\u5e8f\u6d4b\u8bd5\u4f8b\u5b50</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>这就是上面的中文的unicode码； <BR><BR>重新启动Tomcat, 在浏览器里输入http://localhost:8080/international/,你看，标题和内容是不是变成中文了； <BR><BR><B>11、 建立繁体中文的资源文件 </B><BR><BR>在resources目录下建立一个application_tw.properties,输入内容： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE># -- international test -- 
test.title=???H化程式?y?? 
test.body=這是一個國際化程式測試例子</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>因为java的国际化是通过unicode码来实现，所以要把代码转为unicode码；在Dos下，转到resources目录，执行： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>native2ascii application_tw.properties application_zh_TW.properties</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>转换后的application_zh_TW.properties文件内容为： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE># -- international test -- 
test.title=\u570b\u969b\u5316\u7a0b\u5f0f\u6e2c\u8a66 
test.body=\u9019\u662f\u4e00\u500b\u570b\u969b\u5316\u7a0b\u5f0f\u6e2c\u8a66\u4f8b\u5b50</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>这就是上面的繁体中文的unicode码； <BR><BR><B>12、 测试多语言 </B><BR><BR>打开IE的“工具”-&gt;“Internet选项”菜单，“常规”选项卡，点击其中的“语言”按钮，添加“英语(美国)-[en-us]”语言，将其他的语言删除，重新启动IE后，输入http://localhost:8080/international/index.jsp，你会发现内容已经变成英文； <BR><BR>用同样的方法，可以测试简体中文和繁体中文。</FONT></SPAN></TD></TR></TBODY></TABLE></DIV></TD></TR></TBODY></TABLE></HTML:HTML><img src ="http://www.blogjava.net/faithwind/aggbug/30441.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faithwind/" target="_blank">黑咖啡</a> 2006-02-13 13:45 <a href="http://www.blogjava.net/faithwind/articles/30441.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用AJAX开发智能Web应用程序之高级篇 </title><link>http://www.blogjava.net/faithwind/articles/30022.html</link><dc:creator>黑咖啡</dc:creator><author>黑咖啡</author><pubDate>Thu, 09 Feb 2006 05:36:00 GMT</pubDate><guid>http://www.blogjava.net/faithwind/articles/30022.html</guid><wfw:comment>http://www.blogjava.net/faithwind/comments/30022.html</wfw:comment><comments>http://www.blogjava.net/faithwind/articles/30022.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/faithwind/comments/commentRss/30022.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faithwind/services/trackbacks/30022.html</trackback:ping><description><![CDATA[<A href="http://www.mydown.com/code/250/250509.html" target=_blank><FONT color=#dd4822>下载本文源代码</FONT></A><BR><BR>　　<B>一、 引言</B><BR><BR>　　<A href="http://dev.yesky.com/441/2251441.shtml?412" target=_blank><FONT color=#0033ff>在第一部分中</FONT></A>，我们讨论了AJAX基础——建立从脚本到服务器的通讯的能力，这正是使HTML页面具有动态能力的原因所在。然而，这就意味着我们已准备好抛弃我们自己版本的Yahoo邮件吗？不，还没有。原因在于：AJAX是一个混合的祝福。一方面，它使我们能够在Web上创建丰富的桌面级的应用程序；另一方面，如果我们把"翻页面式"的Web应用程序与客户端/服务器或Swing版本的程序进行比较，那么会看到其开发实践并不很相同。我们将需要习惯于这样的事实：构建一个丰富的UI需要时间。须知，允许用户实现更大的灵活性也就相应地需要付出更多的时间为代价。<BR><BR>　　最后的答案当然要依赖于大量的组件库、框架以及具有工业力量的开发工具。且不考虑工具，本文集中于讨论在今天对于AJAX热心者有哪些技术是可用的。在强调需要构建可重用的商业组件的同时，本文将重点分析"隐含的"JavaScript中的面向对象的力量。另外，在强调需要构建定制的UI组件的同时，本文将介绍一个简便的方法——用定制的客户端HTML标签来封装描述逻辑。<BR><BR>　　<B>二、 AJAX语言——对象面向的JavaScript</B><BR><BR>　　由定义来看，JavaScript是典型的AJAX语言。不同于Java，JavaScript并不强调OO风格的编码。然而，令人吃惊的是JavaScript居然全面支持所有的OO语言的主要属性：封装、继承和多态性。Douglas Crockford甚至称JavaScript是"世界上最易被误解的编程语言"。让我们回顾一下JavaScript的面向对象的地方吧。<BR><BR>　　数据类型<BR><BR>　　在Java中，一个类定义了一个数据和它的相关行为的组合。尽管JavaScript保留了class关键字，但是它不支持与常规OOP语言一样的语义。<BR><BR>　　这听起来可能觉得奇怪，但是在JavaScript中，对象是用函数来定义的。事实上，通过在下面的示例中定义一个函数，你就定义了一个简单的空类Calculator：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>function Calculator() {}</TD></TR></TBODY></TABLE><BR>　　一个新的实例的创建与在Java中相同-使用new操作符：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>var myCalculator = new Calculator();</TD></TR></TBODY></TABLE><BR>　　上面这个函数不仅定义一个类，而且还担当了一个构造器。在此，操作符new实现了这一魔术-实例化一个类Calculator的对象并且返回一个对象参考而不是只调用该函数。<BR><BR>　　创建这样的空类是没错，但在实际中并没有多大用处。下面，我们准备使用一个Java-脚本原型结构来填充类定义。JavaScript使用原型当作创建对象的模板。所有的原型属性和方法被参考引用地复制到一个类的每个对象中，所以它们都具有相同的值。你可以改变一个对象中的原型属性的值，并且该新值会覆盖从原型中复制过来的缺省值，但是这仅对于在一个实例中。下列语句将把一个新属性添加到Calculator对象的原型上：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>Calculator.prototype._prop = 0;</TD></TR></TBODY></TABLE><BR>　　既然JavaScript并没有提供一个方法来从句法上表示一个类定义，那么我们将使用with语句来标记该类的定义边界。这也将使得示例代码更为短小，因为该with语句被允许在一个指定的对象上执行一系列的语句而不需要限制属性。<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>function Calculator() {};<BR>with (Calculator) {<BR>　prototype._prop = 0;<BR>　prototype.setProp = function(p) {_prop = p};<BR>　prototype.getProp = function() {return _prop};<BR>}</TD></TR></TBODY></TABLE><BR>　　到目前为止，我们定义了并且初始化了公共变量_prop，并且为它提供了getter和setter方法。<BR><BR>　　需要定义一个静态变量？你可以把静态变量当作是为类所拥有的一个变量。因为在JavaScript中的类用函数对象来描述，所以我们只需要把一个新属性添加到该函数上：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>Calculator.iCount=0;</TD></TR></TBODY></TABLE><BR>　　现在，既然这个iCount变量是一个Calculator对象的属性，那么它将会被类Calculator的所有实例所共享。<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>function Calculator() {Calculator.iCount++;};</TD></TR></TBODY></TABLE><BR>　　上面的代码计算类Calculator的所有实例的个数。<BR><BR>　　封装<BR><BR>　　通过使用如上面所定义的"Calculator"，我们可以存取所有的"class"数据；然而，这增加了派生类中命名冲突的危险性。我们明显地需要封装以把对象看作自包含的实体。<BR><BR>　　数据封装的一种标准语言机制是使用私有变量。并且一个常用的仿效一个私有变量的JavaScript技术是在构造器中定义一个局部变量；这样以来，该局部变量的存取只能经由getter和setter来实现-它们是该构造器中的内部函数。在下列实例中，_prop变量在Calculator函数中定义并且在函数范围外不可见。其中有两个匿名的内部函数（分别被赋予setProp和getProp属性）让我们存取"私有"变量。另外，请注意，这里this的使用-十分相似于在Java中的用法：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>function Calculator() {<BR>　var _prop = 0;<BR>　this.setProp = function (p){_prop = p};<BR>　this.getProp = function() {return _prop};<BR>};</TD></TR></TBODY></TABLE><BR>　　常常被忽视的是在JavaScript中作如此封装所付出的代价。须知，这种代价可能是巨大的，因为内部函数对象对于该"class"的每一个实例被不断地重复创建。<BR><BR>　　因此，既然基于原型构建对象速度更快并且消费更少些的内存，那么我们在最强调性能的场所特别支持使用公共的变量。请注意，你可以使用命名惯例来避免名称冲突-例如，在公共的变量的前面加上该类名。<BR>继承<BR><BR>　　乍看之下，JavaScript缺乏对类层次的支持，这很相似于面向对象语言的程序员对于现代语言的期盼。然而，尽管JavaScript句法没有象Java一样支持类继承，但是我们仍然能够在JavaScript中实现继承-通过把已定义类的一个实例拷贝到其派生类的原型当中。<BR><BR>　　在我们提供举例之前，我们需要介绍一个constructor属性。JavaScript保证每一个原型中包含constructor-它拥有到该构造器函数的一个参考。换句话说，Calculator.prototype.constructor包含一个到Calculator()的参考。<BR><BR>　　现在，下面的代码显示了怎样从基类Calculator派生类ArithmeticCalculator。其中，"第一行"取得类Calculator的所有的属性，而"第二行"把原型constructor的值恢复成ArithmeticCalculator：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>function ArithmeticCalculator() { };<BR>with (ArithmeticCalculator) {<BR>　ArithmeticCalculator .prototype = new Calculator();//第一行<BR>　prototype.constructor = ArithmeticCalculator;//第二行<BR>}</TD></TR></TBODY></TABLE><BR>　　就算上面的实例看起来象一个合成体而不象是继承，但是JavaScript引擎还是清楚这个原型链的。特别是，instanceof操作符会正确地适用于基类和派生类。假定你创建类ArithmeticCalculator的一个新实例：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>var c = new ArithmeticCalculator;</TD></TR></TBODY></TABLE><BR>　　表达式c instanceof Calculator和c instanceof ArithmeticCalculator都会成立。<BR><BR>　　注意，在上面示例中的基类的constructor是在初始化ArithmeticCalculator原型时被调用的，而在创建派生类的实例时是不被调用的。这可能会带来不想要的负面影响，而且为了实现初始化你应该考虑创建一个独立的函数。由于该构造器并不是一个成员函数，所以它无法通过this参考引用调用。我们将需要一个能调用超类的"Calculator"成员函数：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>function Calculator(ops) { ...};<BR>with (Calculator) { prototype.Calculator=Calculator;}</TD></TR></TBODY></TABLE><BR>　　现在，我们可以写一个继承类-它显式地调用基类的构造器：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>function ArithmeticCalculator(ops) { this.Calculator(ops);};<BR>with (ArithmeticCalculator) {<BR>　ArithmeticCalculator .prototype = new Calculator;<BR>　prototype.constructor = ArithmeticCalculator;<BR>　prototype.ArithmeticCalculator = ArithmeticCalculator;<BR>}</TD></TR></TBODY></TABLE><BR>　　多态性<BR><BR>　　JavaScript是一种非类型化的语言-在此，一切都是对象。因此，如果有两个类A和B，它们都定义一个foo()，那么JavaScript将允许在A和B的实例上多态地调用foo()-即使不存在层次关系(虽然是可实现的)。从这一角度来看，JavaScript提供一个比Java更宽的多态性。这种灵活性，象往常一样，也要付出代价。在这种情况中，代价是把类型检查工作代理到应用程序代码。具体地说，如果需要检查一个参考确实指向一个所希望的基类，那么这可以通过instanceof操作符来实现。<BR><BR>　　另一方面，JavaScript并不检查函数调用中的参数-这可以防止用一样的命名和不同的参数来定义多态函数（并且让编译器选择正确的签名）。代之的是，JavaScript提供了一个Java 5风格的函数范围内的argument对象-它允许你根据参数的类型和数量的不同而实现一个不同的行为。<BR><STRONG>三、 示例展示<BR><BR></STRONG>　　本文所附源码列表1实现了一个计算器-它可以计算以一个逆向波兰式标志的表达式。该示例展示了本文中所介绍的主要技术并且也介绍了一些独特的JavaScript特性的用法，例如在一个动态函数调用中以一个数组元素的方式访问对象属性。<BR><BR>　　为了使列表1工作，我们需要另外准备一些代码-它们用于实例化该计算器对象并且调用evaluate方法：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>var e = new ArithmeticCalcuator([2,2,5,"add","mul"]);<BR>alert(e.evaluate()); </TD></TR></TBODY></TABLE><BR>　　<B>四、 AJAX组件授权</B><BR><BR>　　所有的AJAX组件授权方案在今天被逻辑地分成两组。具体地说，第一组用于与基于HTML的UI定义的无缝集成。第二组把HTML当作一个UI定义语言以支持某种XML。在本文中，我们从第一组中来展示一种方法-虽然它存在于浏览器之中却是类似于JSP标签。这些浏览器特定的组件授权扩展在IE情形下称作元素行为，而在最近版本的Firefox，Mozilla和Netscape 8情形下称作可扩展的绑定。<BR><BR>　　<B>五、 定制标签</B><BR><BR>　　Internet Explorer，从版本5.5开始，支持定制的客户端HTML元素的JavaScript授权。不象JSP标签，这些对象并没有在服务器端被预处理到HTML中。而是，它们成为一标准HTML对象模型的合法扩展，并且包括构造控件在内的一切事情，都是动态地发生在客户端的。同样，基于Gecko-引擎的浏览器能够用一个可重用功能动态地装饰任何现有的HTML元素。<BR><BR>　　因此，我们有可能用具有HTML语法的方法、事件和属性来构建一个具有丰富的UI组件的库。这样的组件可以被自由地混合于标准HTML中。内部地，这些组件将会与应用程序服务器进行通讯-以AJAX风格。换句话说，你有可能（并且相对简单地）构建自己的AJAX对象模型。<BR>这种IE风味的方法被称为HTC或HTML组件；其Gecko版本被称为XBL-可扩展的绑定语言（eXtensible Bindings Language）。为了实现本文目的，我们集中于讨论IE。<BR><BR>　　<B>六、 输入HTML组件-HTC</B><BR><BR>　　HTC或HTML组件也被称作行为。它们被划分为两种类型：一种是依附的行为-用一组属性、事件和方法装饰任何现有的HTML元素；另一种是元素行为-看上去象宿主页面的定制的HTML标签的一个扩展集合。依附的行为和元素行为一起提供了开发组件和应用程序的一种简单方案。在此，我们将展示一下最为综合的情形-元素行为。<BR><BR>　　数据绑定复选框控件<BR><BR>　　为了展示元素行为，我们将构建一个定制的数据绑定复选框。构建这样一个控件背后的基本原因在于，一个标准HTML复选框具有下面若干显著的缺点：<BR><BR>　　·需要应用程序编码来把"checked"属性的值映射到商业域值，例如"Y[es]"/"N[o]"，"M[ale]"/"F[emale]"，等等。HTML复选框使用"checked"属性，而许多其它HTML控件使用的则是"value"属性。<BR><BR>　　·需要应用程序编码来维持该控件的状态（修改过的/未修改过的）。这实际上是在所有的HTML控件普遍存在的一个问题。<BR><BR>　　·需要应用程序编码才能创建一个关联标签-它应该接受鼠标点击并相应地改变该复选框的状态。<BR><BR>　　·标准HTML复选框不支持"校验"事件以允许取消一个GUI行为，而这种要求可能存在于某些应用程序中。<BR><BR>　　现在，让我们看一个正在构建的该控件的用法示例，它的用法可能如下情形：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>＜checkbox id="cbx_1" value="N" labelonleft="true"<BR>label="Show Details:" onValue="Y" offValue="N"/＞ </TD></TR></TBODY></TABLE><BR>　　另外，我们的控件将支持可取消的事件onItemChanging和通知事件onItemChanged。<BR><BR>　　定义定制标签<BR><BR>　　从结构上讲，一个定制标签是一个具有一个HTC扩展名的文件-它在＜PUBLIC:COMPONENT＞和＜/PUBLIC:COMPONENT＞标志之间对它的属性，方法和事件加以描述。<BR><BR>　　为了定义一个定制CHECKBOX标签，我们创建一个如下列代码片断中的文件checkbox.htc-其中，第一行负责设置该组件的标签名：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>＜PUBLIC:COMPONENT NAME="cbx" tagName="CHECKBOX"＞<BR>＜PROPERTY NAME="value" GET="getValue" PUT="putValue" /＞<BR>//我们把组件的所有另外的属性放在这里<BR>＜METHOD NAME="show" /＞<BR>//我们把组件的所有另外的方法放在这里<BR>＜EVENT NAME="onItemChanging" ID="onItemChanging"/＞<BR>//我们把组件将向应用程序激活的所有另外的事件放在这里<BR>＜ATTACH EVENT="oncontentready" HANDLER="constructor" /＞<BR>//我们把组件自己处理的另外的事件放在这里<BR>＜SCRIPT＞<BR>//我们把所有的方法，属性getters和setters和事件处理器放在这里<BR>＜/SCRIPT＞<BR>＜/PUBLIC:COMPONENT＞ </TD></TR></TBODY></TABLE><BR>　　使用定制标签<BR><BR>　　尽管HTC文件的内容比较重要，但是这与其文件名是什么无关。值得注意的是，指向该HTC文件的URL需要被使用IMPORT指令指定-这必须在相应的定制标签第一次出现之前（在页面上）完成。下面是最简单的可能的页面使用一个定制的复选框可能看上去的样子-假定该页面和HTC文件处理同一个文件夹下：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>＜HTML xmlns:myns＞<BR>＜?IMPORT namespace="myns" implementation="checkbox.htc" ＞<BR>＜BODY＞<BR>＜myns:checkbox id='cbx_1' label='Hello'/＞<BR>＜/BODY＞<BR>＜/HTML＞</TD></TR></TBODY></TABLE><BR>　　请注意，定制CHECKBOX是怎样在打开的HTML标签中被映射到一个非缺省的命名空间"myns"的。这个IMPORT指令实现把HTC同步加载到浏览器的内存并且还指示浏览器怎样为适当的命名空间实现名称确定的（HTC到命名空间的关联可能是多对一的）。<BR><BR>　　定制标签的构造器<BR><BR>　　最好的初始化HTC的方法是，一旦它被装载就处理oncontentready事件。因此，我们可以定义处理器函数-为了概念清晰起见，我们称之为构造器：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>＜ATTACH EVENT="oncontentready" HANDLER="constructor" /＞</TD></TR></TBODY></TABLE><BR>　　constructor()的逻辑是简单的：根据属性labelonleft的值(见下面的属性定义)按顺序连接一个常规HTML复选框和HTML标签：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>function constructor() {<BR>　//我们将把一个HTML复选框和标签添加到元素体<BR>　//详细情形见列表2<BR>}</TD></TR></TBODY></TABLE><BR>　　定义定制标签属性<BR><BR>　　为了定义属性labelonleft，我们又在＜PUBLIC:COMPONENT＞部分加上一行：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>＜PROPERTY NAME="labelonleft" VALUE="true"/＞ </TD></TR></TBODY></TABLE><BR>　　请注意，这个属性并没有包含getter和/或setter方法。属性onValue和offValue不仅提供了从复选框状态到一个商业值域的映射而且不需要getters和setters：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>＜PROPERTY NAME="onValue" VALUE="true"/＞<BR>＜PROPERTY NAME="offValue" VALUE="false" /＞ </TD></TR></TBODY></TABLE><BR>　　然而，属性checked是用两个getter和setter定义的：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>＜PROPERTY NAME="checked" GET="getChecked" PUT="putChecked" /＞</TD></TR></TBODY></TABLE><BR>　　因此，我们在＜SCRIPT＞部分建立了上面两个方法的定义。正如你所见，setter putChecked()-将在每次复选框状态改变时激发-把value属性设置为下面两个变体之一：onValue或OffValue。请注意，putChecked()将不仅可由在复选框-宿主页面中的脚本触发，而且也能通过在checkbox.htc中的相应的任何赋值操作触发。<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>var _value;<BR>function putChecked( newValue ) {<BR>　value = (newValue?onValue:offValue);<BR>}<BR>function getChecked(){<BR>　return ( _value == onValue);<BR>}</TD></TR></TBODY></TABLE><BR><STRONG>七、 为定制标签定义事件<BR><BR></STRONG>　　让我们看一下onItemChanging和onItemChanged事件的定义以及这些事件是怎样在value属性的setter内部被激发和处理的（见所附源码中的列表2）。方法putValue()有几个让人感兴趣的地方。首先，在分析CHECKBOX标签期间，可以调用这个方法-只要指定这个HTMLvalue属性。这正解释了为什么我们为非构造对象建立一个单独的逻辑分支-为把构造过程与一个对用户击键的反应区别开来。其次，在此我们展示了定制事件onItemChanging的创建和处理-它允许应用程序取消行为。请注意，通过这种方式，无论是击键还是通过编程方式实现赋值都能达到取消的目的。<BR><BR>　　事件取消<BR><BR>　　为了取消事件，一个应用程序应该拦截该事件并且把event.returnValue设置为false。下面的代码片断展示了应用程序是怎样实现取消事件过程的：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>cbx_1::onItemChanging() {<BR>. . . . .<BR>if (canNotBeAllowed) {<BR>　event.returnValue=false;<BR>　. . . . .<BR>}</TD></TR></TBODY></TABLE><BR>　　如果事件没被取消，putValue()把内部的普通HTML复选框的checked属性设置为每个相应的当前值-如果它等于onValue，这个内部复选框将被选中；如果它等于offValue(不存在第三种选择)，复选框不被选中(完整的列表见本文所附源码中的列表2)。<BR><BR>　　复选框的HTML内幕<BR><BR>　　我们控件的绘制是通过助理函数addLabel()和addCheckBox()来实现的并且从一个constructor()内部调用。这些函数把HTML注入进元素的innerHTML。这种注入式HTML的一种简化形式如下所示：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>＜LABEL for=cb_{uniqueID}＞Show Details:＜/LABEL＞<BR>＜INPUT id=cb_{uniqueID} type=checkbox /＞ </TD></TR></TBODY></TABLE><BR>　　在此，uniqueID是一个由IE所生成的唯一的（在一个页面内）字符串-它用来识别HTC的实例。<BR><BR>　　<B>八、 再封装</B><BR><BR>　　在我们的CHECKBOX中有一个缺点。按照我们建立它的方式，在constructor()期间被注入的HTML将隶属于宿主该HTC的页面的DOM。而且，全局的JavaScript变量like_value属于它们所在的文档的全局范围。这是危险的，因为我们偶然会遇到命名冲突的可能性：最明显的情形是使用同一个组件的多个实例。另外这还会导致一个可能性-我们的控件可能会偶然地用相同的名称参考其它对象，反之也如此。<BR><BR>　　为简化起见，需要建立一种专门的机制来为对象授权启动一个真正模块化方法。幸好，HTC技术支持一种智能答案-viewLink。<BR>　 <BR>　　把一个控件声明为封装的最容易的方法是把一个额外声明放到打开和关闭的PUBLIC:COMPONENT标签之间：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>＜PUBLIC:DEFAULTS viewLinkContent/＞ </TD></TR></TBODY></TABLE><BR>　　该控件立即就变成封装性的；而且它有自己的HTML文档树-成为主文档的原子组件。该对象的每个实例有它自己的实例值的集合并且只有公共方法和属性能够从外界代码中加以存取。换句话说，该viewLink机制充分地启动了复杂的Web应用程序的设计和实现-通过使用一种真正的OO的基于组件的方法。<BR><BR>　　特别地，我们可以简化代码-通过从内部复选框和HTML标签的定义中删除uniqueID后缀，因为我们不再担心命名冲突。因此，我们可以替换下面这一行：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>eval( 'cb_'+uniqueID).checked = ( _value == onValue ); </TD></TR></TBODY></TABLE><BR>　　用<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>cb.checked = ( _value == onValue );</TD></TR></TBODY></TABLE><BR>　　并相应地改变addCheckbox()和addLabel()。<BR><BR>　　<B>九、 结论</B><BR><BR>　　既然AJAX竞赛刚刚开始，那么就不存在什么AJAX标准并且没有现成的你可以依赖以构建你的应用程序的可广为接受的RAD工具。虽然软件供应商们可能还需要较长一段时间来创建这种强健的开发平台，AJAX热心者已经开始着手准备-通过一些良好定义的API把可重用的代码块封装为商业组件。<BR><BR>　　以这种方向导航，本文概括了AJAX语言的OO"力量"-JavaScript。另外，还展示了一种可用的组件-授权策略-客户端定制标签技术。我们在仅描述IE特定的定制标签的同时，还另外提供了一个可下载的实例-适于Mozilla浏览器的可扩展的绑定实例。 <img src ="http://www.blogjava.net/faithwind/aggbug/30022.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faithwind/" target="_blank">黑咖啡</a> 2006-02-09 13:36 <a href="http://www.blogjava.net/faithwind/articles/30022.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用AJAX开发智能Web应用程序之基础篇 </title><link>http://www.blogjava.net/faithwind/articles/30021.html</link><dc:creator>黑咖啡</dc:creator><author>黑咖啡</author><pubDate>Thu, 09 Feb 2006 05:34:00 GMT</pubDate><guid>http://www.blogjava.net/faithwind/articles/30021.html</guid><wfw:comment>http://www.blogjava.net/faithwind/comments/30021.html</wfw:comment><comments>http://www.blogjava.net/faithwind/articles/30021.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/faithwind/comments/commentRss/30021.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faithwind/services/trackbacks/30021.html</trackback:ping><description><![CDATA[　<A href="http://www.mydown.com/code/250/250506.html" target=_blank><FONT color=#d54d2b>下载本文源代码</FONT></A><BR><BR>　　<B>一. 什么是AJAX?</B><BR><BR>　　这个名字代表了异步JavaScript＋XMLHTTPRequest，并且意味着你可以在基于浏览器的JavaScript和服务器之间建立套接字通讯。其实AJAX并不是一种新技术，而是已经成功地用于现代浏览器中的若干成功技术的可能性组合。所有的AJAX应用程序实现了一种“丰富的”UI——这是通过JavaScript操作HTML文档对象模型并且经由XMLHttpRequest实现的精确定位的数据检索来实现的。典型的示例AJAX应用程序是Google Labs(http://labs.google.com)的Google Maps和Google Suggest。这些应用程序现场监视用户输入并且提供实时的页面更新。最重要的是，在用户通过地图导航或输入一个查找字符串的同时，这些事件不需要刷新页面。<BR><BR>　　事实上，支持这些令人感到惊讶的应用的技术已经出现一段时间了，尽管它们要求复杂的技能以及使用浏览器的技巧。一些专利产品就提供了相似的能力——如Macromedia Flash插件，Java Applets或.NET运行时——在达到实用上已经有一段时间了。把一种可与服务器通话的脚本组件引入到浏览器中的思想早在IE 5.0中就已经存在。Firefox和其它流行的浏览器也加入到浏览器大军中并以一种内置对象形式支持XMLHTTPRequest。随着跨平台浏览器的出现，这些技术得到了认可并在2004年3月一家称为Adaptive Path的公司中正式提出了AJAX。<BR><BR>　　简而言之，由于来自于Google的支持和安装了一点可用的浏览器技术，加上为了一种"更好的用户体验"，每个人都在把客户端技术添加到Web应用程序上。<BR><BR>　　<B>二. AJAX与传统应用程序的区别</B><BR><BR>　　一个传统Web应用程序模型实际上是一种基本的事件——用户被迫提交表单以实现页面交换。也就是说，表单提交和页面传送无法得到保证：还有更坏的情形——用户需要再次点击。这与AJAX截然不同-——数据跨过线路而不是完整的HTML页面传输。这种数据交换是经由特定的浏览器对象：XMLHttpRequest实现的；再由适当的逻辑来处理每个数据请求的结果，页面的特定区域而不是完整的页面被更新。结果是更快的速度，更少的拥挤和更好的信息传送控制。<BR><BR>　　传统型"click-refresh"Web应用程序强迫用户中断工作过程而等待页面的重装。通过引入AJAX技术，一个客户端脚本能够异步地与服务器通话，而用户仍能保持输入数据。除了对用户透明之外，这样的异步意味着服务器可以有更多时间来处理请求。<BR><BR>　　传统Web应用程序把所有的处理代理到服务器并且强迫服务器进行状态管理。AJAX允许灵活划分应用程序逻辑以及客户和服务器之间的状态管理。这就消除了一种"click-refresh"依赖性并且提供更好的服务器可伸缩性。当该状态存储在客户端，你就不必跨越服务器来维持会话或保存/结束状态-其使用期限是由客户端来定义的。<BR><BR>　　<B>三. AJAX——分布式的MVC</B><BR><BR>　　尽管AJAX应用程序依靠JavaScript来实现描述层，然而处理能力和知识库仍然存在于服务器上。此时，AJAX应用程序大量的与J2EE服务器通讯——把数据输入/输出Web服务和servlets。具有基于AJAX的描述层的J2EE应用程序和标准J2EE应用程序之间的区别首先在于，MVC是通过线路分布的。通过使用AJAX，视图是本地的，而模型和控制器是分布式的——这使得开发者能够灵活地决定哪些部件会是基于客户端的。具体地说，本地视图通过巧妙地操作HTML DOM而生成图形；控制器局部地处理用户输入并且根据开发者的判断扩展到服务器的处理——经由HTTP请求(Web服务，XML/RPC或其它)实现；模型的远程部分是根据客户端需要而下载的以达到实时更新客户端页面；并且状态是在客户端收集的。<BR><BR>　　在以后的AJAX文章中，我们将比较深入地讨论这里的每一种组件并提供有关它们联合在一起进行应用的示例。现在，先不多说，让我们详细地分析一个简单的AJAX示例。<BR><BR>　　<B>四. 邮政区号校验和查询</B><BR><BR>　　我们将创建一个包含三个INPUT字段（Zip，City和State）的HTML页面。我们将保证，只要用户输入邮政区号的前三个数字，该页面上的字段就会用第一个匹配的状态值填充。一旦用户输入了所有五位邮政区号数，我们将立即决定和填充相应的城市。如果邮政区号无效（在服务器的数据库没有找到），那么我们将把邮政区号的边界设置为红色。这样的可视化线索有助于用户并且在现代浏览器中已经成为一种标准（作为一实例，当Firefox找到一个HTML页面中的匹配关键字时，它会高亮与你在浏览器查找域输入的内容一致的部分）。<BR><BR>　　让我们首先创建一个简单的包含三个INPUT字段的HTML：zip，city和state。请注意，一旦一个字符输入进邮政区号字段域中，即调用方法zipChanged()。JavaScript函数zipChanged()(见下)在当zip长度为3时调用函数updateState()，而在当zip长度为5时调用函数up-dateCity()。而updateCity()和updateState()把大部分的工作代理到另一个函数ask()。<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>Zip:＜input id="zipcode" type="text" maxlength="5" onKeyUp="zipChanged()"<BR>style="width:60"/＞<BR>City: ＜input id="city" disabled maxlength="32" style="width:160"/＞<BR>State:＜input id="state" disabled maxlength="2" style="width:30"/＞<BR>＜script src="xmlhttp.js"＞＜/script＞<BR>＜script＞<BR>var zipField = null;<BR>function zipChanged(){<BR>zipField = document.getElementById("zipcode")<BR>var zip = zipField.value;<BR>zip.length == 3?updateState(zip):zip.length == 5?updateCity(zip):"";<BR>}<BR>function updateState(zip) {<BR>　var stateField = document.getElementById("state");<BR>　ask("resolveZip.jsp?lookupType=state&amp;zip="+zip， stateField， zipField);<BR>}<BR>function updateCity(zip) {<BR>　var cityField = document.getElementById("city");<BR>　ask("resolveZip.jsp? lookupType=city&amp;zip="+zip， cityField， zipField);<BR>}<BR>＜/script＞ </TD></TR></TBODY></TABLE><BR>　　函数ask()与服务器进行通讯并分配一个回调函数来处理服务器的响应（见下列代码）。后面，我们将分析具有双重特点的resolveZip.jsp的内容-它根据zip字段中的字符数查找city或state信息。重要的是，ask()使用了具有异步特点的XmlHttpRequest，这样填充state和city字段或着色zip字段边界就可以不必减慢数据入口而得以实现。首先，我们调用request.open()-它用服务器打开套接字频道，使用一个HTTP动词(GET或POST)作为第一个参数并且以数据提供者的URL作为第二个参数。request.open()的最后一个参数被设置为true-它指示该请求的异步特性。注意，该请求还没有被提交。随着对request.send()的调用，开始提交-这可以为POST提供任何必要的有效载荷。在使用异步请求时，我们必须使用request.onreadystatechanged属性来分配请求的回调函数。(如果请求是同步的话，我们应该能够在调用request.send之后立即处理结果，但是我们也有可能阻断用户，直到该请求完成为止。)<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>HTTPRequest = function () {<BR>　var xmlhttp=null;<BR>　try {<BR>　　xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");<BR>　} catch (_e) {<BR>　　try {<BR>　　　xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");<BR>　　} catch (_E) { }<BR>　}<BR>　if (!xmlhttp &amp;&amp; typeof XMLHttpRequest != 'undefined') {<BR>　　try {<BR>　　　xmlhttp = new XMLHttpRequest();<BR>　　} catch (e) {<BR>　　　xmlhttp = false;<BR>　　}<BR>　}<BR>　return xmlhttp;<BR>}<BR><BR>function ask(url， fieldToFill， lookupField) {<BR>　var http = new HTTPRequest();<BR>　http.open("GET"， url， true);<BR>　http.onreadystatechange = function (){ handleHttpResponse(http， fieldToFill，lookupField)};<BR>　http.send(null);<BR>}<BR><BR>function handleHttpResponse(http， fieldToFill， lookupField) {<BR>　if (http.readyState == 4) {<BR>　　result = http.responseText;<BR>　　if ( -1 != result.search("null") ) {<BR>　　　lookupField.style.borderColor = "red";<BR>　　　fieldToFill.value = "";<BR>　　} else {<BR>　　　lookupField.style.borderColor = "";<BR>　　　fieldToFill.value = result;<BR>　　}<BR>　}<BR>}</TD></TR></TBODY></TABLE><BR>　　为ask()所使用的HttpRequest()函数(见上)是一跨浏览器的XMLHTTPRequest的一个实例的构造器；稍后我们将分析它。到目前为止，请注意对于handleResponse()的调用是如何用一匿名函数包装的-这个函数是function(){handleHttpResponse(http，fieldToFill， lookupField)}。<BR><BR>　　该函数的代码是动态创建的并且在每次我们给http.onreadstatechange属性赋值时被编译。结果，JavaScript创建一个指向上下文（所有的变量都可以存取正在结束的方法-ask()）的指针。这样以来，匿名函数和handleResponse()就能够被保证充分存取所有的上下文宿主的变量，直至到匿名函数的参考被垃圾回收站收集为止。换句话说，无论何时我们的匿名函数被调用，它都能无缝地参考request，fieldToFill和lookupField变量，就象它们是全局的一样。而且，每次ask()调用都将创建环境的一个独立拷贝，并且此时这些变量中保存有该函数将结束时的值。<BR><BR>　　现在，让我们分析一下函数handleResponse()。既然它能够在请求处理的不同状态下激活，那么该函数将忽略所有的情形-除了该请求处理完成之外-这相应于request.readyState属性等于4("Completed")。此时，该函数读取服务器的响应文本。与它的名字所暗示的相反，XmlHttpRequest的输入和输出都不必限于XML格式。特别地，我们的resolveZip.jsp(见源码中的列表1)返回普通文本。如果返回值为"unknown"，那么该函数将假定邮政区号是无效的并且把查找字段(zip)边界颜色置为红色。否则，返回值被用于填充字段state或city，并且zip的边界被赋予一种缺省颜色。<BR><BR>　　XMLHttpRequest-传输对象<BR><BR>　　让我们返回到我们的XMLHTTPRequest的跨浏览器实现。最后一个列表包含一个HttpRequest()函数-它向上兼容于IE5.0和Mozilla 1.8/FireFox。为简化起见，我们只创建一个微软XMLHTTPRequest对象，而且如果创建失败，我们假定它是Firefox/Mozilla。<BR><BR>　　该函数的核心是XMLHTTPRequest-这是一个本机浏览器对象，它为包括HTTP协议的任何东西与服务器之间的通讯提供方便。它允许指定任何HTTP动词，头部和有效载荷，并且能够以异步或同步方式工作。不需要下载也不需要安装任何插件-尽管在IE的情形下，XMLHTTPRequest是一个集成到浏览器内部的ActiveX。因而，"Run ActiveX Control and Plugins"默认IE权限应该正好适合使用它。<BR><BR>　　最重要的是，XMLHTTPRequest允许一个到服务器的RPC风格的编程查询而不需要任何页面刷新。它以一种可预测的，可控制的方式来实现此-提供了到HTTP协议的所有细节的完整存取-包括头部和数据的任何定制格式。在以后的文章中，我们将向你展示其它一些业界协议-你可以在这些传输协议（如Web服务和XML-RPC）之上运行-它们极大地简化大规模应用程序的开发和维护。<BR><STRONG>五.服务器端逻辑<BR><BR></STRONG>　　最后，服务器端的resolveZip.jsp被从函数ask()中调用（见所附源码中的列表1）。这个resolveZip.jsp在两种由当前的邮政区号长度所区分的独立的场所下被调用(见zipChanged()函数)。请求参数lookupType的值或者是state或者是city。为简化起见，我们将假定，两个文件state.properties和city.properties都位于服务器中C驱动器的根目录下。resolveZip.jsp逻辑负责用适当的预装载的文件返回查找值。<BR>我们的支持AJAX的页面现在已经准备好了。<BR><BR>　　<B>六.远程脚本技术-一种可选方法</B><BR><BR>　　一些更旧的AJAX实现是基于所谓的远程脚本技术。这种思想是，用户的行为导致经由IFRAME对服务器进行查询，而服务器用JavaScript作出响应，该脚本一旦到达客户端立即被执行。这与XMLHttpRequest方法相比存在较大的区别，在后者情况下，服务器响应数据而客户端解释数据。其好处是这种解决方案支持更旧的浏览器。 <BR><BR>　　基于IFRAME示例的HTML部分(见所附源码中的列表2)与我们在XMLHTTPRequest场合下所用的极相似，但是这次我们将引入另外一个IFRAME元素-controller：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>Zip:＜input id="zipcode" type="text" maxlength="5" onKeyUp="zipChanged()"<BR>style="width:60" size="20"/＞<BR>City: ＜input id="city" disabled maxlength="32" style="width:160" size="20"/＞<BR>State:＜input id="state" disabled maxlength="2" style="width:30" size="20"/＞<BR>＜iframe id="controller" style="visibility:hidden;width:0;height:0"＞＜/iframe＞ </TD></TR></TBODY></TABLE><BR>　　我们保持每次击键都调用zipChanged()一次，但是这一次，从zipChanged()中被调用的函数ask()(见所附源码中的列表3)负责设置IFRAME的src属性，而不是调用一个XMLHTTPRequest：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>function ask(url， fieldToFill， lookupField){<BR>　var controller = document.getElementById("controller");<BR>　controller.src= url+"&amp;field="+fieldToFill.id+"&amp;zip="+lookupField.id;<BR>} </TD></TR></TBODY></TABLE><BR>　　服务器端逻辑由一个粗略的resolveZip.jsp(见所附源码中的列表4)所描述。它与它的XMLHTTPRequest对应物相区别-它返回JavaScript语句，这些语句设置变量字段lookup和city的全局值，而且一旦它到达浏览器即从全局窗口的执行上下文中调用函数response()。<BR><BR>　　函数response()是一修改版本的handleResponse()-这一函数可以免于处理未完成的请求(详见本文所附源码中的列表2)。<BR><BR>　　<B>七. 难题</B><BR><BR>　　为简化起见，让我们"俯看"一下在我们的示例代码中的一些重要的问题：<BR><BR>　　1.事实-XMLHTTPRequest对象实例和回调函数调用在被使用以后并没被破坏-在每次调用后这有可能导致内存泄漏。适当编写的代码应该破坏或重用对象池中的这些实例。而且，客户端必须使用与服务器软件相同的对象管理技术。<BR><BR>　　2.在大多数情况下，错误往往得不到有效处理。例如，在方法ask()中对request.open()的调用可能引发一个异常，这是必须要捕获和处理的，即使在浏览器中没有设置JavaScript异常自动捕获功能。而handleResponse()函数又是另外一个例子。它必须要为可能的服务器端和通讯错误而检查headers和responseText值。在发生错误的情况下，它必须尽力恢复并/或者报告错误。正确开发的AJAX应用程序要尽可能避免"提交"松散的数据，因为往往存在线路断开和其它低级通讯的问题-所以这些程序必须建立一个强壮的和自恢复的框架为此提供支持。<BR><BR>　　3.当前服务器端框架提供相当多的功能-它们可以与一种自由刷新方法和谐相处。例如，让我们考虑一个定制的在指定时间内的服务器端认证的问题。在这种情况下，我们必须拦截到XMLHTTPRequest调用的安全系统响应，显示登录屏幕，然后在用户被认证后重新发出请求。<BR><BR>　　所有的这些问题只是一些典型的用低级API工作的任何应用程序代码，而且所有这些问题都能被解决。好消息是，解决这些问题所需要的技术十分相似于大多数Java开发技术，如Web服务，定制标签和XML/XSLT。唯一的区别在于，现在这些技术以下列形式用于客户端：<BR><BR>　　·Web服务-使用SOAP/REST/RPC等简单通讯标准<BR><BR>　　·客户端定制标签-打包丰富的客户端控件并集成AJAX功能<BR><BR>　　·数据操作-基于XML和基于XSLT技术<BR><BR>　　<B>八. 小结</B><BR><BR>　　AJAX方法能够向人们提供一种与桌面应用程序相同的丰富的互联网体验。但是，我们必须有选择地使用AJAX技术，如当你仍在线购物时，你绝对不想让你的信用卡通过后台处理就悄悄地开始付款。AJAX会成为一种持续的动力吗？我们当然希望这样。在过去的五年时间内我们一直在努力开发AJAX应用程序并且能证明它是健全并且很有效的。然而，它要求一个开发者必须精通大量技术而不是在传统的"click-refresh"Web应用程序中所使用的那些。<img src ="http://www.blogjava.net/faithwind/aggbug/30021.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faithwind/" target="_blank">黑咖啡</a> 2006-02-09 13:34 <a href="http://www.blogjava.net/faithwind/articles/30021.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts+Hibernate简化J2EE的文件操作</title><link>http://www.blogjava.net/faithwind/articles/30020.html</link><dc:creator>黑咖啡</dc:creator><author>黑咖啡</author><pubDate>Thu, 09 Feb 2006 05:32:00 GMT</pubDate><guid>http://www.blogjava.net/faithwind/articles/30020.html</guid><wfw:comment>http://www.blogjava.net/faithwind/comments/30020.html</wfw:comment><comments>http://www.blogjava.net/faithwind/articles/30020.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/faithwind/comments/commentRss/30020.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/faithwind/services/trackbacks/30020.html</trackback:ping><description><![CDATA[<STRONG>1. 引言<BR><BR></STRONG>　　每位Web开发者在工程中都必须实现至少实现一个客户文件的上载功能。永远需要！然而，要求用户仅提供一个指向其数据的<A class=bluekey href="http://www.yesky.com/key/3809/593809.html" target=_blank>URL</A>是不公平的。作为一个开发者，帮助用户顺利地完成这些正是你的工作。HTTP协议的使用将十分有助于解决这个问题，但是许多开发者并没有选择使用HTTP协议。<BR><BR>　　你需要解决的问题有：存储上载的文件并找到关于问题"Where？","Why？"和"How？"等等的有关答案。<BR><BR>　　本文将解释所有在解决这些问题中遇到的瓶颈，并提供了功能性的、易于理解的代码，这些内容很可能会应用于你将来的工程中。<BR><BR>　　<B>2. 准备工作</B><BR><BR>　　本文将使用当前最流行的开发工具，它们是:<BR><BR>　　·应用程序服务器:WebLogic 8.1 SP3服务器<BR><BR>　　·基于Java的构建工具:Apache <A class=bluekey href="http://www.yesky.com/key/337/595337.html" target=_blank>Ant</A> 1.6.2<BR><BR>　　·数据库服务器:MySQL 4.0.16<BR><BR>　　·用于从Java连接到MySQL的:MySQL Connector/J 3.1.7<BR><BR>　　<A class=bluekey href="http://www.yesky.com/key/395/395.html" target=_blank>Struts</A> 1.2.4用作构建Java Web应用程序的框架，而Hibernate 3.0(RC1)用于对象/关系持续性操作和查询服务。<BR><BR>　　本文虽然基于Windows平台写成，但在其它操作系统之上,应该稍作修改就能运行。<BR><BR>　　另外，读者还应熟悉BEA WebLogic服务器以及使用Struts和Hibernate进行J2EE应用程序的开发。篇幅所限，本文并没有讨论关于应用程序和数据库服务器配置的问题。<BR><BR>　　<B>3. 上载工程分析</B> <BR><BR>　　现在让我们讨论存储上载文件的机制，并回答上面列出的三个问题。<BR><BR>　　·Where？你将会把上载文件存储到一个数据库中。<BR><BR>　　·Why？在许多情况下，它确实是合适的解决方案。使用本文的解决方案，你不会因同步上载文件而烦恼，一旦你正在备份着应用程序-你只需要备份数据库就可以了。而且,你不需要与一个用户及其在一文件系统上的文件一直保持十分笨拙的联系。<BR><BR>　　·How？可以使用BLOB(二进制大型对象)字段实现。这样的字段用于存储大型的并且经常是原始或二进制的格式。Hibernate可以使你非常容易地操作这些字段。<BR><BR>　　典型情况下，一个企业应用程序(EAR)由两部分组成:Web层(WAR)和商业层(EJB)。商业层包含一个无状态的会话<A class=bluekey href="http://www.yesky.com/key/3040/593040.html" target=_blank>bean</A>-它借助于Hibernate的帮助实现数据的存储。图1显示了EJB的远程接口。<BR><BR>
<TABLE width="90%" align=center border=0>
<TBODY>
<TR>
<TD>
<DIV align=center><IMG src="http://dev.yesky.com/imagelist/05/09/tx1s46tg79j0.png" border=0><BR>图1.HelloSession EJB的接口。</DIV></TD></TR></TBODY></TABLE><BR>　　从Web层角度看，这个EJB为商业代理所存取。<BR><BR>　　注意，该代码使用了一个类User的对象。User代表<A class=bluekey href="http://www.yesky.com/key/3113/318113.html" target=_blank>什么意思</A>？它是一个保留在数据库中的用户实体的"Plain Old Java Object"（POJO）。你将会活跃地使用这个UserPOJO。设置它的属性并请求EJB来存储它，然后带回一个所有的已存在于数据库中的User实体的列表。或者,由它取回一个专门的User实体并存入POJO中，然后使用一个getter存取器来存取它的属性(见图2)。<BR><BR>
<TABLE width="90%" align=center border=0>
<TBODY>
<TR>
<TD>
<DIV align=center><IMG src="http://dev.yesky.com/imagelist/05/09/fjp4hsqvv9k6.jpg" border=0><BR>图2.所有的Web层的servlet都使用该User POJO。</DIV></TD></TR></TBODY></TABLE><BR>　　非常明显,Web层仅由三个servlet(Struts Action的)组成，一个用于上载文件,一个用于下载文件,一个用于列出所有的User实体及其相关文件。<BR><BR>　　·DownloadFileAction：该servlet仅使用一个参数id，这是在数据库中的一个用户的id。然后，它装入该用户的实体并把该文件从BLOB字段传递到ServletOutputStream。<BR><BR>　　·UploadFileAction：该servlet负责从一个HTML表单中提取数据并用这些数据进一步生成DynaActionForm。它仅提取用户名和上载的文件。<BR><BR>　　·ListAllFilesAction：该servlet没有输入参数或数据，仅负责从数据库中装入所有可用的用户User实体。<BR><STRONG>4. 环境准备<BR><BR></STRONG>　　如果所有相应的软件被正确下载并安装在你的PC上,那么下一步，你就可以准备数据库和存取该数据库的用户而且还要使用名为MySqlDS的JNDI设置好连接池与数据源。同时，请肯定MySQL Connector/J存在于你的类路径中！要检查这一点,只需输入：<BR><BR>echo %CLASSPATH%<BR><BR>　　文件mysql-connector-java-3.1.7-bin.jar(带有完整的路径)应该于此。这是必需的，因为WebLogic需要查找到MySQL Connector/J并用该驱动程序进行工作。<BR><BR>　　现在，既然一切准备妥当，我们就可以在数据库中创建一个表以用于保存用户实体。以任何MySQL客户身份登录到该数据库，然后输入：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>drop table if exists TABLE_USER_FILE;<BR>create table TABLE_USER_FILE<BR>(<BR>USER_ID int auto_increment not null,<BR>USER_USERNAME text,<BR>USER_FILENAME text,<BR>USER_FILETYPE text,<BR>USER_FILESIZE text,<BR>USER_FILEBIN longblob,<BR>primary key (USER_ID)<BR>)<BR>type = MyISAM;</TD></TR></TBODY></TABLE><BR>　　你看,一切都很简单。也许在此你最陌生的是longblob类型。二进制大型对象(BLOB)列是MySQL的秘密武器之一。这些列中存储了二进制的数据，你可以象其它普通的数据类型一样来检索和操纵它。根据MySQL指南有关资料，BLOB是一个二进制大型对象，它能容纳不同大小的数据。事实上,MySQL有四种BLOB类型:<BR><BR>　　·tinyblob:仅255个字符<BR><BR>　　·blob:最大限制到65K字节<BR><BR>　　·mediumblob:限制到16M字节<BR><BR>　　·longblob:可达4GB<BR><BR>　　在每个MySQL的文档(从MySQL4.0开始)的介绍中,一个longblob列的最大允许长度依赖于在客户/服务器协议中可配置的最大包的大小和可用内存数。<BR><BR>　　你可能对在BLOB中存储大型文件非常谨慎，但是请放心使用，MYSQL提供了这样的灵活性！最大包的大小可容易地通过文件my.ini中的适当行进行设置。例如:<BR><BR>set-variable = max_allowed_packet=10M<BR><BR>　　你能指定几乎任何你需要的大小。默认是1M。<BR><BR>　　现在，在Hibernate和WebLogic之间还存在一个问题。根据Hibernate的文档,Hibernate3.0使用ANTLR作为它的新的查询分析器。这真是个遗憾！但是BEA Weblogic在系统类路径中包括了ANTLR的一个版本，它在任何应用程序库装入前就已经被加载了。因为Weblog似乎不支持属性类装载器隔离，在应用程序的上下文中它是不会看到该Hibernate类的。BEA好象在包名前加上前缀来解决这个问题，但是现已发布的ANTLR并没有这个前缀。<BR><BR>　　这个问题的解决办法是，把所有的Hibernate和依赖库放到你的CLASSPATH中。就象如下这样：<BR><BR>C:\green\te3＞echo %CLASSPATH%<BR>.;C:\mysql-connector-java-3.1.7\mysql-connector-java-3.1.7-bin.jar;C:\hibernate-3.0\hibernate3.jar;<BR>C:\hibernate-3.0\lib\ehcache-1.1.jar;C:\hibernate-3.0\lib\jta.jar;C:\hibernate-3.0\lib\xml-apis.jar;<BR>C:\hibernate-3.0\lib\commons-logging-1.0.4.jar;C:\hibernate-3.0\lib\dom4j-1.5.2.jar;<BR>C:\hibernate-3.0\lib\antlr-2.7.4.jar;C:\hibernate-3.0\lib\cglib-full-2.0.2.jar;<BR>C:\hibernate-3.0\lib\jdbc2_0-stdext.jar;C:\hibernate-3.0\lib\xerces-2.6.2.jar;<BR>C:\hibernate-3.0\lib\jaxen-1.1-beta-4.jar;C:\hibernate-3.0\lib\commons-collections-2.1.1.jar;<BR>C:\hibernate-3.0\lib\log4j-1.2.9.jar;<BR><BR>　　现在，unzip源代码到任何你想要的目录下。用你喜欢的文件编辑器打开build.xml文件，并检查(如果必要的话，加以改变)前面涉及你的HOME目录的几行和你的域的标题。保存你的变化并输入:<BR><BR>　　ant<BR><BR>　　当工程建构完成时，你就会得到一个文件TE3.EAR，这是一个准备好等待发布的包(名字TE3仅是个普通名字)。然后，你就可以用WebLogic的管理控制台发布它，当发布后，用你的浏览器http://localhost:7001/te3/打开它。之后，你将看到两个选项:"upload file"和"list all files"。<BR><STRONG>5. 代码分析<BR><BR></STRONG>　　现在,你已经看到了一切是如何工作的，下面解释一下几个更为重要的代码片断。如前述，UploadFileAction.java使用DynaActionForm来保持HTML表单的属性。下面是它在/WEB-INF/struts-config.xml文件中的定义：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>...<BR>＜form-beans＞<BR>　＜form-bean name="uploadFileForm" type="org.apache.struts.action.DynaActionForm" dynamic="true"＞<BR>　　＜form-property name="myFile" type="org.apache.struts.upload.FormFile"/＞<BR>　　＜form-property name="myName" type="java.lang.String"/＞<BR>　＜/form-bean＞<BR>＜/form-beans＞<BR>...<BR>＜action-mappings＞<BR>＜action path="/UploadFile" type="com.prokhorenko.web.UploadFileAction" name="uploadFileForm"＞<BR>...</TD></TR></TBODY></TABLE><BR>　　下面的代码教你怎样通过属性存取来"存储"一个上传的文件：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>...<BR>User per = new User();<BR>DynaActionForm df = (DynaActionForm) form;<BR>FormFile myFile = (FormFile) df.get("myFile");<BR>...<BR>per.setFilebin ( Hibernate.createBlob (myFile.getInputStream()) );<BR>...</TD></TR></TBODY></TABLE><BR>　　Hibernate.createBlob(...)返回初始的不变的java.sql.Blob对象并使用它，因为为了设置User实体的filebin属性，该属性被定义并被映射为java.sql.Blob。<BR><BR>　　接下去一段有趣的代码来自于DownloadFileAction.java，它用’id’加载该User实体：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>...<BR>User per = bd.getUser( new Long((String)request.getParameter("id")) );<BR>...</TD></TR></TBODY></TABLE><BR>　　下一步,你就需要设置响应的头部，并开始把Blob型字段filebin的内容写到ServletOutputStream中：<BR><BR>
<TABLE borderColor=#cccccc width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>... <BR>ServletOutputStream outStream = response.getOutputStream();<BR>InputStream in = per.getFilebin().getBinaryStream();<BR>byte[] buffer = new byte[32768];<BR>int n = 0;<BR>while ( ( n = in.read(buffer)) != -1) {<BR>　outStream.write(buffer, 0, n);<BR>}<BR>in.close();<BR>outStream.flush();<BR>...</TD></TR></TBODY></TABLE><BR>　　<B>6. 最容易的解决方案</B><BR><BR>　　在所有官方档案中，Hibernate 3.0都包装了Blob和Clob的实例,这样以来就允许具有类型Blob或Clob的属性的类可以被分离、串行化、反串行化以及被传递而实现合并的目的。因此,你会看到，Struts和Hibernate几乎为你做了一切事情-而需要你做的仅仅是极少的几个步骤。 <BR><BR>　　现在看来，上载文件并把它们存储到数据库中已不再是象以前那样是一项繁重的任务。你只需选择正确的工具并知道如何灵活地使用它们即可！<img src ="http://www.blogjava.net/faithwind/aggbug/30020.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/faithwind/" target="_blank">黑咖啡</a> 2006-02-09 13:32 <a href="http://www.blogjava.net/faithwind/articles/30020.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>