﻿<?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-憨厚生-随笔分类-web系统开发</title><link>http://www.blogjava.net/hulizhong/category/37714.html</link><description>----Java's Slave----&lt;br&gt;
***Java's Host***</description><language>zh-cn</language><lastBuildDate>Thu, 17 Dec 2009 03:35:58 GMT</lastBuildDate><pubDate>Thu, 17 Dec 2009 03:35:58 GMT</pubDate><ttl>60</ttl><item><title>转 JavaScript:全面解析Ajax跨站数据传输和iframe跨域名js调用(6种方法)</title><link>http://www.blogjava.net/hulizhong/archive/2009/12/16/306196.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Wed, 16 Dec 2009 09:51:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/12/16/306196.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/306196.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/12/16/306196.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/306196.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/306196.html</trackback:ping><description><![CDATA[转 http://blog.youmila.com/?p=513<br />
<br />
<p>关于跨域名问题还是问题么，这方面的解决实践非常多，今天我就旧话重提把我所知道的通过几个应用场景来分别总结一下</p>
<p>先说明一点：我说的某某域名在您的控制下的意思是这个域名下的网页由您来负责开发内部的JavaScript<br />
<span style="font-weight: bold">场景一</span>：将bbs.xxx.com的页面用iframe嵌入到www.xxx.com的中，如何在iframe内外使用js通信<br />
一级域名都是xxx.com 这个域名一定是在您的控制下，所以你只要在两个页面中同时升级域名即可<br />
在父窗口和iframe内部分别加上js语句：document.domain=&#8221;xxx.com&#8221;;<br />
之后2个页面就等于在同一域名下，通过window.parent oIframe.contentDocument就可以相互访问，进行无障碍的JS通信<br />
在新浪、淘宝等很多页面都能找到这样的语句。不过document.domain不可以随便指定，只能向上升级，从bbs.xxx.com升级到yyy.com肯定会出错</p>
<p><span style="font-weight: bold">场景二</span>：将www.yyy.com的页面用iframe嵌入到www.xxx.com的中，两个域名都在您的控制下，如何在iframe内外进行一定的数据交流<br />
你可以通过相互改变hash值的方式来进行一些数据的通信</p>
<p>这里的实现基于如下技术要点：<br />
1、父窗口通过改变子窗口的src中的hash值把一部分信息传入，如果src只有hash部分改变，那么子窗口是不会重新载入的。<br />
2、子窗口可以重写父窗口的location.href，但是注意这里子窗口无法读取而只能重写location.href所以要求前提是您控制两个域名，知道当前父窗口的location.href是什么并写在子窗口内，这样通过parent.location.href = &#8220;已知的父窗口的href&#8221;+&#8221;#&#8221;+hash。这样父窗口只有hash改变也不会重载。<br />
3、上面两步分别做到了两个窗口之间的无刷新数据通知，那么下面的来说如何感知数据变化。标准中没有相关规定，所以当前的任意浏览器遇到location.hash变化都不会触发任何javaScript事件，也就是说您要自己写监听函数来监视loaction.hash的值的变化。做法是通过setTimeout或者setInterval来写一个监听函数每20-100ms查看一下hash是否变化，如果变化了驱动js根据新的数据做想做的事情。</p>
<p>这种实现的一些分析：<br />
1、信息通道是双向的，当然会兼容单向，如果只是父窗口向子窗口通知数据，只需要子窗口写hash监听，反之亦然。<br />
2、局限性也是颇大，因为这种通信的前提是双方知道对方的location.href。如果父窗口带有动态的location.search也就是查询参数，那么子窗口的处理上就比较困难，需要把父窗口的location.search作为传递信息的一部分告知子窗口。<br />
3、另外的困扰会有浏览器带给你，IE之外的浏览器遇到hash的改变会记录历史，这样你在处理前进后退的时候会非常头疼</p>
<p><span style="font-weight: bold">场景三</span>：将www.yyy.com的页面用iframe嵌入到www.xxx.com的中，只有被嵌入的yyy.com在您的控制下，如何在iframe内外进行一定的交流<br />
真实场景：google adsence的一个需求，你希望google发现您的页面不能匹配出相关性非常好的按点击付费广告时，你希望google的广告iframe能够隐藏。<br />
google的广告iframe在google域下显然不能把自己隐藏掉，那么怎么办呢？<br />
1、google会提供给你一个html页面<br />
2、您将这个页面放置在您的域名下，并告诉google它的位置<br />
3、当google发现没有很好的广告时，会将子窗口的loaction重定向到您的那个页面下，这样您的页面因为同域名就可以访问父页面来隐藏自己了<br />
是不是很巧的方法？</p>
<p><span style="font-weight: bold">场景四</span>：您是内容发布商，如何改造接口，让其他域名下的页面可以从浏览器端出发获得您的数据<br />
我们知道ajax的xmlHttpRequest()说到底是一个无刷新请求服务器数据的辅助工具，但是xmlHttpRequest并不能跨域名请求数据，在某些情况下成了极大的限制。<br />
但是我们如果通过其他方式完成无刷新请求数据不也可以么，我们用Dom方法操作动态JS脚本请求来做这件事。<br />
&nbsp;&nbsp;&nbsp; //创建一个脚本节点<br />
&nbsp;&nbsp;&nbsp; var oScript = document.createElement(&#8217;script&#8217;);<br />
&nbsp;&nbsp;&nbsp; //指定脚本src src可以指向任意域名<br />
&nbsp;&nbsp;&nbsp; //注意src不再指向静态js，而是带着查询参数指向一个动态脚本广播服务。<br />
&nbsp;&nbsp;&nbsp; oScript.src = &#8220;http://yyy.com/query.php?&#8221;+yourQueryString;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; //如果指定了charset 同时还可以解决xmlHttpRequest另一大困扰 乱码问题&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; //oScript.charset = &#8220;utf-8&#8243;;<br />
&nbsp;&nbsp;&nbsp; //通过Dom操作把这个新的节点加入到文档当中&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; document.getElementsByTagName(&#8221;head&#8221;)[0].appendChild(oScript);</p>
<p>这样只要query.php的输出是可执行的javaScript脚本，比如：djsCallBack（{jsondata}）;<br />
当他从服务器返回后就会自动执行,你可以方便的用json方式来做数据传递了。<br />
要注意，您的脚本请求最好带上时间戳，避免浏览器缓存造成取回数据实时性下降。</p>
<p>如果您是数据提供者，您可以要求数据索取者在查询参数中提供回调函数名，比如query.php?callback=myDataHandler&amp;key=&#8230;?<br />
这样您就可以根据参数来提供给他myDataHandler({jsondata}),这样不同的数据索取者都会得到自定义的正确的异步回调。</p>
<p><span style="font-weight: bold">场景五</span>：通过后端程序语言，为了跨域名而做各自的后台数据抓取转化服务，比如php curl，YAHOO&nbsp; CHINA NCP就是用这种方案。</p>
<p><span style="font-weight: bold">场景六</span>：通过flash proxy，因为flash的跨域调用可以通过crossdomain.xml和security.allowdomain(&#8217;*')文件实现，而js又可以和flash进行通信，所以js完全可以借用flash</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 实现js跨域通信。</p>
<p>&nbsp;<br />
<span style="font-weight: bold">总结总结</span>：<br />
第一种场景，相应的处理办法有这非常好的效果，可以说完全解决了问题。<br />
第二种场景，相应的处理办法具有一定的跨域数据交流功效，具有相当大的局限，并不适合在复杂业务流程中应用，实际上我也确实也没看到过基于此的大规模应用。<br />
第三种场景，相应的处理办法比较巧妙，虽然redirect之后就不干你什么事了，但如果你是google一样面向众多域名的内容提供商，也是个不错的解决思路。<br />
第四种场景，相应的处理办法非常强大，对比Ajax可以看到，跨域名没问题，无刷新没问题，本身又是异步的，JSON比xml快的多，同时解决乱码问题，只是请求都是Get方式的，不能做Post方式的请求。多一种武器自然可以从容选择了。</p>
<p>第五种场景，处理很方便，也很实用。</p>
<p>第六种场景，需要一定的flash基础哈，作用当然非常强大。</p><img src ="http://www.blogjava.net/hulizhong/aggbug/306196.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-12-16 17:51 <a href="http://www.blogjava.net/hulizhong/archive/2009/12/16/306196.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>细节处见功夫---cookie的应用和处理</title><link>http://www.blogjava.net/hulizhong/archive/2009/12/08/305166.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Tue, 08 Dec 2009 07:03:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/12/08/305166.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/305166.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/12/08/305166.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/305166.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/305166.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cookie在web开发中应用的比较多!浏览器默认的接受cookie的,但是如果浏览器禁止了cookie,会出现什么情况了?<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在此我测试了如下系统:gmail,163邮箱,126邮箱,tom邮箱<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我在<span style="color: red"><strong>浏览器禁用cookie</strong></span>的情况下,登陆上述4邮箱!<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>Gmail:</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;正确输入用户名密码后,系统给出如下提示:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<img height="239" alt="" src="http://www.blogjava.net/images/blogjava_net/hulizhong/gmail_cookie.jpg" width="844" border="0" /><br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>163邮箱:<br />
</strong><br />
&nbsp;正确输入用户名密码后,系统给出如下提示:让我搞不清楚是用户名密码错误还是其它错误!<br />
<br />
<img height="504" alt="" src="http://www.blogjava.net/images/blogjava_net/hulizhong/163_test.jpg" width="844" border="0" /><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>126邮箱<br />
</strong>&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;正确输入用户名密码后,系统给出如下提示:让我搞不清楚是用户名密码错误还是其它错误!<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<img height="482" alt="" src="http://www.blogjava.net/images/blogjava_net/hulizhong/126_test.jpg" width="867" border="0" /><br />
&nbsp;&nbsp;<strong>&nbsp;&nbsp;tom邮箱</strong><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;正确输入用户名密码后,系统给出如下提示:居然提示我是"<strong>非法请求</strong>"<br />
<br />
<img height="349" alt="" src="http://www.blogjava.net/images/blogjava_net/hulizhong/tom_test.jpg" width="815" border="0" /></p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从上面测试中感受到不同的用户体验.对大多数做web开发的人来说,<a href="http://www.blogjava.net/hulizhong/archive/2009/12/07/304955.html">判断浏览器是否支持Cookie</a>并不是什么难事!难的是对细节的处理!<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>也许我们与优秀的产品差距最大的是对细节的处理!<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;附: 我测试用的是IE6.0</p><img src ="http://www.blogjava.net/hulizhong/aggbug/305166.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-12-08 15:03 <a href="http://www.blogjava.net/hulizhong/archive/2009/12/08/305166.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 回忆我们经典的开发工具</title><link>http://www.blogjava.net/hulizhong/archive/2009/10/20/299010.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Tue, 20 Oct 2009 05:04:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/10/20/299010.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/299010.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/10/20/299010.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/299010.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/299010.html</trackback:ping><description><![CDATA[转 http://blog.csdn.net/gaoweipeng/archive/2009/10/18/4695780.aspx<br />
平时的开发如果我们能有些得心应手的开发工具，就好比是如虎添翼。会大大的提高我们的开发效率。Visual Studio 自不必说，通过此文，和大家回忆下除此之外的经典的开发工具，同时希望能把这些经典的工具介绍给新手，相信对平日的开发会有很大的帮助。<br />
<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/193608/o_comment.gif" border="0" height="24" width="32" /><strong style="font-size: 12pt;">Internet Explorer Developer Toolbar</strong><br style="font-size: 12pt;" />
<span style="color: red;">简介</span>：微软发布了Internet Explorer Developer Toolbar最新版.该产品让开发人员能够深入探索和理解Web页面,帮助开发者更好地创建Web应用.安装后可以在IE中快速分析网页的软件.该工具条可集成在IE窗口,或以浮动窗口形式存在.<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/ittoolbar.gif" height="274" width="1273" /><br />
IE Developer Toolbar特性如下:<br />
<p>浏览和修改Web页的文档对象模型(DOM).通过多种技术方式定位、选定Web页上的特定元素.禁止或激活IE设置.查看HTML对象的类名、
ID,以及类似链接路径、tab顺序、快捷键等细节.描绘表格、单元格、图片或选定标签的轮廓.显示图片象素、大小、路径、替代文字等.即时重定义浏览器
窗口大小到800x600或自定义大小.清空浏览器缓存和cookie,被清除项可从所有对象或给定域中选择.直接访问关联W3C规范参考、IE开发组
blog或其他来源.－显示设计时标尺,帮助对齐对象. ....&nbsp;<br />
</p>
<p><span style="color: red; font-family: 宋体;">下载地址及相关资料：</span><a href="http://www.skycn.com/soft/24101.html" target="_blank"><br />
http://www.microsoft.com/downloads/details.aspx?FamilyID=e59c3964-672d-4511-bb3e-2d5e1db91038&amp;DisplayLang=en#Overview</a></p>
<p><br />
</p>
<p><img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/193608/o_comment.gif" border="0" height="24" width="32" /><strong style="font-size: 12pt;">HttpWatch</strong><br style="font-size: 12pt;" />
<span style="color: red;">简介</span>：强大的<u>网页数据分析工具</u>。集成在Internet Explorer工具栏。包括网页摘要。Cookies管理。缓存管理。消息头发送/接受。字符查询。POST 数据和目录管理功能。报告输出。</p>
<p>安装完HttpWatch后，就会在浏览器中找到他：<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/httpwatch2.gif" border="0" height="178" width="404" /><br />
主界面：<br />
<img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/httpwatch.gif" border="0" height="326" width="927" />&nbsp;<br />
<span style="color: red;">下载地址：</span><a href="http://www.crsky.com/soft/3455.html" target="_blank">http://www.crsky.com/soft/3455.html</a></p>
<p><span style="color: #ff0000;">使</span><span style="color: #ff0000;">用介绍：</span><a href="http://www.cnblogs.com/mayingbao/archive/2007/11/30/978530.html" target="_blank">http://www.cnblogs.com/mayingbao/archive/2007/11/30/978530.html</a> </p>
<p><br />
</p>
<p><img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/193608/o_comment.gif" border="0" height="24" width="32" /><strong style="font-size: 12pt;">Fiddler2</strong></p>
<p><span style="color: red;">简介</span>：Fiddler2是一个<u>网络调试代理</u>，用来监测本地计算
机和Internet之间所有的HTTP通讯。可以监测所有的HTTP通讯，设置断点，并且可以修改到进入到本地计算机的数据和从本地计算机出去的数据
（就是可以伪造数据）。Fiddler包含一个JScript .NET 事件脚本子系统（event-based scripting
subsystem），可以使用任何一种.Net语言扩展。该软件是免费的，支持多种浏览器，包括Internet Explorer，Mozilla
Firefox，Opera和其它一些浏览器。</p>
<p><img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/fiddle2.gif" height="381" width="790" /><br />
</p>
<p>从Fiddler官方网站上可以看见原版的英文介绍：<a title="http://www.fiddler2.com/fiddler2/" href="http://www.fiddler2.com/fiddler2/" target="_blank">http://www.fiddler2.com/fiddler2/</a><br />
<span style="color: red;">下载地址：</span><a title="http://www.fiddler2.com/fiddler2/" href="http://www.fiddler2.com/fiddler2/" target="_blank">http://www.fiddler2.com/fiddler2/</a></p>
<p><span style="color: red;">使用介绍：</span><a href="http://blog.csdn.net/lihongzhai/archive/2009/09/14/4551035.aspx" target="_blank">http://blog.csdn.net/lihongzhai/archive/2009/09/14/4551035.aspx</a> </p>
<p><br />
</p>
<p><img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/193608/o_comment.gif" border="0" height="24" width="32" /><strong style="font-size: 12pt;">NUnit&nbsp;</strong></p>
<p>NUnit是一个<u>单元测试框架</u>，专门针对于。NET来写的.其实在前面有JUnit(Java)，CPPUnit(C++)，他们都是xUnit的一员。最初它是从JUnit而来。现在的版本是2.2.接下来我所用的都是基于这个版本。</p>
<p><img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/NUnit.1.jpg" height="315" width="554" /></p>
<p><span style="color: red;">下载地址：</span><a href="http://www.nunit.org/index.php" target="_blank">http://www.nunit.org/index.php</a></p>
<p><span style="color: red;">使用介绍：</span><a href="http://www.uml.org.cn/net/200702273.asp" target="_blank">http://www.uml.org.cn/net/200702273.asp</a> </p>
<p><br />
</p>
<p><img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/193608/o_comment.gif" border="0" height="24" width="32" /><strong><span style="font-size: 12pt;">PowerDesigner</span></strong><br />
</p>
<p><span style="color: red;">简介：</span>PowerDesigner是Sybase公司的CASE工具集，使用它可以方便地对管理信息系统进行分析设计，它几乎包括了<u>数据库模型设计</u>的全过程。<br />
利用PowerDesigner可以制作数据流程图、概念数据模型、物理数据模型，可以生成多种客户端开发工具的应用程序，还可为数据仓库制作结构模型，也能对团队设计模型进行控制。<br />
</p>
<p>主要功能：DataArchitect：这是一个强大的数据库设计工具，使用DataArchitect
可利用实体-关系图为一个信息系统。创建"概念数据模型"－CDM（Conceptual Data Model）。并且可根据CDM
产生基于某一特定数据库管理系统（例如：Sybase System 11）的"物理数据模型"-PDM(Physical Data
Model)。还可优化PDM，产生为特定DBMS 创建数据库的SQL 语句并可以文件形式存储以便在其他时刻运行这些SQL
语句创建数据库。另外，DataArchitect还可根据已存在的数据库反向生成<br />
PDM，CDM 及创建数据库的SQL脚本。<br />
ProcessAnalyst：这部分用于创建功能模型和数据流图，创建"处理层次关系"。<br />
AppModeler：为客户/服务器应用程序创建应用模型。<br />
ODBC Administrator：此部分用来管理系统的各种数据源。<br />
</p>
<p><img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/PD_03_440.jpg" height="539" width="752" /><br />
</p>
<p><span style="color: red;">下载地址：</span><a href="http://www.baidu.com/s?tn=sitehao123&amp;bs=PowerDesigner&amp;f=3&amp;wd=powerdesigner%CF%C2%D4%D8&amp;oq=powerdesigner&amp;rsp=0" target="_blank">http://www.baidu.com/s?tn=sitehao123&amp;bs=PowerDesigner&amp;f=3&amp;wd=powerdesigner%CF%C2%D4%D8&amp;oq=powerdesigner&amp;rsp=0</a> ：)<br />
</p>
<p><span style="color: red;">使用介绍： </span><a href="http://www.cnblogs.com/yxonline/archive/2007/04.html" target="_blank">http://www.cnblogs.com/yxonline/archive/2007/04.html</a> <br />
</p>
<p><br />
</p>
<p><img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/193608/o_comment.gif" border="0" height="24" width="32" />&nbsp;<strong style="font-size: 12pt;">Reflector</strong></p>
<p><span style="color: red;">简介：</span>Visual Studio内置的ILDASM成为最初<u>挖掘程序集的上佳工具</u>。
但自从Reflector出现后，ILDASM相形见拙。因为，Reflector能提供更多的程序集信息。Reflector可以将.NET程序集中的
中间语言反编译成C#或者Visual Basic代码。除了能将IL转换为C#或Visual
Basic以外，Reflector还能够提供程序集中类及其成员的概要信息、提供查看程序集中IL的能力以及提供对第三方插件的支持。<img alt="" src="http://www.cnblogs.com/gaoweipeng/admin/file:///C:/DOCUME%7E1/ADMINI%7E1/LOCALS%7E1/Temp/moz-screenshot.png" /></p>
<p><img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/reflector.JPG" height="465" width="778" /><br />
</p>
<p><span style="color: red;">下载地址</span><span style="color: red;">： </span><a href="http://files.cnblogs.com/gaoweipeng/Reflector.rar" target="_blank">/Files/gaoweipeng/Reflector.rar</a></p>
<p><span style="color: red;">使用介绍及其他相关下载：</span><a href="http://www.cnblogs.com/zzy2740/archive/2005/09/20/240216.html" target="_blank">http://www.cnblogs.com/zzy2740/archive/2005/09/20/240216.html</a></p>
<p><img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/193608/o_comment.gif" border="0" height="24" width="32" /><strong style="font-size: 12pt;">1st JavaScript Editor</strong></p>
<p><span style="color: red;">简介：</span> 1st JavaScript Editor 是一款强大的<u>JavaScript开发、校验和调试工具</u>，
它简单易用，不论你是初学者或者是专业的开发人士，都可以轻松上手！同时它又是完美的Ajax (Asynchronous JavaScript
and XML),CSS, HTML, DOM DHTML开发工具！它提供了简单而且实用的功能：丰富的代码编辑功能(JavaScript,
HTML, CSS, VBScript, PHP ,ASP(Net))，语法高亮，内置预览功能，提供了完整的HTML 标记, HTML 属性,
HTML 事件, JavaScript 事件和JavaScript 函数等完整的代码库，同时有着贴心的代码自动补全功能。<br />
</p>
<p><img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/jsEdit.JPG" height="470" width="698" /><br />
</p>
<p>官网及下载地址：<span style="font-size: 10.5pt; font-family: 'Times New Roman';"><a href="http://yaldex.com/" target="_blank">http://yaldex.com/</a></span><br />
</p>
<p><img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/193608/o_comment.gif" border="0" height="24" width="32" /><strong style="font-size: 12pt;">Regulator</strong></p>
<p><span style="color: red;">简介</span>：使用Reglator可以方便的验证<u>正则表达式的执行结果</u>，带有智能感知功能。帮助文档是一个很好的学习正则的入门教程，也可以当作参考手册。基本上就全了，同时进行学习和实践。还有生成c# or vb.net代码等附加功能。 类似的软件现在很多：RegexBuddy ，RegexTester。等<br />
</p>
<p><img alt="" src="http://images.cnblogs.com/cnblogs_com/gaoweipeng/regulator_main_1245339666.png" height="424" width="682" /></p>
<p>&nbsp;<span style="color: red;">下载地址：</span><a title="" href="http://sourceforge.net/projects/regulator/" target="_blank">http://sourceforge.net/projects/regulator/</a></p>
<p>&nbsp;<span style="color: red;">使用介绍：</span><a href="http://www.ctochina.net/topic/ShowArticle/112.html" target="_blank">http://www.ctochina.net/topic/ShowArticle/112.html</a></p>
<p>&nbsp;小结：上面的开发工具都是我平时喜欢用的，希望通过此文的介绍，对没有使用过的朋友带来帮助。也希望园子中的朋友们<strong>秀一秀自己平时常用的开发工具</strong>，分享些更实用，方便的开发工具！</p>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/299010.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-10-20 13:04 <a href="http://www.blogjava.net/hulizhong/archive/2009/10/20/299010.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 HTTP状态码（HTTP Status codes）简介 </title><link>http://www.blogjava.net/hulizhong/archive/2009/05/06/269205.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Wed, 06 May 2009 03:38:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/05/06/269205.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/269205.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/05/06/269205.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/269205.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/269205.html</trackback:ping><description><![CDATA[转 http://www.fboss.com/article.asp?id=816<br />
<br />
近来总有朋友咨询cPanel 的Awstats中&#8220;HTTP错误码（HTTP Error codes）&#8221;的含义，以及是否需要关注和处理。<br />
关于Awstats请查看《CP How-To：如何使用cPanel查看站点统计数据（awstats）》 文章地址： <a href="http://bbs.netpu.net/viewthread.php" target="_blank" rel="external">http://bbs.netpu.net/viewthread.php</a> tid=694 其实这是一个误会，在这里它应该是&#8220;HTTP状态码（HTTP Status codes）&#8221;。<br />
OK，既然是状态码，那么就可能有正确和错误两种状态了（至少不全是错误了，大大的松口气吧）。那么这些代码都代表什么含义呢？到底哪些是错误状态，哪些是正确状态呢？不要急，下边我冒充内行为大家做一个简单的介绍。<br />
HTTP与Status codes<br />
HTTP可能大家都熟悉，就是超文本传输协议。浏览器通过HTTP与WEB
Server通讯（也有一些其它软件比如IM使用HTTP协议传递数据），把我们的请求（HTTP
Request）传递给服务器，服务器响应这个请求，返回应答（HTTP Response）以及我们需要的数据。大致就是这个样子了。<br />
如果
我们请求啥，服务器就返回啥，是乎就不需要HTTP Status
codes了。但是事情往往不是那么简单。比如我们请求一个网页页面，可是服务器不存在这个页面，或者这个页面被转移到其它地方，或者服务器禁止我们查看
这个页面等等，所以为了便于浏览器处理这些正确与不正确的情况，HTTP用Status codes来表示请求（HTTP
Request）在服务器端被处理的情况。Status codes通过应答（HTTP Response）返回给浏览器，浏览器根据这个作相应的处理。<br />
HTTP Status codes的分类<br />
既然有正确和错误的状态，HTTP定义两大类状态码是不是就可以了？人家制订协议的可是专家（不象我是冒充的），想得比我们要周全，要长远。HTTP 1.1中定义了5大类Status codes，分别是：<br />
Informational 意义：信息 范围：1XX<br />
Successful 意义：成功 范围：2XX<br />
Redirection 意义：重定向 范围：3XX<br />
Client Error 意义：客户端错误 范围：4XX<br />
Server Error 意义：服务器错误 范围：5XX<br />
您看看人家想得多周到啊，真专家就是真专家。<br />
常见HTTP Status codes简介<br />
下面简单介绍一下我们经常碰到的HTTP Status codes。<br />
也许是我孤陋寡闻，常遇到的HTTP Status codes就那么几个，见笑啦。<br />
Successful 200 - OK：OK
这个是最常见的啦（也许我们不会直接看到，但是如果您使用一些抓包工具，大多数http应答中都有这个）。意义很简单，就是说服务器收到并理解客户端的请
求而且正常处理了。 206 - Partial Content：部分内容。 这个也经常发生。很容易让大家发懵。
通俗点说就是如果客户端请求文档（图像，文本，声音等等）的部分内容，服务器正常处理，那么就返回206。大致意思就是它请求的时候，除了指定请求的内
容，还指定了偏移量以及长度。 部分内容，没搞错吧？呵呵没搞错，现在很多浏览器以及软件支持断点续传就是靠这个的。呵呵，以后看到206不要怕了。<br />
Redirection 301 - Moved Permanently：永久移动。
这个状态码是指客户端要请求的资源已经被永久的转移到一个新的地方了。这个应答（HTTP
Response）里边同时包含了资源的新地址。它告诉客户端，如果下次还想要这个资源，那么就用新的地址去取。 302 Found：临时重定向。
这个状态码是指客户端要请求的资源临时放到一个新地方了。同样，应答中也包含了资源的新地址。 307 - Temporary
Redirect：临时重定向。（如果不去实现协议或者做相关开发，我们大致理解它很302差不多就可以啦）
有一篇谈重定向的文章《从Business.com遭封看302重定向》很有意思 我们转载了一份，地址： <a href="http://bbs.netpu.net/viewthread.php" target="_blank" rel="external">http://bbs.netpu.net/viewthread.php</a>
tid=810 Client Error 400 - Bad Request：错误请求 就是请求的语法错误，服务器无法理解。 401 &#8211;
Unauthorized：未授权 当服务器返回401 Code，就是告诉说客户端访问指定资源以前，必须通过服务器的授权。 403 &#8211;
Forbidden：禁止访问 就是不允许访问某些资源。 404 - Not Found：找不到 找不到客户端请求的内容<br />
Server Error 500 - Internal Server Error 服务器内部错误。<br />
结束语<br />
越写越懒，所以就简简单单写这么多啦。没有啥大用处。如果能消除一两位关于这方面朋友的疑虑，就足以令我欣慰了。既然之前都说过是冒充内行，所以有错漏之处在所难免，还望大家不吝赐教。<br />
需要深入研究这方面内容的朋友，千万不要看这篇文章，以免被此文误导。请学习官方的协议内容。 官方的资料地址:<br />
<a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html" target="_blank" rel="external">http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html</a>
<br />
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/269205.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-05-06 11:38 <a href="http://www.blogjava.net/hulizhong/archive/2009/05/06/269205.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 利用JExcelApi来动态生成excel文档 </title><link>http://www.blogjava.net/hulizhong/archive/2009/04/28/267825.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Tue, 28 Apr 2009 01:02:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/04/28/267825.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/267825.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/04/28/267825.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/267825.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/267825.html</trackback:ping><description><![CDATA[转 http://www.blogjava.net/eamoi/archive/2005/01/14/299.html<br />
<br />
<p>首先，请到<a href="http://www.andykhan.com/jexcelapi/index.html">http://www.andykhan.com/jexcelapi/index.html</a>下载java excel api，主页上同时有比较详细的介绍。最新版本为2.4.3，同时也可以到：<a href="http://www.andykhan.com/jexcelapi/jexcelapi_2_4_3.tar.gz">http://www.andykhan.com/jexcelapi/jexcelapi_2_4_3.tar.gz</a>下载到该最新版的API，由于该项目是开源的，所以下载的文件中已经包含了源代码，同样的，文件中也有javadoc，大家在开发中可以参考javadoc。</p>
<p>下载完毕后，我们需要把文件中的jxl.jar加入到你的开发classpath中。<br />
下图是现在要生产的excel截图：<br />
<a href="http://blog.csdn.net/beming/gallery/image/3437.aspx">http://blog.csdn.net/beming/gallery/image/3437.aspx</a><br />
</p>
<p>代码如下：</p>
<p>&nbsp;&nbsp;&nbsp;File excel = new File("d:/aming.xls");<br />
&nbsp;&nbsp;&nbsp;if(!excel.exists()){<br />
&nbsp;&nbsp;&nbsp;&nbsp;excel.createNewFile();<br />
&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;WritableWorkbook wwb = Workbook.createWorkbook(excel);<br />
&nbsp;&nbsp;&nbsp;WritableSheet ws = wwb.createSheet("testexcel",0);<br />
&nbsp;&nbsp;&nbsp;Label lable = null;<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;//对中文的支持非常好<br />
&nbsp;&nbsp;&nbsp;lable = new Label(0,0,"我的中国心");<br />
&nbsp;&nbsp;&nbsp;ws.addCell(lable);<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;//可以定义模板格式化你的cell<br />
&nbsp;&nbsp;&nbsp;WritableFont
wf = new
WritableFont(WritableFont.ARIAL,10,WritableFont.NO_BOLD,false,UnderlineStyle.NO_UNDERLINE,
Colour.BLACK);<br />
&nbsp;&nbsp;&nbsp;WritableCellFormat wcf = new WritableCellFormat(wf);<br />
&nbsp;&nbsp;&nbsp;wcf.setBackground(Colour.WHITE);<br />
&nbsp;&nbsp;&nbsp;lable = new Label(0,1,"fdsl",wcf);<br />
&nbsp;&nbsp;&nbsp;ws.addCell(lable);<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;wf = new WritableFont(WritableFont.TIMES,18,WritableFont.BOLD,true);<br />
&nbsp;&nbsp;&nbsp;wcf = new WritableCellFormat(wf);<br />
&nbsp;&nbsp;&nbsp;lable = new Label(0,2,"aming",wcf);<br />
&nbsp;&nbsp;&nbsp;ws.addCell(lable);<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;//cell的类型同样可以定义为数字类型<br />
&nbsp;&nbsp;&nbsp;Number nb = new Number(0,3,21.4321321);<br />
&nbsp;&nbsp;&nbsp;ws.addCell(nb);<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;//支持格式化你的数字串<br />
&nbsp;&nbsp;&nbsp;NumberFormat nf = new NumberFormat("#.###");<br />
&nbsp;&nbsp;&nbsp;wcf = new WritableCellFormat(nf);<br />
&nbsp;&nbsp;&nbsp;nb = new Number(0,4,21.43254354354354,wcf);<br />
&nbsp;&nbsp;&nbsp;ws.addCell(nb);</p>
<p>&nbsp;&nbsp;&nbsp;//cell的类型可以为boolean类型<br />
&nbsp;&nbsp;&nbsp;Boolean bl = new Boolean(0,5,true);<br />
&nbsp;&nbsp;&nbsp;ws.addCell(bl);</p>
<p>&nbsp;&nbsp;&nbsp;//cell的类型同样可以为日期，时间<br />
&nbsp;&nbsp;&nbsp;DateTime dt = new DateTime(0,6,new Date());<br />
&nbsp;&nbsp;&nbsp;ws.addCell(dt);</p>
<p>&nbsp;&nbsp;&nbsp;//并且可以很好格式化你的日期格式<br />
&nbsp;&nbsp;&nbsp;DateFormat df = new DateFormat("MM dd yyyy hh:mm:ss");<br />
&nbsp;&nbsp;&nbsp;wcf = new WritableCellFormat(df);<br />
&nbsp;&nbsp;&nbsp;dt = new DateTime(0,7,new Date(),wcf);<br />
&nbsp;&nbsp;&nbsp;ws.addCell(dt);<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;//开始写文件了<br />
&nbsp;&nbsp;&nbsp;wwb.write();<br />
&nbsp;&nbsp;&nbsp;wwb.close();<br />
<br />
上面的下载地址无法打开.<br />
下载请到:http://prdownloads.sourceforge.net/jexcelapi</p>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/267825.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-04-28 09:02 <a href="http://www.blogjava.net/hulizhong/archive/2009/04/28/267825.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 web.xml配置详细说明(下)</title><link>http://www.blogjava.net/hulizhong/archive/2009/04/24/267404.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Fri, 24 Apr 2009 09:17:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/04/24/267404.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/267404.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/04/24/267404.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/267404.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/267404.html</trackback:ping><description><![CDATA[转 http://blog.csdn.net/fenglibing/archive/2009/03/19/4005449.aspx<br />
<br />
<p>5.2 分配JSP初始化参数 <br />
给JSP页面提供初始化参数在三个方面不同于给servlet提供初始化参数。 <br />
1）使用jsp-file而不是servlet-class。因此，WEB-INF/web.xml文件的servlet元素如下所示：</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;PageName&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;jsp-file&gt;/RealPage.jsp&lt;/jsp-file&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-name&gt;...&lt;/param-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-value&gt;...&lt;/param-value&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>... &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;
&lt;jsp-file&gt;/RealPage.jsp&lt;/jsp-file&gt;
&lt;init-param&gt;
&lt;param-name&gt;...&lt;/param-name&gt;
&lt;param-value&gt;...&lt;/param-value&gt;
&lt;/init-param&gt;
...
&lt;/servlet&gt;</pre>
<p>2)
几乎总是分配一个明确的URL模式。对servlet，一般相应地使用以http://host/webAppPrefix/servlet/
开始的缺省URL。只需记住，使用注册名而不是原名称即可。这对于JSP页面在技术上也是合法的。例如，在上面给出的例子中，可用URL
http://host/webAppPrefix/servlet/PageName
访问RealPage.jsp的对初始化参数具有访问权的版本。但在用于JSP页面时，许多用户似乎不喜欢应用常规的servlet的URL。此外，如果
JSP页面位于服务器为其提供了目录清单的目录中（如，一个既没有index.html也没有index.jsp文件的目录），则用户可能会连接到此
JSP页面，单击它，从而意外地激活未初始化的页面。因此，好的办法是使用url-pattern（5.3节）将JSP页面的原URL与注册的
servlet名相关联。这样，客户机可使用JSP页面的普通名称，但仍然激活定制的版本。例如，给定来自项目1的servlet定义，可使用下面的
servlet-mapping定义：</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;PageName&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/RealPage.jsp&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet-mapping&gt;
&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;
&lt;url-pattern&gt;/RealPage.jsp&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</pre>
<p>3）JSP页使用jspInit而不是init。自动从JSP页面建立的servlet或许已经使用了inti方法。因此，使用JSP声明提供一个init方法是不合法的，必须制定jspInit方法。 <br />
为
了说明初始化JSP页面的过程，程序清单5-9给出了一个名为InitPage.jsp的JSP页面，它包含一个jspInit方法且放置于
deployDemo Web应用层次结构的顶层。一般，http://host/deployDemo/InitPage.jsp
形式的URL将激活此页面的不具有初始化参数访问权的版本，从而将对firstName和emailAddress变量显示null。但是，
web.xml文件（程序清单5-10）分配了一个注册名，然后将该注册名与URL模式/InitPage.jsp相关联。 <br />
<br />
程序清单5-9 InitPage.jsp</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;!DOCTYPE HTML PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//W3C//DTD HTML 4.0 Transitional//EN"</span></span><span>&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;HTML&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;HEAD&gt;&lt;TITLE&gt;JSP Init Test&lt;/TITLE&gt;&lt;/HEAD&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;BODY BGCOLOR=</span><span class="string"><span style="color: #0000ff;">"#FDF5E6"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;H2&gt;Init Parameters:&lt;/H2&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;UL&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;LI&gt;First name: &lt;%= firstName %&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;LI&gt;Email address: &lt;%= emailAddress %&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/UL&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/BODY&gt;&lt;/HTML&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;%! &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">private</span></strong></span><span> String firstName, emailAddress; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> jspInit() { &nbsp;&nbsp;</span> </li>
    <li><span>ServletConfig config = getServletConfig(); &nbsp;&nbsp;</span> </li>
    <li><span>firstName = config.getInitParameter(</span><span class="string"><span style="color: #0000ff;">"firstName"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>emailAddress = config.getInitParameter(</span><span class="string"><span style="color: #0000ff;">"emailAddress"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>%&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"&gt;
&lt;HTML&gt;
&lt;HEAD&gt;&lt;TITLE&gt;JSP Init Test&lt;/TITLE&gt;&lt;/HEAD&gt;
&lt;BODY BGCOLOR="#FDF5E6"&gt;
&lt;H2&gt;Init Parameters:&lt;/H2&gt;
&lt;UL&gt;
&lt;LI&gt;First name: &lt;%= firstName %&gt;
&lt;LI&gt;Email address: &lt;%= emailAddress %&gt;
&lt;/UL&gt;
&lt;/BODY&gt;&lt;/HTML&gt;
&lt;%!
private String firstName, emailAddress;
public void jspInit() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter("firstName");
emailAddress = config.getInitParameter("emailAddress");
}
%&gt;</pre>
<p><br />
<br />
程序清单5-10 web.xml（说明JSP页面的init参数的摘录）</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;?xml version=</span><span class="string"><span style="color: #0000ff;">"1.0"</span></span><span> encoding=</span><span class="string"><span style="color: #0000ff;">"ISO-8859-1"</span></span><span>?&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;!DOCTYPE web-app &nbsp;&nbsp;</span> </li>
    <li><span>PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"</span></span><span>&nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"http://java.sun.com/dtd/web-app_2_3.dtd"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span>&lt;web-app&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;InitPage&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;jsp-file&gt;/InitPage.jsp&lt;/jsp-file&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-name&gt;firstName&lt;/param-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-value&gt;Bill&lt;/param-value&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-name&gt;emailAddress&lt;/param-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-value&gt;gates</span><span class="annotation"><span style="color: #646464;">@oracle</span></span><span>.com&lt;/param-value&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt; InitPage&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/InitPage.jsp&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/web-app&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
&lt;!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;
&lt;web-app&gt;
&lt;!-- ... --&gt;
&lt;servlet&gt;
&lt;servlet-name&gt;InitPage&lt;/servlet-name&gt;
&lt;jsp-file&gt;/InitPage.jsp&lt;/jsp-file&gt;
&lt;init-param&gt;
&lt;param-name&gt;firstName&lt;/param-name&gt;
&lt;param-value&gt;Bill&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;init-param&gt;
&lt;param-name&gt;emailAddress&lt;/param-name&gt;
&lt;param-value&gt;gates@oracle.com&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;/servlet&gt;
&lt;!-- ... --&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt; InitPage&lt;/servlet-name&gt;
&lt;url-pattern&gt;/InitPage.jsp&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
&lt;!-- ... --&gt;
&lt;/web-app&gt;</pre>
<p><br />
<br />
5.3 提供应用范围内的初始化参数 <br />
一般，对单个地servlet或JSP页面分配初始化参数。指定的servlet或
JSP页面利用ServletConfig的getInitParameter方法读取这些参数。但是，在某些情形下，希望提供可由任意servlet或
JSP页面借助ServletContext的getInitParameter方法读取的系统范围内的初始化参数。 <br />
可利用context-param元素声明这些系统范围内的初始化值。context-param元素应该包含param-name、param-value以及可选的description子元素，如下所示： <br />
&lt;context-param&gt; <br />
&lt;param-name&gt;support-email&lt;/param-name&gt; <br />
&lt;param-value&gt;blackhole@mycompany.com&lt;/param-value&gt; <br />
&lt;/context-param&gt; <br />
可
回忆一下，为了保证可移植性，web.xml内的元素必须以正确的次序声明。但这里应该注意，context-param元素必须出现任意与文档有关的元
素（icon、display-name或description）之后及filter、filter-mapping、listener或
servlet元素之前。 <br />
5.4 在服务器启动时装载servlet <br />
假如servlet或JSP页面有一个要花很长时间执行的
init
（servlet）或jspInit（JSP）方法。例如，假如init或jspInit方法从某个数据库或ResourceBundle查找产量。这种
情况下，在第一个客户机请求时装载servlet的缺省行为将对第一个客户机产生较长时间的延迟。因此，可利用servlet的load-on-
startup元素规定服务器在第一次启动时装载servlet。下面是一个例子。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt; &#8230; &lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &#8230; &lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &lt;!-- or jsp-file --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;load-on-startup/&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt; &#8230; &lt;/servlet-name&gt;
&lt;servlet-class&gt; &#8230; &lt;/servlet-class&gt; &lt;!-- or jsp-file --&gt;
&lt;load-on-startup/&gt;
&lt;/servlet&gt;</pre>
<p>可以为此元素体提供一个整数而不是使用一个空的load-on-startup。想法是服务器应该在装载较大数目的servlet或JSP页面之前
装载较少数目的servlet或JSP页面。例如，下面的servlet项（放置在Web应用的WEB-INF目录下的web.xml文件中的web-
app元素内）将指示服务器首先装载和初始化SearchServlet，然后装载和初始化由位于Web应用的result目录中的index.jsp文
件产生的 servlet。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;Search&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;myPackage.SearchServlet&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &lt;!-- or jsp-file --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;load-on-startup&gt;</span><span class="number"><span style="color: #c00000;">1</span></span><span>&lt;/load-on-startup&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;Results&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;/results/index.jsp&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &lt;!-- or jsp-file --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;load-on-startup&gt;</span><span class="number"><span style="color: #c00000;">2</span></span><span>&lt;/load-on-startup&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt;Search&lt;/servlet-name&gt;
&lt;servlet-class&gt;myPackage.SearchServlet&lt;/servlet-class&gt; &lt;!-- or jsp-file --&gt;
&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
&lt;/servlet&gt;
&lt;servlet&gt;
&lt;servlet-name&gt;Results&lt;/servlet-name&gt;
&lt;servlet-class&gt;/results/index.jsp&lt;/servlet-class&gt; &lt;!-- or jsp-file --&gt;
&lt;load-on-startup&gt;2&lt;/load-on-startup&gt;
&lt;/servlet&gt;</pre>
<p><br />
<span style="text-decoration: underline;">6 声明过滤器</span> <br />
<br />
servlet版本2.3引入了过滤器的概念。虽然所有支持servlet API版本2.3的服务器都支持过滤器，但为了使用与过滤器有关的元素，必须在web.xml中使用版本2.3的DTD。 <br />
过
滤器可截取和修改进入一个servlet或JSP页面的请求或从一个servlet或JSP页面发出的相应。在执行一个servlet或JSP页面之前，
必须执行第一个相关的过滤器的doFilter方法。在该过滤器对其FilterChain对象调用doFilter时，执行链中的下一个过滤器。如果没
有其他过滤器，servlet或JSP页面被执行。过滤器具有对到来的ServletRequest对象的全部访问权，因此，它们可以查看客户机名、查找
到来的cookie等。为了访问servlet或JSP页面的输出，过滤器可将响应对象包裹在一个替身对象（stand-in
object）中，比方说把输出累加到一个缓冲区。在调用FilterChain对象的doFilter方法之后，过滤器可检查缓冲区，如有必要，就对它
进行修改，然后传送到客户机。 <br />
例如，程序清单5-11帝国难以了一个简单的过滤器，只要访问相关的servlet或JSP页面，它就截取请求并在标准输出上打印一个报告（开发过程中在桌面系统上运行时，大多数服务器都可以使用这个过滤器）。 <br />
<br />
程序清单5-11 ReportFilter.java</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span class="keyword"><strong><span style="color: #7f0055;">package</span></strong></span><span> moreservlets; &nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> java.io.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.http.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> java.util.*; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="comment"><span style="color: #008200;">/** Simple filter that prints a report on the standard output </span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* whenever the associated servlet or JSP page is accessed. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &lt;P&gt; </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* Taken from More Servlets and JavaServer Pages </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* from Prentice Hall and Sun Microsystems Press, </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* http://www.moreservlets.com/. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &#169; 2002 Marty Hall; may be freely used or adapted. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">*/</span></span><span>&nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span> ReportFilter </span><span class="keyword"><strong><span style="color: #7f0055;">implements</span></strong></span><span> Filter { &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> doFilter(ServletRequest request, &nbsp;&nbsp;</span> </li>
    <li><span>ServletResponse response, &nbsp;&nbsp;</span> </li>
    <li><span>FilterChain chain) &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">throws</span></strong></span><span> ServletException, IOException { &nbsp;&nbsp;</span> </li>
    <li><span>HttpServletRequest req = (HttpServletRequest)request; &nbsp;&nbsp;</span> </li>
    <li><span>System.out.println(req.getRemoteHost() + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">" tried to access "</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span>req.getRequestURL() + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">" on "</span></span><span> + </span><span class="keyword"><strong><span style="color: #7f0055;">new</span></strong></span><span> Date() + </span><span class="string"><span style="color: #0000ff;">"."</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>chain.doFilter(request,response); &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> init(FilterConfig config) &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">throws</span></strong></span><span> ServletException { &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> destroy() {} &nbsp;&nbsp;</span> </li>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">package moreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
/** Simple filter that prints a report on the standard output
* whenever the associated servlet or JSP page is accessed.
* &lt;P&gt;
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* &#169; 2002 Marty Hall; may be freely used or adapted.
*/
public class ReportFilter implements Filter {
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest)request;
System.out.println(req.getRemoteHost() +
" tried to access " +
req.getRequestURL() +
" on " + new Date() + ".");
chain.doFilter(request,response);
}
public void init(FilterConfig config)
throws ServletException {
}
public void destroy() {}
}</pre>
<p>一旦建立了一个过滤器，可以在web.xml中利用filter元素以及filter-name（任意名称）、file-class（完全限定的类
名）和（可选的）init-params子元素声明它。请注意，元素在web.xml的web-app元素中出现的次序不是任意的；允许服务器（但不是必
需的）强制所需的次序，并且实际中有些服务器也是这样做的。但这里要注意，所有filter元素必须出现在任意filter-mapping元素之前，
filter-mapping元素又必须出现在所有servlet或servlet-mapping元素之前。 <br />
例如，给定上述的ReportFilter类，可在web.xml中作出下面的filter声明。它把名称Reporter与实际的类ReportFilter（位于moreservlets程序包中）相关联。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;filter&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;filter-name&gt;Reporter&lt;/filter-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;filter-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moresevlets.ReportFilter&lt;/filter-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/filter&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;filter&gt;
&lt;filter-name&gt;Reporter&lt;/filter-name&gt;
&lt;filter-class&gt;moresevlets.ReportFilter&lt;/filter-class&gt;
&lt;/filter&gt;</pre>
<p>一旦命名了一个过滤器，可利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。关于此项工作有两种选择。 <br />
首
先，可使用filter-name和servlet-name子元素把此过滤器与一个特定的servlet名（此servlet名必须稍后在相同的
web.xml文件中使用servlet元素声明）关联。例如，下面的程序片断指示系统只要利用一个定制的URL访问名为SomeServletName
的servlet或JSP页面，就运行名为Reporter的过滤器。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;filter-mapping&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;filter-name&gt;Reporter&lt;/filter-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;SomeServletName&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/filter-mapping&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;filter-mapping&gt;
&lt;filter-name&gt;Reporter&lt;/filter-name&gt;
&lt;servlet-name&gt;SomeServletName&lt;/servlet-name&gt;
&lt;/filter-mapping&gt;</pre>
<p>其次，可利用filter-name和url-pattern子元素将过滤器与一组servlet、JSP页面或静态内容相关联。例如，相面的程序片段指示系统只要访问Web应用中的任意URL，就运行名为Reporter的过滤器。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;filter-mapping&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;filter-name&gt;Reporter&lt;/filter-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/*&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/filter-mapping&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;filter-mapping&gt;
&lt;filter-name&gt;Reporter&lt;/filter-name&gt;
&lt;url-pattern&gt;/*&lt;/url-pattern&gt;
&lt;/filter-mapping&gt;</pre>
<p>例如，程序清单5-12给出了将ReportFilter过滤器与名为PageName的servlet相关联的web.xml文件的一部分。名字
PageName依次又与一个名为TestPage.jsp的JSP页面以及以模式http:
//host/webAppPrefix/UrlTest2/
开头的URL相关联。TestPage.jsp的源代码已经JSP页面命名的谈论在前面的3节"分配名称和定制的URL"中给出。事实上，程序清单5-
12中的servlet和servlet-name项从该节原封不动地拿过来的。给定这些web.xml项，可看到下面的标准输出形式的调试报告（换行是
为了容易阅读）。 <br />
audit.irs.gov tried to access <br />
http://mycompany.com/deployDemo/UrlTest2/business/tax-plan.html <br />
on Tue Dec 25 13:12:29 EDT 2001. <br />
<br />
程序清单5-12 Web.xml（说明filter用法的摘录）</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;?xml version=</span><span class="string"><span style="color: #0000ff;">"1.0"</span></span><span> encoding=</span><span class="string"><span style="color: #0000ff;">"ISO-8859-1"</span></span><span>?&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;!DOCTYPE web-app &nbsp;&nbsp;</span> </li>
    <li><span>PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"</span></span><span>&nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"http://java.sun.com/dtd/web-app_2_3.dtd"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span>&lt;web-app&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;filter&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;filter-name&gt;Reporter&lt;/filter-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;filter-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moresevlets.ReportFilter&lt;/filter-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/filter&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;filter-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;filter-name&gt;Reporter&lt;/filter-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;PageName&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/filter-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;PageName&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;jsp-file&gt;/RealPage.jsp&lt;/jsp-file&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt; PageName &lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/UrlTest2/*&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/web-app&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
&lt;!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;
&lt;web-app&gt;
&lt;filter&gt;
&lt;filter-name&gt;Reporter&lt;/filter-name&gt;
&lt;filter-class&gt;moresevlets.ReportFilter&lt;/filter-class&gt;
&lt;/filter&gt;
&lt;!-- ... --&gt;
&lt;filter-mapping&gt;
&lt;filter-name&gt;Reporter&lt;/filter-name&gt;
&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;
&lt;/filter-mapping&gt;
&lt;!-- ... --&gt;
&lt;servlet&gt;
&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;
&lt;jsp-file&gt;/RealPage.jsp&lt;/jsp-file&gt;
&lt;/servlet&gt;
&lt;!-- ... --&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt; PageName &lt;/servlet-name&gt;
&lt;url-pattern&gt;/UrlTest2/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
&lt;!-- ... --&gt;
&lt;/web-app&gt;</pre>
<p><br />
7 指定欢迎页 假如用户提供了一个像http: //host/webAppPrefix/directoryName/
这样的包含一个目录名但没有包含文件名的URL，会发生什么事情呢？用户能得到一个目录表？一个错误？还是标准文件的内容？如果得到标准文件内容，是
index.html、index.jsp、default.html、default.htm或别的什么东西呢？
Welcome-file-list
元素及其辅助的welcome-file元素解决了这个模糊的问题。例如，下面的web.xml项指出，如果一个URL给出一个目录名但未给出文件名，服
务器应该首先试用index.jsp，然后再试用index.html。如果两者都没有找到，则结果有赖于所用的服务器（如一个目录列表）。
虽然许多服务器缺省遵循这种行为，但不一定必须这样。因此，明确地使用welcom-file-list保证可移植性是一种良好的习惯。 <br />
</p>
<p>8
指定处理错误的页面
现在我了解到，你在开发servlet和JSP页面时从不会犯错误，而且你的所有页面是那样的清晰，一般的程序员都不会被它们的搞糊涂。但是，是人总会犯
错误的，用户可能会提供不合规定的参数，使用不正确的URL或者不能提供必需的表单字段值。除此之外，其它开发人员可能不那么细心，他们应该有些工具来克
服自己的不足。 error-page元素就是用来克服这些问题的。它有两个可能的子元素，分别是：error-code和exception-
type。第一个子元素error-code指出在给定的HTTP错误代码出现时使用的URL。第二个子元素excpetion-type指出在出现某个
给定的Java异常但未捕捉到时使用的URL。error-code和exception-type都利用location元素指出相应的URL。此
URL必须以/开始。location所指出的位置处的页面可通过查找HttpServletRequest对象的两个专门的属性来访问关于错误的信息，
这两个属性分别是：javax.servlet.error.status_code和javax.servlet.error.message。
可回忆一下，在web.xml内以正确的次序声明web-app的子元素很重要。这里只要记住，error-page出现在web.xml文件的末尾附
近，servlet、servlet-name和welcome-file-list之后即可。 <br />
</p>
<p>8.1 error-code元素
为了更好地了解error-code元素的值，可考虑一下如果不正确地输入文件名，大多数站点会作出什么反映。这样做一般会出现一个404错误信息，它表
示不能找到该文件，但几乎没提供更多有用的信息。另一方面，可以试一下在www.microsoft.com、www.ibm.com
处或者特别是在www.bea.com
处输出未知的文件名。这是会得出有用的消息，这些消息提供可选择的位置，以便查找感兴趣的页面。提供这样有用的错误页面对于Web应用来说是很有价值得。
事实上rm-error-page子元素）。由form-login-page给出的HTML表单必须具有一个j_security_check的
ACTION属性、一个名为j_username的用户名文本字段以及一个名为j_password的口令字段。
例如，程序清单5-19指示服务器使用基于表单的验证。Web应用的顶层目录中的一个名为login.jsp的页面将收集用户名和口令，并且失败的登陆将
由相同目录中名为login-error.jsp的页面报告。 程序清单5-19 web.xml（说明login-config的摘录） <br />
</p>
<p>9.2
限制对Web资源的访问 现在，可以指示服务器使用何种验证方法了。"了不起，"你说道，"除非我能指定一个来收到保护的
URL，否则没有多大用处。"没错。指出这些URL并说明他们应该得到何种保护正是security-constriaint元素的用途。此元素在
web.xml中应该出现在login-config的紧前面。它包含是个可能的子元素，分别是：web-resource-collection、
auth-constraint、user-data-constraint和display-name。下面各小节对它们进行介绍。 l
web-resource-collection
此元素确定应该保护的资源。所有security-constraint元素都必须包含至少一个web-resource-collection项。此元
素由一个给出任意标识名称的web-resource-name元素、一个确定应该保护的URL的url-pattern元素、一个指出此保护所适用的
HTTP命令（GET、POST等，缺省为所有方法）的http-method元素和一个提供资料的可选description元素组成。例如，下面的
Web-resource-collection项（在security-constratint元素内）指出Web应用的proprietary目录中
所有文档应该受到保护。 重要的是应该注意到，url-pattern仅适用于直接访问这些资源的客户机。特别是，它不适合于通过MVC体系结构利用
RequestDispatcher来访问的页面，或者不适合于利用类似jsp:forward的手段来访问的页面。这种不匀称如果利用得当的话很有好
处。例如，servlet可利用MVC体系结构查找数据，把它放到bean中，发送请求到从bean中提取数据的JSP页面并显示它。我们希望保证决不直
接访问受保护的JSP页面，而只是通过建立该页面将使用的bean的servlet来访问它。url-pattern和auth-contraint元素
可通过声明不允许任何用户直接访问JSP页面来提供这种保证。但是，这种不匀称的行为可能让开发人员放松警惕，使他们偶然对应受保护的资源提供不受限制的
访问。 l auth-constraint
尽管web-resource-collention元素质出了哪些URL应该受到保护，但是auth-constraint元素却指出哪些用户应该具有
受保护资源的访问权。此元素应该包含一个或多个标识具有访问权限的用户类别role-
name元素，以及包含（可选）一个描述角色的description元素。例如，下面web.xml中的security-constraint元素部
门规定只有指定为Administrator或Big Kahuna（或两者）的用户具有指定资源的访问权。
重要的是认识到，到此为止，这个过程的可移植部分结束了。服务器怎样确定哪些用户处于任何角色以及它怎样存放用户的口令，完全有赖于具体的系统。
例如，Tomcat使用install_dir/conf/tomcat-users.xml将用户名与角色名和口令相关联，正如下面例子中所示，它指出
用户joe（口令bigshot）和jane（口令enaj）属于administrator和kahuna角色。 l
user-data-constraint
这个可选的元素指出在访问相关资源时使用任何传输层保护。它必须包含一个transport-guarantee子元素（合法值为NONE、
INTEGRAL或CONFIDENTIAL），并且可选地包含一个description元素。transport-guarantee为NONE值将
对所用的通讯协议不加限制。INTEGRAL值表示数据必须以一种防止截取它的人阅读它的方式传送。虽然原理上（并且在未来的HTTP版本中），在
INTEGRAL和CONFIDENTIAL之间可能会有差别，但在当前实践中，他们都只是简单地要求用SSL。例如，下面指示服务器只允许对相关资源做
HTTPS连接： l display-name
security-constraint的这个很少使用的子元素给予可能由GUI工具使用的安全约束项一个名称。 9.3 分配角色名
迄今为止，讨论已经集中到完全由容器（服务器）处理的安全问题之上了。但servlet以及JSP页面也能够处理它们自己的安全问题。
例如，容器可能允许用户从bigwig或bigcheese角色访问一个显示主管人员额外紧贴的页面，但只允许bigwig用户修改此页面的参数。完成这
种更细致的控制的一种常见方法是调用HttpServletRequset的isUserInRole方法，并据此修改访问。 Servlet的
security-role-ref子元素提供出现在服务器专用口令文件中的安全角色名的一个别名。例如，假如编写了一个调用
request.isUserInRole（"boss"）的servlet，但后来该servlet被用在了一个其口令文件调用角色manager而不
是boss的服务器中。下面的程序段使该servlet能够使用这两个名称中的任何一个。
也可以在web-app内利用security-role元素提供将出现在role-name元素中的所有安全角色的一个全局列表。分别地生命角色使高级
IDE容易处理安全信息。 10 控制会话超时
如果某个会话在一定的时间内未被访问，服务器可把它扔掉以节约内存。可利用HttpSession的setMaxInactiveInterval方法直
接设置个别会话对象的超时值。如果不采用这种方法，则缺省的超时值由具体的服务器决定。但可利用session-config和session-
timeout元素来给出一个适用于所有服务器的明确的超时值。超时值的单位为分钟，因此，下面的例子设置缺省会话超时值为三个小时（180分钟）。</p>
<p>
11 Web应用的文档化 越来越多的开发环境开始提供servlet和JSP的直接支持。例子有Borland Jbuilder
Enterprise Edition、Macromedia UltraDev、Allaire JRun
Studio（写此文时，已被Macromedia收购）以及IBM VisuaAge for Java等。
大量的web.xml元素不仅是为服务器设计的，而且还是为可视开发环境设计的。它们包括icon、display-name和discription
等。
可回忆一下，在web.xml内以适当地次序声明web-app子元素很重要。不过，这里只要记住icon、display-name和
description是web.xml的web-app元素内的前三个合法元素即可。 l icon
icon元素指出GUI工具可用来代表Web应用的一个和两个图像文件。可利用small-icon元素指定一幅16 x
16的GIF或JPEG图像，用large-icon元素指定一幅32 x 32的图像。下面举一个例子： l display-name
display-name元素提供GUI工具可能会用来标记此Web应用的一个名称。下面是个例子。 l description
description元素提供解释性文本，如下所示： 12 关联文件与MIME类型
服务器一般都具有一种让Web站点管理员将文件扩展名与媒体相关联的方法。例如，将会自动给予名为mom.jpg的文件一个image/jpeg的
MIME
类型。但是，假如你的Web应用具有几个不寻常的文件，你希望保证它们在发送到客户机时分配为某种MIME类型。mime-mapping元素（具有
extension和mime-type子元素）可提供这种保证。例如，下面的代码指示服务器将application/x-fubar的MIME类型分
配给所有以.foo结尾的文件。
或许，你的Web应用希望重载（override）标准的映射。例如，下面的代码将告诉服务器在发送到客户机时指定.ps文件作为纯文本
（text/plain）而不是作为PostScript（application/postscript）。</p>
<p> 13 定位TLD JSP
taglib元素具有一个必要的uri属性，它给出一个TLD（Tag Library
Descriptor）文件相对于Web应用的根的位置。TLD文件的实际名称在发布新的标签库版本时可能会改变，但我们希望避免更改所有现有JSP页
面。此外，可能还希望使用保持taglib元素的简练性的一个简短的uri。这就是部署描述符文件的taglib元素派用场的所在了。Taglib包含两
个子元素：taglib-uri和taglib-location。taglib-uri元素应该与用于JSP
taglib元素的uri属性的东西相匹配。Taglib-location元素给出TLD文件的实际位置。例如，假如你将文件chart-tags-
1.3beta.tld放在WebApp/WEB-INF/tlds中。现在，假如web.xml在web-app元素内包含下列内容。
给出这个说明后，JSP页面可通过下面的简化形式使用标签库。 14 指定应用事件监听程序
应用事件监听器程序是建立或修改servlet环境或会话对象时通知的类。它们是servlet规范的版本2.3中的新内容。这里只简单地说明用来向
Web应用注册一个监听程序的web.xml的用法。
注册一个监听程序涉及在web.xml的web-app元素内放置一个listener元素。在listener元素内，listener-class元
素列出监听程序的完整的限定类名，如下所示： &lt;listener&gt;
虽然listener元素的结构很简单，但请不要忘记，必须正确地给出web-app元素内的子元素的次序。listener元素位于所有的
servlet
元素之前以及所有filter-mapping元素之后。此外，因为应用生存期监听程序是serlvet规范的2.3版本中的新内容，所以必须使用
web.xml DTD的2.3版本，而不是2.2版本。
例如，程序清单5-20给出一个名为ContextReporter的简单的监听程序，只要Web应用的Servlet-Context建立（如装载
Web应用）或消除（如服务器关闭）时，它就在标准输出上显示一条消息。程序清单5-21给出此监听程序注册所需要的web.xml文件的一部分。
程序清单5-20 ContextReporterjava 程序清单5-21 web.xml（声明一个监听程序的摘录） 15 J2EE元素
本节描述用作J2EE环境组成部分的Web应用的web.xml元素。这里将提供一个简明的介绍，详细内容可以参阅
http://java.sun.com/j2ee/j2ee-1_3-fr-spec.pdf的Java 2 Plantform
Enterprise Edition版本1.3规范的第5章。 l distributable distributable
元素指出，Web应用是以这样的方式编程的：即，支持集群的服务器可安全地在多个服务器上分布Web应用。例如，一个可分布的应用必须只使用
Serializable对象作为其HttpSession对象的属性，而且必须避免用实例变量（字段）来实现持续性。distributable元素直
接出现在discription元素之后，并且不包含子元素或数据，它只是一个如下的标志。 l resource-env-ref resource
-env-ref元素声明一个与某个资源有关的管理对象。此元素由一个可选的description元素、一个resource-env-ref-
name元素（一个相对于java:comp/env环境的JNDI名）以及一个resource-env-type元素（指定资源类型的完全限定的
类），如下所示： l env-entry env
-entry元素声明Web应用的环境项。它由一个可选的description元素、一个env-entry-name元素（一个相对于java:
comp/env环境JNDI名）、一个env-entry-value元素（项值）以及一个env-entry-type元素（java.lang程序
包中一个类型的完全限定类名，java.lang.Boolean、java.lang.String等）组成。下面是一个例子： l ejb-ref
ejb
-ref元素声明对一个EJB的主目录的应用。它由一个可选的description元素、一个ejb-ref-name元素（相对于java:
comp/env的EJB应用）、一个ejb-ref-type元素（bean的类型，Entity或Session）、一个home元素（bean的主
目录接口的完全限定名）、一个remote元素（bean的远程接口的完全限定名）以及一个可选的ejb-link元素（当前bean链接的另一个
bean的名称）组成。 l ejb-local-ref
ejb-local-ref元素声明一个EJB的本地主目录的引用。除了用local-home代替home外，此元素具有与ejb-ref元素相同的属
性并以相同的方式使用。</p>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/267404.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-04-24 17:17 <a href="http://www.blogjava.net/hulizhong/archive/2009/04/24/267404.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转  web.xml配置详细说明(中)</title><link>http://www.blogjava.net/hulizhong/archive/2009/04/24/267403.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Fri, 24 Apr 2009 09:15:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/04/24/267403.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/267403.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/04/24/267403.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/267403.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/267403.html</trackback:ping><description><![CDATA[转 http://blog.csdn.net/fenglibing/archive/2009/03/19/4005446.aspx<br />
<br />
3.2 定义定制的URL <br />
大多数服务器具有一个缺省的serlvet URL： <br />
http://host
/webAppPrefix/servlet/packageName.ServletName。虽然在开发中使用这个URL很方便，但是我们常常会希望
另一个URL用于部署。例如，可能会需要一个出现在Web应用顶层的URL（如，http:
//host/webAppPrefix/Anyname），并且在此URL中没有servlet项。位于顶层的URL简化了相对URL的使用。此外，对
许多开发人员来说，顶层URL看上去比更长更麻烦的缺省URL更简短。 <br />
事实上，有时需要使用定制的URL。比如，你可能想关闭缺省URL映射，以便更好地强制实施安全限制或防止用户意外地访问无初始化参数的servlet。如果你禁止了缺省的URL，那么你怎样访问servlet呢？这时只有使用定制的URL了。 <br />
为
了分配一个定制的URL，可使用servlet-mapping元素及其servlet-name和url-pattern子元素。Servlet-
name元素提供了一个任意名称，可利用此名称引用相应的servlet；url-pattern描述了相对于Web应用的根目录的URL。url-
pattern元素的值必须以斜杠（/）起始。 <br />
下面给出一个简单的web.xml摘录，它允许使用URL http://host/webAppPrefix/UrlTest而不是http://host/webAppPrefix/servlet/Test或 <br />
http:
//host/webAppPrefix/servlet/moreservlets.TestServlet。请注意，仍然需要XML头、
DOCTYPE声明以及web-app封闭元素。此外，可回忆一下，XML元素出现地次序不是随意的。特别是，需要把所有servlet元素放在所有
servlet-mapping元素之前。
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;Test&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moreservlets.TestServlet&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;Test&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/UrlTest&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt;Test&lt;/servlet-name&gt;
&lt;servlet-class&gt;moreservlets.TestServlet&lt;/servlet-class&gt;
&lt;/servlet&gt;
&lt;!-- ... --&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;Test&lt;/servlet-name&gt;
&lt;url-pattern&gt;/UrlTest&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</pre>
<p>URL模式还可以包含通配符。例如，下面的小程序指示服务器发送所有以Web应用的URL前缀开始，以..asp结束的请求到名为BashMS的servlet。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;BashMS&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;msUtils.ASPTranslator&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;BashMS&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/*.asp&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt;BashMS&lt;/servlet-name&gt;
&lt;servlet-class&gt;msUtils.ASPTranslator&lt;/servlet-class&gt;
&lt;/servlet&gt;
&lt;!-- ... --&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;BashMS&lt;/servlet-name&gt;
&lt;url-pattern&gt;/*.asp&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</pre>
<p><br />
3.3 命名JSP页面 <br />
因为JSP页面要转换成sevlet，自然希望就像命名servlet一样命名JSP页面。毕竟，JSP
页面可能会从初始化参数、安全设置或定制的URL中受益，正如普通的serlvet那样。虽然JSP页面的后台实际上是servlet这句话是正确的，但
存在一个关键的猜疑：即，你不知道JSP页面的实际类名（因为系统自己挑选这个名字）。因此，为了命名JSP页面，可将jsp-file元素替换为
servlet-calss元素，如下所示：</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;Test&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;jsp-file&gt;/TestPage.jsp&lt;/jsp-file&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt;Test&lt;/servlet-name&gt;
&lt;jsp-file&gt;/TestPage.jsp&lt;/jsp-file&gt;
&lt;/servlet&gt;</pre>
<p>命名JSP页面的原因与命名servlet的原因完全相同：即为了提供一个与定制设置（如，初始化参数和安全设置）一起使用的名称，并且，以便能更
改激活
JSP页面的URL（比方说，以便多个URL通过相同页面得以处理，或者从URL中去掉.jsp扩展名）。但是，在设置初始化参数时，应该注意，JSP页
面是利用jspInit方法，而不是init方法读取初始化参数的。 <br />
例如，程序清单5-3给出一个名为TestPage.jsp的简单JSP
页面，它的工作只是打印出用来激活它的URL的本地部分。TestPage.jsp放置在deployDemo应用的顶层。程序清单5-4给出了用来分配
一个注册名PageName，然后将此注册名与http://host/webAppPrefix/UrlTest2/anything
形式的URL相关联的web.xml文件（即，deployDemo/WEB-INF/web.xml）的一部分。 <br />
<br />
程序清单5-3 TestPage.jsp</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;!DOCTYPE HTML PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//W3C//DTD HTML 4.0 Transitional//EN"</span></span><span>&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;HTML&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;HEAD&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;TITLE&gt; &nbsp;&nbsp;</span> </li>
    <li><span>JSP Test Page &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/TITLE&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/HEAD&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;BODY BGCOLOR=</span><span class="string"><span style="color: #0000ff;">"#FDF5E6"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;H2&gt;URI: &lt;%= request.getRequestURI() %&gt;&lt;/H2&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/BODY&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/HTML&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"&gt;
&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;
JSP Test Page
&lt;/TITLE&gt;
&lt;/HEAD&gt;
&lt;BODY BGCOLOR="#FDF5E6"&gt;
&lt;H2&gt;URI: &lt;%= request.getRequestURI() %&gt;&lt;/H2&gt;
&lt;/BODY&gt;
&lt;/HTML&gt;</pre>
<p><br />
<br />
程序清单5-4 web.xml（说明JSP页命名的摘录）</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;?xml version=</span><span class="string"><span style="color: #0000ff;">"1.0"</span></span><span> encoding=</span><span class="string"><span style="color: #0000ff;">"ISO-8859-1"</span></span><span>?&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;!DOCTYPE web-app &nbsp;&nbsp;</span> </li>
    <li><span>PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"</span></span><span>&nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"http://java.sun.com/dtd/web-app_2_3.dtd"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span>&lt;web-app&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;PageName&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;jsp-file&gt;/TestPage.jsp&lt;/jsp-file&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt; PageName &lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/UrlTest2/*&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/web-app&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
&lt;!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;
&lt;web-app&gt;
&lt;!-- ... --&gt;
&lt;servlet&gt;
&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;
&lt;jsp-file&gt;/TestPage.jsp&lt;/jsp-file&gt;
&lt;/servlet&gt;
&lt;!-- ... --&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt; PageName &lt;/servlet-name&gt;
&lt;url-pattern&gt;/UrlTest2/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
&lt;!-- ... --&gt;
&lt;/web-app&gt;</pre>
<p><br />
<br />
4 禁止激活器servlet <br />
<br />
对servlet或JSP页面建立定制URL的一个原因是，这样做可以注册从
init（servlet）或jspInit（JSP页面）方法中读取得初始化参数。但是，初始化参数只在是利用定制URL模式或注册名访问
servlet或JSP页面时可以使用，用缺省URL http://host/webAppPrefix/servlet/ServletName
访问时不能使用。因此，你可能会希望关闭缺省URL，这样就不会有人意外地调用初始化servlet了。这个过程有时称为禁止激活器servlet，因为
多数服务器具有一个用缺省的servlet URL注册的标准servlet，并激活缺省的URL应用的实际servlet。 <br />
有两种禁止此缺省URL的主要方法： <br />
l 在每个Web应用中重新映射/servlet/模式。 <br />
l 全局关闭激活器servlet。 <br />
重
要的是应该注意到，虽然重新映射每个Web应用中的/servlet/模式比彻底禁止激活servlet所做的工作更多，但重新映射可以用一种完全可移植
的方式来完成。相反，全局禁止激活器servlet完全是针对具体机器的，事实上有的服务器（如ServletExec）没有这样的选择。下面的讨论对每
个Web应用重新映射/servlet/ URL模式的策略。后面提供在Tomcat中全局禁止激活器servlet的详细内容。 <br />
4.1 重新映射/servlet/URL模式 <br />
在
一个特定的Web应用中禁止以http://host/webAppPrefix/servlet/
开始的URL的处理非常简单。所需做的事情就是建立一个错误消息servlet，并使用前一节讨论的url-pattern元素将所有匹配请求转向该
servlet。只要简单地使用： <br />
&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt; <br />
作为servlet-mapping元素中的模式即可。 <br />
例如，程序清单5-5给出了将SorryServlet servlet（程序清单5-6）与所有以http://host/webAppPrefix/servlet/ 开头的URL相关联的部署描述符文件的一部分。 <br />
<br />
程序清单5-5 web.xml（说明JSP页命名的摘录）</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;?xml version=</span><span class="string"><span style="color: #0000ff;">"1.0"</span></span><span> encoding=</span><span class="string"><span style="color: #0000ff;">"ISO-8859-1"</span></span><span>?&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;!DOCTYPE web-app &nbsp;&nbsp;</span> </li>
    <li><span>PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"</span></span><span>&nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"http://java.sun.com/dtd/web-app_2_3.dtd"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span>&lt;web-app&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;Sorry&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moreservlets.SorryServlet&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt; Sorry &lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/web-app&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
&lt;!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;
&lt;web-app&gt;
&lt;!-- ... --&gt;
&lt;servlet&gt;
&lt;servlet-name&gt;Sorry&lt;/servlet-name&gt;
&lt;servlet-class&gt;moreservlets.SorryServlet&lt;/servlet-class&gt;
&lt;/servlet&gt;
&lt;!-- ... --&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt; Sorry &lt;/servlet-name&gt;
&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
&lt;!-- ... --&gt;
&lt;/web-app&gt;</pre>
<p><br />
<br />
程序清单5-6 SorryServlet.java</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span class="keyword"><strong><span style="color: #7f0055;">package</span></strong></span><span> moreservlets; &nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> java.io.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.http.*; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="comment"><span style="color: #008200;">/** Simple servlet used to give error messages to </span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* users who try to access default servlet URLs </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* (i.e., http://host/webAppPrefix/servlet/ServletName) </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* in Web applications that have disabled this </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* behavior. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &lt;P&gt; </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* Taken from More Servlets and JavaServer Pages </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* from Prentice Hall and Sun Microsystems Press, </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* http://www.moreservlets.com/. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &#169; 2002 Marty Hall; may be freely used or adapted. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">*/</span></span><span>&nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span> SorryServlet </span><span class="keyword"><strong><span style="color: #7f0055;">extends</span></strong></span><span> HttpServlet { &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> doGet(HttpServletRequest request, &nbsp;&nbsp;</span> </li>
    <li><span>HttpServletResponse response) &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">throws</span></strong></span><span> ServletException, IOException { &nbsp;&nbsp;</span> </li>
    <li><span>response.setContentType(</span><span class="string"><span style="color: #0000ff;">"text/html"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>PrintWriter out = response.getWriter(); &nbsp;&nbsp;</span> </li>
    <li><span>String title = </span><span class="string"><span style="color: #0000ff;">"Invoker Servlet Disabled."</span></span><span>; &nbsp;&nbsp;</span> </li>
    <li><span>out.println(ServletUtilities.headWithTitle(title) + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;H2&gt;"</span></span><span> + title + </span><span class="string"><span style="color: #0000ff;">"&lt;/H2&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"Sorry, access to servlets by means of\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"URLs that begin with\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"http://host/webAppPrefix/servlet/\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"has been disabled.\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;/BODY&gt;&lt;/HTML&gt;"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> doPost(HttpServletRequest request, &nbsp;&nbsp;</span> </li>
    <li><span>HttpServletResponse response) &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">throws</span></strong></span><span> ServletException, IOException { &nbsp;&nbsp;</span> </li>
    <li><span>doGet(request, response); &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">package moreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/** Simple servlet used to give error messages to
* users who try to access default servlet URLs
* (i.e., http://host/webAppPrefix/servlet/ServletName)
* in Web applications that have disabled this
* behavior.
* &lt;P&gt;
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* &#169; 2002 Marty Hall; may be freely used or adapted.
*/
public class SorryServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Invoker Servlet Disabled.";
out.println(ServletUtilities.headWithTitle(title) +
"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n" +
"&lt;H2&gt;" + title + "&lt;/H2&gt;\n" +
"Sorry, access to servlets by means of\n" +
"URLs that begin with\n" +
"http://host/webAppPrefix/servlet/\n" +
"has been disabled.\n" +
"&lt;/BODY&gt;&lt;/HTML&gt;");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}</pre>
<p><br />
4.2 全局禁止激活器：Tomcat <br />
Tomcat 4中用来关闭缺省URL的方法与Tomcat 3中所用的很不相同。下面介绍这两种方法： <br />
1．禁止激活器： Tomcat 4 <br />
Tomcat
4用与前面相同的方法关闭激活器servlet，即利用web.xml中的url-mapping元素进行关闭。不同之处在于Tomcat使用了放在
install_dir/conf中的一个服务器专用的全局web.xml文件，而前面使用的是存放在每个Web应用的WEB-INF目录中的标准
web.xml文件。 <br />
因此，为了在Tomcat 4中关闭激活器servlet，只需在install_dir/conf/web.xml中简单地注释出/servlet/* URL映射项即可，如下所示：</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;!-- &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;invoker&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet-mapping&gt; &nbsp;&nbsp;</span> </li>
    <li><span>--&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;!--
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;invoker&lt;/servlet-name&gt;
&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
--&gt;</pre>
<p>再次提醒，应该注意这个项是位于存放在install_dir/conf的Tomcat专用的web.xml文件中的，此文件不是存放在每个Web应用的WEB-INF目录中的标准web.xml。 <br />
2．禁止激活器：Tomcat3 <br />
在Apache
Tomcat的版本3中，通过在install_dir/conf/server.xml中注释出InvokerInterceptor项全局禁止缺省
servlet URL。例如，下面是禁止使用缺省servlet URL的server.xml文件的一部分。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;!-- &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;RequsetInterceptor &nbsp;&nbsp;</span> </li>
    <li><span>className=</span><span class="string"><span style="color: #0000ff;">"org.apache.tomcat.request.InvokerInterceptor"</span></span><span>&nbsp;&nbsp;</span> </li>
    <li><span>debug=</span><span class="string"><span style="color: #0000ff;">"0"</span></span><span> prefix=</span><span class="string"><span style="color: #0000ff;">"/servlet/"</span></span><span> /&gt; &nbsp;&nbsp;</span> </li>
    <li><span>--&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;!--
&lt;RequsetInterceptor
className="org.apache.tomcat.request.InvokerInterceptor"
debug="0" prefix="/servlet/" /&gt;
--&gt;</pre>
<p><br />
5 初始化和预装载servlet与JSP页面 <br />
<br />
这里讨论控制servlet和JSP页面的启动行为的方法。特别是，说明了怎样分配初始化参数以及怎样更改服务器生存期中装载servlet和JSP页面的时刻。 <br />
5.1 分配servlet初始化参数 <br />
利
用init-param元素向servlet提供初始化参数，init-param元素具有param-name和param-value子元素。例如，
在下面的例子中，如果initServlet
servlet是利用它的注册名（InitTest）访问的，它将能够从其方法中调用getServletConfig().
getInitParameter("param1")获得"Value
1"，调用getServletConfig().getInitParameter("param2")获得"2"。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;InitTest&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moreservlets.InitServlet&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-name&gt;param1&lt;/param-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-value&gt;value1&lt;/param-value&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-name&gt;param2&lt;/param-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-value&gt;</span><span class="number"><span style="color: #c00000;">2</span></span><span>&lt;/param-value&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt;InitTest&lt;/servlet-name&gt;
&lt;servlet-class&gt;moreservlets.InitServlet&lt;/servlet-class&gt;
&lt;init-param&gt;
&lt;param-name&gt;param1&lt;/param-name&gt;
&lt;param-value&gt;value1&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;init-param&gt;
&lt;param-name&gt;param2&lt;/param-name&gt;
&lt;param-value&gt;2&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;/servlet&gt;</pre>
<p>在涉及初始化参数时，有几点需要注意： <br />
l 返回值。GetInitParameter的返回值总是一个String。因此，在前一个例子中，可对param2使用Integer.parseInt获得一个int。 <br />
l JSP中的初始化。JSP页面使用jspInit而不是init。JSP页面还需要使用jsp-file元素代替servlet-class。 <br />
l
缺省URL。初始化参数只在通过它们的注册名或与它们注册名相关的定制URL模式访问Servlet时可以使用。因此，在这个例子中，param1和
param2初始化参数将能够在使用URL http://host/webAppPrefix/servlet/InitTest
时可用，但在使用URL http://host/webAppPrefix/servlet/myPackage.InitServlet
时不能使用。 <br />
例如，程序清单5-7给出一个名为InitServlet的简单servlet，它使用init方法设置firstName和emailAddress字段。程序清单5-8给出分配名称InitTest给servlet的web.xml文件。 <br />
程序清单5-7 InitServlet.java</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span class="keyword"><strong><span style="color: #7f0055;">package</span></strong></span><span> moreservlets; &nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> java.io.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.http.*; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="comment"><span style="color: #008200;">/** Simple servlet used to illustrate servlet </span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* initialization parameters. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &lt;P&gt; </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* Taken from More Servlets and JavaServer Pages </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* from Prentice Hall and Sun Microsystems Press, </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* http://www.moreservlets.com/. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &#169; 2002 Marty Hall; may be freely used or adapted. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">*/</span></span><span>&nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span> InitServlet </span><span class="keyword"><strong><span style="color: #7f0055;">extends</span></strong></span><span> HttpServlet { &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">private</span></strong></span><span> String firstName, emailAddress; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> init() { &nbsp;&nbsp;</span> </li>
    <li><span>ServletConfig config = getServletConfig(); &nbsp;&nbsp;</span> </li>
    <li><span>firstName = config.getInitParameter(</span><span class="string"><span style="color: #0000ff;">"firstName"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>emailAddress = config.getInitParameter(</span><span class="string"><span style="color: #0000ff;">"emailAddress"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> doGet(HttpServletRequest request, &nbsp;&nbsp;</span> </li>
    <li><span>HttpServletResponse response) &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">throws</span></strong></span><span> ServletException, IOException { &nbsp;&nbsp;</span> </li>
    <li><span>response.setContentType(</span><span class="string"><span style="color: #0000ff;">"text/html"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>PrintWriter out = response.getWriter(); &nbsp;&nbsp;</span> </li>
    <li><span>String uri = request.getRequestURI(); &nbsp;&nbsp;</span> </li>
    <li><span>out.println(ServletUtilities.headWithTitle(</span><span class="string"><span style="color: #0000ff;">"Init Servlet"</span></span><span>) + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;H2&gt;Init Parameters:&lt;/H2&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;UL&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;LI&gt;First name: "</span></span><span> + firstName + </span><span class="string"><span style="color: #0000ff;">"\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;LI&gt;Email address: "</span></span><span> + emailAddress + </span><span class="string"><span style="color: #0000ff;">"\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;/UL&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;/BODY&gt;&lt;/HTML&gt;"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">package moreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/** Simple servlet used to illustrate servlet
* initialization parameters.
* &lt;P&gt;
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* &#169; 2002 Marty Hall; may be freely used or adapted.
*/
public class InitServlet extends HttpServlet {
private String firstName, emailAddress;
public void init() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter("firstName");
emailAddress = config.getInitParameter("emailAddress");
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String uri = request.getRequestURI();
out.println(ServletUtilities.headWithTitle("Init Servlet") +
"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n" +
"&lt;H2&gt;Init Parameters:&lt;/H2&gt;\n" +
"&lt;UL&gt;\n" +
"&lt;LI&gt;First name: " + firstName + "\n" +
"&lt;LI&gt;Email address: " + emailAddress + "\n" +
"&lt;/UL&gt;\n" +
"&lt;/BODY&gt;&lt;/HTML&gt;");
}
}</pre>
<p><br />
程序清单5-8 web.xml（说明初始化参数的摘录）</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;?xml version=</span><span class="string"><span style="color: #0000ff;">"1.0"</span></span><span> encoding=</span><span class="string"><span style="color: #0000ff;">"ISO-8859-1"</span></span><span>?&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;!DOCTYPE web-app &nbsp;&nbsp;</span> </li>
    <li><span>PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"</span></span><span>&nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"http://java.sun.com/dtd/web-app_2_3.dtd"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span>&lt;web-app&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- ... --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;InitTest&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moreservlets.InitServlet&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-name&gt;firstName&lt;/param-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-value&gt;Larry&lt;/param-value&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;init-param&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-name&gt;emailAddress&lt;/param-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;param-value&gt;Ellison</span><span class="annotation"><span style="color: #646464;">@Microsoft</span></span><span>.com&lt;/param-value&gt; &nbsp;&nbsp;</span> </li>
</ol>
</div>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/267403.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-04-24 17:15 <a href="http://www.blogjava.net/hulizhong/archive/2009/04/24/267403.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转  web.xml配置详细说明(上)</title><link>http://www.blogjava.net/hulizhong/archive/2009/04/24/267402.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Fri, 24 Apr 2009 09:13:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/04/24/267402.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/267402.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/04/24/267402.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/267402.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/267402.html</trackback:ping><description><![CDATA[转 http://blog.csdn.net/fenglibing/archive/2009/03/19/4005441.aspx<br />
<br />
<p><strong>转自<a href="http://elevenet.javaeye.com/blog/67862">http://elevenet.javaeye.com/blog/67862</a></strong></p>
<p><strong>这篇文章还是不错的，有些老，有些地址还是直接翻译的。不是很准确。</strong></p>
<p><strong>关键字: J2EE</strong></p>
<div class="blog_content">1 定义头和根元素 <br />
<br />
部署描述符文件就像所有XML文件一样，必须以一个XML头开始。这个头声明可以使用的XML版本并给出文件的字符编码。 <br />
DOCYTPE声明必须立即出现在此头之后。这个声明告诉服务器适用的servlet规范的版本（如2.2或2.3）并指定管理此文件其余部分内容的语法的DTD(Document Type Definition，文档类型定义)。 <br />
所有部署描述符文件的顶层（根）元素为web-app。请注意，XML元素不像HTML，他们是大小写敏感的。因此，web-App和WEB-APP都是不合法的，web-app必须用小写。 <br />
<br />
2 部署描述符文件内的元素次序 <br />
<br />
XML
元素不仅是大小写敏感的，而且它们还对出现在其他元素中的次序敏感。例如，XML头必须是文件中的第一项，DOCTYPE声明必须是第二项，而web-
app元素必须是第三项。在web-app元素内，元素的次序也很重要。服务器不一定强制要求这种次序，但它们允许（实际上有些服务器就是这样做的）完全
拒绝执行含有次序不正确的元素的Web应用。这表示使用非标准元素次序的web.xml文件是不可移植的。 <br />
下面的列表给出了所有可直接出现在web-app元素内的合法元素所必需的次序。例如，此列表说明servlet元素必须出现在所有servlet-mapping元素之前。请注意，所有这些元素都是可选的。因此，可以省略掉某一元素，但不能把它放于不正确的位置。 <br />
l icon&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; icon 元素指出IDE和GUI工具用来表示Web应用的一个和两个图像文件的位置。 <br />
l display-name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; display-name元素提供GUI工具可能会用来标记这个特定的Web应用的一个名称。 <br />
l description&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; description元素给出与此有关的说明性文本。 <br />
l context-param&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; context-param元素声明应用范围内的初始化参数。 <br />
l filter&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 过滤器元素将一个名字与一个实现javax.servlet.Filter接口的类相关联。 <br />
l filter-mapping&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一旦命名了一个过滤器，就要利用filter-mapping元素把它与一个或多个servlet或JSP 页面相关联。 <br />
l listener&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; servlet API的版本2.3增加了对事件监听程序的支持，事件监听程序在建立、修改和删除会话或servlet环境时得到通知。Listener元素指出事件监听程序类。 <br />
l servlet&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在向servlet或JSP页面制定初始化参数或定制URL时，必须首先命名servlet或JSP页面。Servlet元素就是用来完成此项任务的。 <br />
l servlet-mapping&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 服务器一般为servlet提供一个缺省的URL：<a href="http://host/webAppPrefix/servlet/ServletName">http://host/webAppPrefix/servlet/ServletName</a>。但是，常常会更改这个URL，以便servlet可以访问初始化参数或更容易地处理相对URL。在更改缺省URL时，使用servlet-mapping元素。 <br />
l
session-config&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
如果某个会话在一定时间内未被访问，服务器可以抛弃它以节省内存。可通过使用HttpSession的setMaxInactiveInterval方法
明确设置单个会话对象的超时值，或者可利用session-config元素制定缺省超时值。 <br />
l mime-mapping&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果Web应用具有想到特殊的文件，希望能保证给他们分配特定的MIME类型，则mime-mapping元素提供这种保证。 <br />
l welcom-file-list&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; welcome-file-list元素指示服务器在收到引用一个目录名而不是文件名的URL时，使用哪个文件。 <br />
l error-page&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error-page元素使得在返回特定HTTP状态代码时，或者特定类型的异常被抛出时，能够制定将要显示的页面。 <br />
l taglib&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; taglib元素对标记库描述符文件（Tag Libraryu Descriptor file）指定别名。此功能使你能够更改TLD文件的位置，而不用编辑使用这些文件的JSP页面。 <br />
l resource-env-ref&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; resource-env-ref元素声明与资源相关的一个管理对象。 <br />
l resource-ref&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; resource-ref元素声明一个资源工厂使用的外部资源。 <br />
l security-constraint&nbsp;&nbsp;&nbsp; security-constraint元素制定应该保护的URL。它与login-config元素联合使用 <br />
l login-config 用login-config元素来指定服务器应该怎样给试图访问受保护页面的用户授权。它与sercurity-constraint元素联合使用。 <br />
l
security-role&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
security-role元素给出安全角色的一个列表，这些角色将出现在servlet元素内的security-role-ref元素的role-
name子元素中。分别地声明角色可使高级IDE处理安全信息更为容易。 <br />
l env-entry&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; env-entry元素声明Web应用的环境项。 <br />
l ejb-ref&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ejb-ref元素声明一个EJB的主目录的引用。 <br />
l ejb-local-ref&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ejb-local-ref元素声明一个EJB的本地主目录的应用。 <br />
<br />
3 分配名称和定制的UL <br />
<br />
在web.xml中完成的一个最常见的任务是对servlet或JSP页面给出名称和定制的URL。用servlet元素分配名称，使用servlet-mapping元素将定制的URL与刚分配的名称相关联。 <br />
3.1 分配名称 <br />
为
了提供初始化参数，对servlet或JSP页面定义一个定制URL或分配一个安全角色，必须首先给servlet或JSP页面一个名称。可通过
servlet元素分配一个名称。最常见的格式包括servlet-name和servlet-class子元素（在web-app元素内），如下所示：
<br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;servlet&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;servlet-name&gt;Test&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moreservlets.TestServlet&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt;&nbsp;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">&lt;servlet&gt;
&lt;servlet-name&gt;Test&lt;/servlet-name&gt;
&lt;servlet-class&gt;moreservlets.TestServlet&lt;/servlet-class&gt;
&lt;/servlet&gt;</pre>
这
表示位于WEB-INF/classes/moreservlets/TestServlet的servlet已经得到了注册名Test。给
servlet一个名称具有两个主要的含义。首先，初始化参数、定制的URL模式以及其他定制通过此注册名而不是类名引用此servlet。其次,可在
URL而不是类名中使用此名称。因此，利用刚才给出的定义，URL http://host/webAppPrefix/servlet/Test
可用于 http://host/webAppPrefix/servlet/moreservlets.TestServlet 的场所。 <br />
请
记住：XML元素不仅是大小写敏感的，而且定义它们的次序也很重要。例如，web-app元素内所有servlet元素必须位于所有servlet-
mapping元素（下一小节介绍）之前，而且还要位于5.6节和5.11节讨论的与过滤器或文档相关的元素（如果有的话）之前。类似地，servlet
的servlet-name子元素也必须出现在servlet-class之前。5.2节"部署描述符文件内的元素次序"将详细介绍这种必需的次序。 <br />
例
如，程序清单5-1给出了一个名为TestServlet的简单servlet，它驻留在moreservlets程序包中。因为此servlet是扎根
在一个名为deployDemo的目录中的Web应用的组成部分，所以TestServlet.class放在deployDemo/WEB-
INF/classes/moreservlets中。程序清单5-2给出将放置在deployDemo/WEB-INF/内的web.xml文件的一部
分。此web.xml文件使用servlet-name和servlet-class元素将名称Test与TestServlet.class相关联。图
5-1和图5-2分别显示利用缺省URL和注册名调用TestServlet时的结果。 <br />
<br />
程序清单5-1 TestServlet.java <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span class="keyword"><strong><span style="color: #7f0055;">package</span></strong></span><span> moreservlets; &nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> java.io.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.*; &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">import</span></strong></span><span> javax.servlet.http.*; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="comment"><span style="color: #008200;">/** Simple servlet used to illustrate servlet naming </span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* and custom URLs. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &lt;P&gt; </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* Taken from More Servlets and JavaServer Pages </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* from Prentice Hall and Sun Microsystems Press, </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* http://www.moreservlets.com/. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">* &#169; 2002 Marty Hall; may be freely used or adapted. </span></span></span></li>
    <li><span><span class="comment"><span style="color: #008200;">*/</span></span><span>&nbsp;&nbsp;</span></span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span> TestServlet </span><span class="keyword"><strong><span style="color: #7f0055;">extends</span></strong></span><span> HttpServlet { &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">public</span></strong></span><span> </span><span class="keyword"><strong><span style="color: #7f0055;">void</span></strong></span><span> doGet(HttpServletRequest request, &nbsp;&nbsp;</span> </li>
    <li><span>HttpServletResponse response) &nbsp;&nbsp;</span> </li>
    <li><span class="keyword"><strong><span style="color: #7f0055;">throws</span></strong></span><span> ServletException, IOException { &nbsp;&nbsp;</span> </li>
    <li><span>response.setContentType(</span><span class="string"><span style="color: #0000ff;">"text/html"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>PrintWriter out = response.getWriter(); &nbsp;&nbsp;</span> </li>
    <li><span>String uri = request.getRequestURI(); &nbsp;&nbsp;</span> </li>
    <li><span>out.println(ServletUtilities.headWithTitle(</span><span class="string"><span style="color: #0000ff;">"Test Servlet"</span></span><span>) + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;H2&gt;URI: "</span></span><span> + uri + </span><span class="string"><span style="color: #0000ff;">"&lt;/H2&gt;\n"</span></span><span> + &nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"&lt;/BODY&gt;&lt;/HTML&gt;"</span></span><span>); &nbsp;&nbsp;</span> </li>
    <li><span>} &nbsp;&nbsp;</span> </li>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre name="code_lighted" class="java" style="display: none;">package moreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/** Simple servlet used to illustrate servlet naming
* and custom URLs.
* &lt;P&gt;
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* &#169; 2002 Marty Hall; may be freely used or adapted.
*/
public class TestServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String uri = request.getRequestURI();
out.println(ServletUtilities.headWithTitle("Test Servlet") +
"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n" +
"&lt;H2&gt;URI: " + uri + "&lt;/H2&gt;\n" +
"&lt;/BODY&gt;&lt;/HTML&gt;");
}
}</pre>
<br />
程序清单5-2 web.xml（说明servlet名称的摘录） <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码</div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;?xml version=</span><span class="string"><span style="color: #0000ff;">"1.0"</span></span><span> encoding=</span><span class="string"><span style="color: #0000ff;">"ISO-8859-1"</span></span><span>?&gt; &nbsp;&nbsp;</span></span> </li>
    <li><span>&lt;!DOCTYPE web-app &nbsp;&nbsp;</span> </li>
    <li><span>PUBLIC </span><span class="string"><span style="color: #0000ff;">"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"</span></span><span>&nbsp;&nbsp;</span> </li>
    <li><span class="string"><span style="color: #0000ff;">"http://java.sun.com/dtd/web-app_2_3.dtd"</span></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&nbsp;&nbsp;</span> </li>
    <li><span>&lt;web-app&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- &#8230; --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-name&gt;Test&lt;/servlet-name&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt;moreservlets.TestServlet&lt;/servlet-</span><span class="keyword"><strong><span style="color: #7f0055;">class</span></strong></span><span>&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/servlet&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;!-- &#8230; --&gt; &nbsp;&nbsp;</span> </li>
    <li><span>&lt;/web-app&gt;&nbsp;&nbsp;</span> </li>
</ol>
</div>
</div>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/267402.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-04-24 17:13 <a href="http://www.blogjava.net/hulizhong/archive/2009/04/24/267402.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 Java Web中的入侵检测及简单实现</title><link>http://www.blogjava.net/hulizhong/archive/2009/04/17/266178.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Fri, 17 Apr 2009 08:42:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/04/17/266178.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/266178.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/04/17/266178.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/266178.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/266178.html</trackback:ping><description><![CDATA[转 http://blog.csdn.net/zhaowei001/archive/2007/12/29/2001800.aspx<br />
<br />
作者：<a href="http://www.easyjf.com/">EasyJF开源团队</a> 大峡<br />
<br />
<strong>一、简介</strong><br />
<br />
在Java
Web应用程中，特别是网站开发中，我们有时候需要为应用程序增加一个入侵检测程序来防止恶意刷新的功能，防止非法用户不断的往Web应用中重复发送数
据。当然，入侵检测可以用很多方法实现，包括软件、硬件防火墙，入侵检测的策略也很多。在这里我们主要介绍的是Java
Web应用程序中通过软件的方式实现简单的入侵检测及防御。
<p>　　该方法的实现原理很简单，就是用户访问Web系统时记录每个用户的信息，然后进行对照，并根据设定的策略(比如：1秒钟刷新页面10次)判断用户是否属于恶意刷新。</p>
<p>
我们的入侵检测程序应该放到所有Java Web程序的执行前，也即若发现用户是恶意刷新就不再继续执行Java
Web中的其它部分内容，否则就会失去了意义。这就需要以插件的方式把入侵检测的程序置入Java Web应用中，使得每次用户访问Java
Web，都先要到这个入侵检测程序中报一次到，符合规则才能放行。</p>
<p>　　Java Web应用大致分为两种，一种纯JSP(+Java
Bean)方式，一种是基于框架(如Struts、EasyJWeb等)的。第一种方式的Java Web可以通过Java
Servlet中的Filter接口实现，也即实现一个Filter接口，在其doFilter方法中插入入侵检测程序，然后再web.xml中作简单的
配置即可。在基于框架的Web应用中，由于所有应用都有一个入口，因此可以把入侵检测的程序直接插入框架入口引擎中，使框架本身支持入侵检测功能。当然,
也可以通过实现Filter接口来实现。</p>
<p>　　在EasyJWeb框架中，已经置入了简单入侵检测的程序，因此，这里我们以EasyJWeb框架为例,介绍具体的实现方法及源码，完整的代码可以在EasyJWeb源码中找到。</p>
<p>　　在基于EasyJWeb的Java Web应用中(如<a href="http://www.easyjf.com/bbs/">http://www.easyjf.com/bbs/</a>)，默认情况下你只要连续刷新页面次数过多，即会弹出如下的错误：</p>
<p>　　EasyJWeb框架友情提示!:-): <br />
<font color="#ff0000">　　您对页面的刷新太快,请等待60秒后再刷新页面！ <br />
详细请查询<a href="http://www.easyjf.com/" target="_blank">http://www.easyjf.com</a> </font></p>
<p><br />
<strong>二、用户访问信息记录UserConnect.java类</strong>　　<br />
<br />
这个类是一个简单的Java Bean，主要代表用户的信息，包括用户名、IP、第一次访问时间、最后登录时间、登录次数、用户状态等。全部<br />
<br />
代码如下：<br />
<br />
package com.easyjf.web;</p>
<p>import java.util.Date;<br />
/**<br />
*<br />
* </p>
<p>Title:用户验证信息</p>
<br />
*
<p>Description:记录用户登录信息,判断用户登录情况</p>
<br />
*
<p>Copyright: Copyright (c) 2006</p>
<br />
*
<p>Company: <a href="http://www.easyjf.com%3c/p">www.easyjf.com</a></p>
&gt;<br />
* @author 蔡世友<br />
* @version 1.0<br />
*/<br />
public class UserConnect {<br />
private String userName;<br />
private String ip;<br />
private Date firstFailureTime;<br />
private Date lastLoginTime;<br />
private int failureTimes;//用户登录失败次数<br />
private int status=0;//用户状态0表示正常,-1表示锁定<br />
public int getFailureTimes() {<br />
&nbsp;return failureTimes;<br />
}<br />
public void setFailureTimes(int failureTimes) {<br />
&nbsp;this.failureTimes = failureTimes;<br />
}<br />
public Date getFirstFailureTime() {<br />
&nbsp;return firstFailureTime;<br />
}
<p>public void setFirstFailureTime(Date firstFailureTime) {<br />
&nbsp;this.firstFailureTime = firstFailureTime;<br />
}</p>
<p>public String getIp() {<br />
&nbsp;return ip;<br />
}</p>
<p>public void setIp(String ip) {<br />
&nbsp;this.ip = ip;<br />
}</p>
<p>public Date getLastLoginTime() {<br />
&nbsp;return lastLoginTime;<br />
}</p>
<p>public void setLastLoginTime(Date lastLoginTime) {<br />
&nbsp;this.lastLoginTime = lastLoginTime;<br />
}</p>
<p>public String getUserName() {<br />
&nbsp;return userName;<br />
}</p>
<p>public void setUserName(String userName) {<br />
&nbsp;this.userName = userName;<br />
}</p>
<p>public int getStatus() {<br />
&nbsp;return status;<br />
}</p>
<p>public void setStatus(int status) {<br />
&nbsp;this.status = status;<br />
}</p>
<p>}</p>
<p><br />
<strong>三、监控线程UserConnectManage.java类</strong><br />
<br />
这是入侵检测的核心部分，主要实现具体的入侵检测、记录、判断用户信息、在线用户的刷新等功能，并提供其它应用程序使用本组件的调用接口。<br />
<br />
package com.easyjf.web;</p>
<p>import java.util.Date;<br />
import java.util.HashMap;<br />
import java.util.HashSet;<br />
import java.util.Iterator;<br />
import java.util.Map;<br />
import java.util.Set;</p>
<p>import org.apache.log4j.Logger;<br />
/**<br />
*<br />
* </p>
<p>Title:用户入侵检测信息</p>
<br />
*
<p>Description:用于判断用户刷新情况检查，默认为10秒钟之内连续连接10次为超时</p>
<br />
*
<p>Copyright: Copyright (c) 2006</p>
<br />
*
<p>Company: <a href="http://www.easyjf.com%3c/p">www.easyjf.com</a></p>
&gt;<br />
* @author 蔡世友<br />
* @version 1.0<br />
*/<br />
public class UserConnectManage {<br />
private static final Logger logger = (Logger) Logger.getLogger(UserConnectManage.class.getName());<br />
private static int maxFailureTimes=10;//最大登录失败次数<br />
private static long maxFailureInterval=10000;//毫秒，达到最大登录次数且在这个时间范围内<br />
private static long waitInterval=60000;//失败后接受连接的等待时间，默认1分钟<br />
private static int maxOnlineUser=200;//同时在线的最大数<br />
private final static Map users=new HashMap();//使用ip+userName为key存放用户登录信息UserLoginAuth<br />
private static Thread checkThread=null;<br />
private static class CheckTimeOut implements Runnable{&nbsp;<br />
private Thread parentThread;<br />
public&nbsp; CheckTimeOut(Thread parentThread)&nbsp;<br />
&nbsp;{<br />
&nbsp;&nbsp;this.parentThread=parentThread;&nbsp;<br />
&nbsp;&nbsp;synchronized(this){<br />
&nbsp;&nbsp;if(checkThread==null){&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;checkThread= new Thread(this);<br />
&nbsp;&nbsp;&nbsp;//System.out.println("创建一个新线程！");<br />
&nbsp;&nbsp;&nbsp;checkThread.start();&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;}<br />
&nbsp;}&nbsp;<br />
&nbsp;public void run() {&nbsp;&nbsp;<br />
&nbsp;&nbsp;while(true)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;if(parentThread.isAlive()){<br />
&nbsp;&nbsp;&nbsp;try{<br />
&nbsp;&nbsp;&nbsp;Thread.sleep(2000);<br />
&nbsp;&nbsp;&nbsp;int i=0;<br />
&nbsp;&nbsp;&nbsp;if(users.size()&gt;maxOnlineUser)//当达到最大用户数时清除<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;synchronized(users){//执行删除操作<br />
&nbsp;&nbsp;&nbsp;&nbsp;Iterator it=users.keySet().iterator();<br />
&nbsp;&nbsp;&nbsp;&nbsp;Set set=new HashSet();<br />
&nbsp;&nbsp;&nbsp;&nbsp;Date now=new Date();<br />
&nbsp;&nbsp;&nbsp;&nbsp;while(it.hasNext())<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object key=it.next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserConnect user=(UserConnect)users.get(key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(now.getTime()-user.getFirstFailureTime().getTime()&gt;maxFailureInterval)//删除超时的用户<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set.add(key);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info("删除了一个超时的连接"+i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i++;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(i&lt;5)//如果删除少于5个，则强行删除1/2在线记录，牺牲性能的情况下保证内存<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int num=maxOnlineUser/2;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;it=users.keySet().iterator();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while(it.hasNext() &amp;&amp; i<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set.add(it.next());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info("删除了一个多余的连接"+i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i++;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;users.keySet().removeAll(set);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;catch(Exception e)<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;break;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;logger.info("监视程序运行结束！");&nbsp;<br />
&nbsp;}<br />
}<br />
//通过checkLoginValidate判断是否合法的登录连接，如果合法则继续，非法则执行<br />
public static boolean checkLoginValidate(String ip,String userName)//只检查认证失败次数<br />
{<br />
&nbsp;boolean ret=true;<br />
&nbsp;Date now=new Date();&nbsp;<br />
&nbsp;String key=ip+":"+userName;<br />
&nbsp;UserConnect auth=(UserConnect)users.get(key);<br />
&nbsp;if(auth==null)//把用户当前的访问信息加入到users容器中<br />
&nbsp;{<br />
&nbsp;&nbsp;auth=new UserConnect();<br />
&nbsp;&nbsp;auth.setIp(ip);<br />
&nbsp;&nbsp;auth.setUserName(userName);<br />
&nbsp;&nbsp;auth.setFailureTimes(0);<br />
&nbsp;&nbsp;auth.setFirstFailureTime(now);<br />
&nbsp;&nbsp;users.put(key,auth);&nbsp;&nbsp;<br />
&nbsp;&nbsp;if(checkThread==null)new CheckTimeOut(Thread.currentThread());<br />
&nbsp;}&nbsp;<br />
&nbsp;else<br />
&nbsp;{<br />
&nbsp;&nbsp;if(auth.getFailureTimes()&gt;maxFailureTimes)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //如果在限定的时间间隔内，则返回拒绝用户连接的信息<br />
&nbsp;&nbsp;&nbsp;if((now.getTime()-auth.getFirstFailureTime().getTime())<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;ret=false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;auth.setStatus(-1);<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;else&nbsp;
if(auth.getStatus()==-1 &amp;&amp;
(now.getTime()-auth.getFirstFailureTime().getTime()&lt;
(maxFailureInterval+waitInterval)))//重置计数器<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;ret=false;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;else&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;auth.setFailureTimes(0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;auth.setFirstFailureTime(now);<br />
&nbsp;&nbsp;&nbsp;&nbsp;auth.setStatus(0);<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;//登录次数加1<br />
&nbsp;&nbsp;auth.setFailureTimes(auth.getFailureTimes()+1);<br />
&nbsp;}<br />
&nbsp;//System.out.println(key+":"+auth.getFailureTimes()+":"+ret+":"+(now.getTime()-auth.getFirstFailureTime().getTime()));<br />
&nbsp;return ret;<br />
}
<p>public static void reset(String ip,String userName)//重置用户信息<br />
{&nbsp;<br />
&nbsp;Date now=new Date();&nbsp;<br />
&nbsp;String key=ip+":"+userName;<br />
&nbsp;UserConnect auth=(UserConnect)users.get(key);<br />
&nbsp;if(auth==null)//把用户当前的访问信息加入到users容器中<br />
&nbsp;{<br />
&nbsp;&nbsp;auth=new UserConnect();<br />
&nbsp;&nbsp;auth.setIp(ip);<br />
&nbsp;&nbsp;auth.setUserName(userName);<br />
&nbsp;&nbsp;auth.setFailureTimes(0);<br />
&nbsp;&nbsp;auth.setFirstFailureTime(now);<br />
&nbsp;&nbsp;users.put(key,auth);<br />
&nbsp;}&nbsp;<br />
&nbsp;else<br />
&nbsp;{<br />
&nbsp;&nbsp;auth.setFailureTimes(0);<br />
&nbsp;&nbsp;auth.setFirstFailureTime(now);<br />
&nbsp;}<br />
}<br />
public static void remove(String ip,String userName)//删除用户在容器中的记录<br />
{<br />
&nbsp;String key=ip+":"+userName;<br />
&nbsp;users.remove(key);<br />
}<br />
public static void clear()//清空容器中内容<br />
{<br />
&nbsp;if(!users.isEmpty())users.clear();<br />
}<br />
public static long getMaxFailureInterval() {<br />
&nbsp;return maxFailureInterval;<br />
}</p>
<p>public static void setMaxFailureInterval(long maxFailureInterval) {<br />
&nbsp;UserConnectManage.maxFailureInterval = maxFailureInterval;<br />
}</p>
<p>public static int getMaxFailureTimes() {<br />
&nbsp;return maxFailureTimes;<br />
}</p>
<p>public static void setMaxFailureTimes(int maxFailureTimes) {<br />
&nbsp;UserConnectManage.maxFailureTimes = maxFailureTimes;<br />
}</p>
<p>public static int getMaxOnlineUser() {<br />
&nbsp;return maxOnlineUser;<br />
}</p>
<p>public static void setMaxOnlineUser(int maxOnlineUser) {<br />
&nbsp;UserConnectManage.maxOnlineUser = maxOnlineUser;<br />
}</p>
<p>public static long getWaitInterval() {<br />
&nbsp;return waitInterval;<br />
}</p>
<p>public static void setWaitInterval(long waitInterval) {<br />
&nbsp;UserConnectManage.waitInterval = waitInterval;<br />
}</p>
<p><br />
<strong>四、调用接口</strong><br />
<br />
在需要进入侵检测判断的地方，直接使用UserConnectManage类中的checkLoginValidate方法即可。如EasyJWeb的核心Servlet　<br />
<br />
com.easyjf.web.ActionServlet中调用UserConnectManage的代码：<br />
&nbsp; &nbsp;if(!UserConnectManage.checkLoginValidate(request.getRemoteAddr(),"guest"))<br />
&nbsp; &nbsp; &nbsp; &nbsp;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;info(request,response,new Exception("您对页面的刷新太快,请等待"+UserConnectManage.getWaitInterval()/1000+"秒<br />
<br />
后再刷新页面！"));<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return;<br />
&nbsp; &nbsp; &nbsp; &nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<br />
<br />
<br />
<strong>五、总结</strong><br />
当然，这里提供的方法只是一个简单的实现示例，由于上面的用户信息是直接保存在内存中，若并发用户很大的时候的代码的占用，可以考虑引入数据库来记录用
户的访问信息，当然相应的执行效率肯定用降低。上面介绍的实现中，入侵检测判断的策略也只有用户访问次数及时间间隔两个元素，您还可以根据你的实现情况增
加其它的检测元素。<br />
<br />
由于水平有限，该设计上有N不合理或者需要改进的地方，恳请大家指正！</p>
<br />
<br />
<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=657199</p>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/266178.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-04-17 16:42 <a href="http://www.blogjava.net/hulizhong/archive/2009/04/17/266178.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 JSONP的起源</title><link>http://www.blogjava.net/hulizhong/archive/2009/03/24/261764.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Tue, 24 Mar 2009 13:34:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/03/24/261764.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/261764.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/03/24/261764.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/261764.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/261764.html</trackback:ping><description><![CDATA[转 http://www.cn-cuckoo.com/2008/09/13/the-origin-of-jsonp-262.html<br />
<br />
<p>浏览器安全模型规定，XMLHttpRequest、框架（frame）等只能在一个域中通信。从安全角度考虑，这个规定很合理；但是，也确实给分布式（面向服务、混搭等等本周提到的概念）Web开发带来了麻烦。</p>
<p>为了实现跨域通信，通常的解决方案有3种：</p>
<p><strong>本地代理：</strong><br />
需要一些硬件设施（没有服务器的客户端无法运行），并且带宽和潜伏时间也要加倍（远程服务器－代理服务器－客户端）。</p>
<p><strong> Flash：</strong><br />
远程主机中需要部署一个crossdomain.xml文件，而且，Flash作为一门专有技术，其前途尚不明朗；换句话说，开发人员很可能要学习一种目标不确定的编程语言。</p>
<p><strong>Script标签：</strong><br />
无法确切知道内容是否有效，没有标准的实现方法，又可能被认为是一种&#8220;安全风险&#8221;。</p>
<p></p>
<hr />
在此，我建议使用一种新技术，也是一种独立于标准的方法，即通过script标签来跨域获取数据，名为JSON with Padding，或者就叫<strong>JSONP</strong>。JSONP的原理很简单，但需要服务器端给予相应配合。大致来说，JSONP的实现思路就是在客户端编程时作好使用JSON数据的准备，然后再通过圆括号将这些数据括起来以创建一条有效的JavaScript语句（可能是一次有效的函数调用）。
<p>也就是说，客户端可以使用一个用于命名jsonp的查询参数来决定可以获取的数据。最简单的情况下，如果jsonp参数为空，则返回的数据就是被括在圆括号中的JSON。</p>
<p>下面，我们就以<a href="http://del.icio.us/help/json" title="http://del.icio.us/help/json" target="_blank">del.icio.us的JSON API</a>为例，来说明JSONP的原理。该API有一个&#8220;script tag&#8221;变量（即，可以将下面的URL作为script标签的src属性值，用以加载del.icio.us这个API提供的数据。——译者注）如下所示：</p>
<p>http://del.icio.us/feeds/json/bob/mochikit+interpreter:</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="双击隐藏行号" ondblclick="linenumber(this)">
    <li class="hl-firstline">if(typeof(Delicious) == 'undefined') Delicious = {};</li>
    <li>Delicious.posts = [{</li>
    <li>	"u": "http://mochikit.com/examples/interpreter/index.html",</li>
    <li>	"d": "Interpreter - JavaScript Interactive Interpreter",</li>
    <li>	"t": [</li>
    <li>		"mochikit","webdev","tool","tools",</li>
    <li>		"javascript","interactive","interpreter","repl"</li>
    <li>	]</li>
    <li>}]</li>
</ol>
</div>
<p>如果用JSONP的方式来表示，那么与此具有相同语义的URL应该是这样的：</p>
<p>http://del.icio.us/feeds/json/bob/mochikit+interpreter?<br />
jsonp=if(typeof(Delicious)%3D%3D%27undefined%27)<br />
Delicious%3D%7B%7D%3BDelicious.posts%3D</p>
<p>单纯看这个URL似乎没有什么，但我们可以要求服务器在数据有效时给出通知。因此，我可以编写一个用于跟踪数据的小系统：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="双击隐藏行号" ondblclick="linenumber(this)">
    <li class="hl-firstline">var delicious_callbacks = {};</li>
    <li>function getDelicious(callback, url) {</li>
    <li>	var uid = (new Date()).getTime();</li>
    <li>	delicious_callbacks[uid] = function () {</li>
    <li>		delete delicious_callbacks[uid];</li>
    <li>		callback();</li>
    <li>	};</li>
    <li>	url += "?jsonp=" + encodeURIComponent("delicious_callbacks[" + uid + "]");</li>
    <li>	// 手工输入代码，向文档中插入script标签</li>
    <li>};</li>
    <li>&nbsp;</li>
    <li>getDelicious(doSomething, "http://del.icio.us/feeds/json/bob/mochikit+interpreter");</li>
</ol>
</div>
<p>根据以上假设，用于获取数据的URL应该如下所示：<br />
http://del.icio.us/feeds/json/bob/mochikit+interpreter?jsonp=delicious_callbacks%5B12345%5D</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="双击隐藏行号" ondblclick="linenumber(this)">
    <li class="hl-firstline">delicious_callbacks[12345]([{</li>
    <li>	"u": "http://mochikit.com/examples/interpreter/index.html",</li>
    <li>	"d": "Interpreter - JavaScript Interactive Interpreter",</li>
    <li>	"t": [</li>
    <li>		"mochikit","webdev","tool","tools",</li>
    <li>		"javascript","interactive","interpreter","repl"</li>
    <li>	]</li>
    <li>}])</li>
</ol>
</div>
<p>可见，由于使用圆括号括住了返回的数据，这就相当于把一个JSONP请求转化成了一次函数调用，或者得到了一个纯粹的JSON直接量。服务器所要配合做的，就是在JSON数据的开头添加一小段文本（即回调函数的名称。——译者注）并将JSON数据放在括号中！</p>
<p>当然，接下来最好是使用Mochikit、Dojo等框架来抽象JSONP，从而让自己省去动手编写DOM以插入script标签的麻烦。</p>
<p>没错，JSONP只是解决了标准化的问题。假如远程主机想通过script标签向页面中注入恶意代码，而不是返回JSON数据，那么页面安全可能会
随时受到威胁。不过，一旦实现了JSONP，那么对开发人员来说肯定是一件省时省力的大好事，在此基础上各种一般化的抽象、教程及文档也会应运而生的。</p>
<p>注：缩写词 JSONP 由 Bob Ippolito 在一篇名为 &#8220;<a href="http://bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp/" title="http://bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp/" target="_blank">Remote JSON - JSONP</a>&#8221; 的文章中提出。但许多支持以 JSONP 技术实现跨域通信的厂商没有称其为 JSONP。例如，雅虎公司就称这种技术为 &#8220;JSON with callbacks&#8221;。另外，原文发表于2005年12月5日。</p>
<div class="post-info">
九月 13th, 2008 in
<a href="http://www.cn-cuckoo.com/category/web" title="查看 Web开发 的全部文章" rel="category tag">Web开发</a>											</div>
<h1 class="comments-title">留言(9)</h1>
<div class="comment" id="comment-2185">
<div class="comment-avatar">
<img alt="" src="http://www.gravatar.com/avatar/be62fdd065e143bb3b693e32b795b875?s=50&amp;d=http%3A%2F%2Fwww.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D50&amp;r=G" class="avatar avatar-50 photo" width="50" height="50" />				</div>
<div class="comment-content">
<div class="comment-info"><span><a href="http://www.ebook-x.com/" rel="external nofollow" class="url">hello cuckoo</a></span>九月 16th, 2008 at 9:42 上午 </div>
<p>中秋好。<br />
怎么把我的链接去掉了呢？<br />
给我做个链接可以吗？？？<br />
非常感谢！！<br />
我的EMAIL：hardcometure@163.com</p>
</div>
</div>
<div class="comment" id="comment-2186">
<div class="comment-avatar">
<img alt="" src="http://www.gravatar.com/avatar/23fd4332db6748087c7d157ec30c6257?s=50&amp;d=http%3A%2F%2Fwww.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D50&amp;r=G" class="avatar avatar-50 photo" width="50" height="50" />				</div>
<div class="comment-content">
<div class="comment-info"><span>vampire</span>九月 16th, 2008 at 8:41 下午 </div>
<p>大概是说通过script的src来实现跨域，通过经编码的json在url中传递数据？<br />
是不是说页面上的js生产带有不同参数的jsonp，通过src传递给服务端，服务端根据该jsonp返回相应数据？有点不太明白，能否提供一个实例？谢谢</p>
</div>
</div>
<div class="comment" id="comment-2188">
<div class="comment-avatar">
<img alt="" src="http://www.gravatar.com/avatar/f225dfc5180b6ae93d054de8c06b08dc?s=50&amp;d=http%3A%2F%2Fwww.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D50&amp;r=G" class="avatar avatar-50 photo" width="50" height="50" />				</div>
<div class="comment-content">
<div class="comment-info"><span><a href="http://www.cn-cuckoo.com/" rel="external nofollow" class="url">admin</a></span>九月 17th, 2008 at 8:43 下午 </div>
<p>@vampire</p>
<p>你的理解是对的，看来我的翻译你能看明白，呵呵。使用JSONP技术时，一般是由JS在客户端页面中动态插入script标签，将其src属性设置
为带参数的URL。当页面加载时，前述URL会将参数通过GET请求发送到相应服务器端应用程序（由URL表示），服务器根据参数返回数据——注意，这个
数据格式是JSON，并且（注意）被包含在一个函数调用中，例如：callback({json_data})。这样，在客户端页面中存在预定义的
callback(data)函数的定义时，服务器返回的函数调用就会立即执行，由客户端的函数对数据进行操作。</p>
<p>实际的例子有很多，如Yahoo Search、Google Base、Flickr Search、Amazon
Search等等。要学习和使用这些站点提供的支持JSONP的API，一般要注意两点：一是有的站点要求注册密钥（使用时必须放到参数中），二是要注意
参数的使用方法。例如，有的API要求使用预定义的回调函数，而有的API则允许使用者自己定义回调函数。</p>
<p>下面就是向Flickr Search请求JSONP响应的URL，其中使用了预定义的回调函数jsonFlickrApi（参数中不必给出）：</p>
<p><a href="http://api.flickr.com/services/rest/" rel="nofollow">http://api.flickr.com/services/rest/</a><br />
?method=flickr.photos.search<br />
&amp;api_key=85618ad7d326d8ef93c6bee9ed32706f<br />
&amp;per_page=5&amp;format=json&amp;text=china</p>
<p>下面这个URL发送到Google Base，它允许开发人员使用自己定义的回调函数：</p>
<p><a href="http://www.google.com/base/feeds/snippets" rel="nofollow">http://www.google.com/base/feeds/snippets</a><br />
?q=jquery<br />
&amp;alt=json-in-script<br />
&amp;callback=customCallback</p>
<p>把前面的URL放到浏览器地址栏中，回车，即可看到结果。</p>
</div>
</div>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/261764.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-03-24 21:34 <a href="http://www.blogjava.net/hulizhong/archive/2009/03/24/261764.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>cache</title><link>http://www.blogjava.net/hulizhong/archive/2009/03/22/261319.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Sun, 22 Mar 2009 07:55:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/03/22/261319.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/261319.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/03/22/261319.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/261319.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/261319.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 转 http://www.mnot.net/cache_docs/    What&#8217;s a Web Cache? Why do people use    them?    Kinds of Web Caches            Browser Caches        Proxy Caches            Aren&#82...&nbsp;&nbsp;<a href='http://www.blogjava.net/hulizhong/archive/2009/03/22/261319.html'>阅读全文</a><img src ="http://www.blogjava.net/hulizhong/aggbug/261319.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-03-22 15:55 <a href="http://www.blogjava.net/hulizhong/archive/2009/03/22/261319.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 REST反模式</title><link>http://www.blogjava.net/hulizhong/archive/2009/03/22/261316.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Sun, 22 Mar 2009 07:36:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/03/22/261316.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/261316.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/03/22/261316.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/261316.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/261316.html</trackback:ping><description><![CDATA[转 http://www.infoq.com/cn/articles/rest-anti-patterns<br />
<br />
&nbsp;&nbsp; 人们在试验REST时，通常会四处寻找样例——而他们往往不仅能找到一大堆自称&#8220;符合REST&#8221;或标榜为&#8220;REST API&#8221;的样例，还会发现许多关于某个自称符合REST的特定服务名不副实的讨论。&nbsp; <br />
&nbsp;&nbsp; <br />
<p>为什么会这样？HTTP虽不是什么新事物，但人们使用它的方式却五花八门。其中有些做法符合Web设计者的初衷，但许多并非如此。要为你的HTTP
应用（无论是面向人类、还是计算机、或同时面向这两者使用的）应用REST原则，意味着你要恰好反过来：尽量&#8220;正确地&#8221;使用Web，或者说按符合REST
的方式使用Web（倘若你不喜欢用对或错来评判的话）。对许多人来说，这的确是一种崭新的方式方法。</p>
<p>我经常在文章里作同样的声明：REST、Web和HTTP是不同的事物；REST可以用多种不同技术来实现，而HTTP只是一种恰好符合REST架
构风格的具体架构。所以，其实我应该小心区分&#8220;REST&#8221;与&#8220;REST式HTTP&#8221;这两个概念的。但我没有这么做，在本文剩余部分，我们姑且认为它们是相
同的事物。</p>
<p>跟任何新的方式方法一样，发掘一些共同的模式是有益的。在本系列的<a href="http://www.infoq.com/articles/rest-introduction">第一</a>和<a href="http://www.infoq.com/articles/tilkov-rest-doubts">第二</a>篇
文章中，我已经讲述了一些基础——比如集合资源的概念、将计算结果转换为资源本身、以及用聚合（syndication）来模仿事件。后续文章将进一步讲
述这些及其他模式。不过在本文中，我想主要说说反模式（anti-patterns）——即那些力求符合REST式HTTP、但未能成功而造成问题的典型
做法。</p>
<p>首先我们来看看我发掘了哪些反模式：</p>
<ol>
    <li>全部采用GET</li>
    <li>全部采用POST</li>
    <li>忽视缓存</li>
    <li>忽视响应代码</li>
    <li>误用cookies</li>
    <li>忘记超媒体</li>
    <li>忽视MIME类型</li>
    <li>破坏自描述性</li>
</ol>
<p>下面我们来逐个详细说明。 </p>
<h2>全部采用GET</h2>
<p>在许多人看来，REST仅仅意味着用HTTP暴露一些应用功能。HTTP GET是最重要的基本操作（operation
）（严格地讲，用&#8220;动词（verb）&#8221;或&#8220;方法（method）&#8221;这样的术语比较好）。GET方法应当用于获取由URI标识的资源的一个表示
（representation），而许多（即便谈不上所有）现有的HTTP库和服务器编程API不是将URI视为一种资源标识符（resource
identifier），而是将之视为一种传递参数的便利手段。这导致了以下这种URIs的出现：</p>
<pre><code>http://example.com/some-api?method=deleteCustomer&amp;id=1234<br />
</code></pre>
<p>实际上，你无法根据构成URI的字符获知关于给定系统的&#8220;REST性（RESTfulness）&#8221;的任何信息，不过对于上面那个URI，我们可以判
断该
GET操作不是&#8220;安全的（safe）&#8221;——也就是说，调用者很可能要为结果（删除一个客户）负责，尽管规范里说在这种情况下使用GET方法是错误的。</p>
<p>这种做法唯一有利的方面在于它编程起来容易，而且在浏览器中调试也简单——你只要把URI粘贴到浏览器地址栏里、然后调整一些&#8220;参数&#8221;就行了。这种反模式主要存在以下问题： </p>
<ol>
    <li>URI没有被用作资源标识符，而是被用于传递操作及其参数了。</li>
    <li>HTTP方法（HTTP method）不一定跟语义相符。</li>
    <li>这种链接一般不可加入书签。</li>
    <li>有&#8220;爬虫&#8221;造成非预期副作用的风险。</li>
</ol>
<p>注意：符合这一反模式的APIs没准最终<a href="http://www.markbaker.ca/blog/2005/04/14/accidentally-restful/" title="Web Things, by Mark Baker    &#187; &#187; Accidentally RESTful">碰巧符合REST原则</a>。这里有个例子：</p>
<pre><code>http://example.com/some-api?method=findCustomer&amp;id=1234<br />
</code></pre>
<p>这个URI是标识操作及其参数呢，还是标识一个资源呢？两种情况都有可能：它可以是一个完全合法的、可加入书签的URI；对它做GET操作也许是&#8220;
安全的
&#8221;；它也许会根据Accept报头返回不同的格式，并支持复杂的缓存机制。在很多情况下，这将是偶然的。API经常在刚开始时采用这种方式来暴露一个&#8220;读
&#8221;接口，但当开发者要增添&#8220;写&#8221;功能时就有问题了（因为你无法通过对上述URI做PUT操作来更新一个客户——开发者得构造另一个URI）。</p>
<h2>全部采用POST</h2>
<p>这一反模式跟前一个颇为相似，只不过这里用的是POST方法而已。POST除了携带一个URI，还携带一个实体主体（entity
body）。一个典型的场景是：将单个URI作为POST请求的目标、通过发送不同的消息来表达不同的意图。实际上，SOAP 1.1
Web服务就是这样做的，它把HTTP当作一种&#8220;传输协议&#8221;来用。服务器根据SOAP消息（可能还包括一些WS-Addressing
SOAP报头）决定做什么。</p>
<p>可能有人认为&#8220;全部采用POST&#8221;跟&#8220;全部采用GET&#8221;存在的问题完全一样，只是它更难用一些，而且不能利用缓存（甚至连偶尔的机会都没有），且无法支持书签。事实上，它并不是违反了哪条REST原则，而是根本忽视了REST原则。 </p>
<h2>忽视缓存</h2>
<p>即使你按各个动词的原本意图来使用它们，你仍可以轻易禁止缓存机制。最简单的做法就是在你的HTTP响应里增加这样一个报头： </p>
<pre><code>Cache-control: no-cache<br />
</code></pre>
<p>这样可以禁止缓存机制发挥作用。当然，这也许正是你想要做的，然而通常这只是你的Web框架规定的一个缺省设置。不过，对高效的缓存与再验证
（caching and re-validation）的支持，是采用REST式HTTP的诸多关键优点之一。Sam
Ruby表示，在判断是否符合REST原则时的一个关键问题就是&#8220;<a href="http://www.infoq.com/articles/richardson-ruby-restful-ws" title="InfoQ: Interview and Book Excerpt: RESTful Web Services">你支持ETag吗</a>&#8221;？
（ETag是HTTP
1.1里引入的一种机制，它允许客户端通过加密的校验和来验证一个被缓存的表示是否仍然有效）。要生成正确的报头，最简单的做法就是把这个任务交给一个&#8220;
知道&#8221;怎样做的基础设施——例如通过在Web服务器（比如Apache HTTPD）的目录里生成一个文件。</p>
<p>当然，这也要涉及到客户端一方：你在为一个REST式服务实现程序客户端时，你应充分利用现有的缓存机制，以免每次都重新获取表示。例如，服务器也
许已经发出信息：初次返回的表示在600秒内都可被认为是&#8220;新的&#8221;（比方说因为后端系统每30分钟才轮询一次）。这样的话，短时间内重复请求同一信息就完
全没必要了。在客户端设置一个代理缓存（比如Squid）也许比自行构建相应逻辑更好。</p>
<p>HTTP的缓存机制强大而复杂；<a href="http://www.mnot.net/cache_docs/" title="Caching Tutorial for Web Authors and Webmasters">Mark Nottingham的《缓存指南（Cache Tutorial）》</a>是一个很好的指南。 </p>
<h2>忽视响应代码</h2>
<p>HTTP提供了一组丰富的<a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html" title="HTTP/1.1: Status Code Definitions">应用级状态代码</a>，
它们可用于应付不同场合，不过许多Web开发者对此并不知晓。大部分人对200（&#8220;OK&#8221;）、404（&#8220;Not
found&#8221;）和500（&#8220;Internal server
error&#8221;）这些状态代码是比较熟悉的。但除此以外还有很多其他状态代码，正确使用这些状态代码意味着客户端与服务器可以在一个具备较丰富语义的层次上
进行沟通。</p>
<p>例如，201（&#8220;Created&#8221;）响应代码表明已经创建了一个新的资源，其URI在Location响应报头里。409（&#8220;Conflict&#8221;）
告诉客户端存在冲突，比如随PUT请求发送的是基于老版本资源的数据。再如，412（&#8220;Precondition
Failed&#8221;）表明服务器不能满足客户端的预期。</p>
<p>正确使用状态代码的另一方面涉及客户端：应该根据一种统一的总体方法对不同类别的状态代码（例如所有2xx段代码、所有5xx段代码）作不同处理——例如，即便客户端不具备处理特定代码的逻辑，但至少应把所有2xx段代码视为成功信号。</p>
<p>许多声称符合REST的应用仅仅返回200或500，甚至只返回200（并在响应实体主体里给出错误文本——SOAP就是这样的）。你要是愿意，可
以称之为&#8220;通过状态代码200传达错误&#8221;，但无论你觉得采用哪个术语好，假如你不利用HTTP状态代码丰富的应用语义，那么你将错失提高重用性、增强互操
作性和提升松耦合性的机会。</p>
<h2>误用cookies</h2>
<p id="scroll_to_here">利用cookies来传播某个服务端会话状态的键（key）是另一种REST反模式。 </p>
<p>Cookies表明肯定哪个地方不符合REST了。是这样吗？不；不一定。REST的关键思想之一是无状态性（statelessness）——不
是说一个服务器不能保存任何数据：倘若是资源状态（resource state）或客户端状态（client
state），那是可以的。服务器不能保存的是会话状态（session
state），因为那会造成可伸缩性、可靠性及耦合方面的问题。Cookies的最典型的用法是：保存一个跟&#8220;某个保存在服务端内存里的数据结构&#8221;相关联
的键（key）。这意味着，浏览器随各次请求发出去的cookie是被用于构建会话状态的。</p>
<p>如果一个cookie被用于保存一些&#8220;服务器不依赖于会话状态即可验证&#8221;的信息（比如认证令牌），那么这样的cookies是完全符合REST原则
的——
不过有一点需要注意：如果有其他更为标准的方式来传递一则信息（比如放在URI里、放在某个标准报头里、或较少见地放在消息主体里），那就不应将之放在
cookie里。例如，按REST式HTTP的观点来使用HTTP认证就比较好。</p>
<h2>忘记超媒体</h2>
<p>最不易接受的REST思想就是标准的方法集合。REST理论并没有规定标准集合由哪些方法组成，它只是规定必须有一组适用于所有资源的方法集合。对
于
HTTP来说，这组集合是GET、PUT、POST和DELETE（至少起初是这样），你需要一定适应时间才能掌握如何将所有应用语义投射到这四个动词
上。但你一旦适应了，就可以开始运用这个REST的子集——一种基于Web的CRUD（Create、Read、Update、
Delete）架构——了。暴露这种反模式的应用不是真正的&#8220;非REST式&#8221;应用（假如存在这种事物的话），它们只是未能利用一个REST核心概念——&#8220;
超媒体即应用状态引擎（hypermedia as the engine of application state）&#8221;。</p>
<p>超媒体（hypermedia）是一个把事物链接起来的概念，正是它造就了Web这个网——一个互联的资源集合，应用通过跟随链接从一个状态进入另一个状态。这听上去也许有点深奥，不过其实遵从这一原则是有正当理由的。 </p>
<p>&#8220;忘记超媒体&#8221;反模式的首要表现就是：表示（representation）里缺少链接。尽管通常客户端可以根据一定的规则来构造URI，但是因为
服务器没有发送任何链接，所以客户端将无法跟随链接。一种较好的做法是：即支持构造URI，又支持跟随链接——这里的链接通常反映了下层数据模型中的关
系。但最好的情况是：客户端应该只需知道一个URI；其他URI（各个URI及其构造模式，如：各种查询字符串）应该通过超媒体（作为资源表示里的链接）
来传达。 Atom发布协议（Atom Publishing Protocol）就是一个好例子，它有一个服务文档（service
documents）的概念，服务文档为它所描述的域内的各个集合提供具名元素（named
elements）。最后，应用可能经历的状态迁移应该是动态传播的，客户端应该可以不用掌握多少知识就可以跟随它们。HTML就是一个好榜样，它包含足
够的信息，以便浏览器可以向用户提供一个完全动态的接口。</p>
<p>我本想增加一个&#8220;人类可读的URI&#8221;反模式的。但我没那么做，因为我跟其他人一样也喜欢可读的、好&#8220;篡改&#8221;的URI。但是当人们采用REST时，他
们经常浪费许多时间来讨论&#8220;正确的&#8221;URI设计，而忘记了超媒体方面。所以，我建议你不要花太多时间来寻找正确的URI设计（毕竟，它们只是字符串而
已），而是多花一些精力在表示里寻找提供链接的正确地方。</p>
<h2>忽视MIME类型</h2>
<p>HTTP有个内容协商（content
negotiation）的概念，它允许客户端根据需要获取资源的不同表示（representations）。例如，一个资源也许有不同格式的表示（如
XML、JSON或YAML等）以便于用各种不同语言（如Java、JavaScript及Ruby）实现的消费者所使用。再如，一个资源可能即有面向人
类的PDF或JPEG版表示，又有&#8220;机器可读的&#8221;XML版表示。还有，一个资源可能同时支持v1.1版和v1.2版的自定义表示格式。不管怎样，也许可以
为&#8220;只有一个表示格式&#8221;找到理由，但这常常意味着丢掉某种机会。</p>
<p>显然，若一个服务能为更多未预见到的客户端所用（或重用）那更好。因此，依靠现有、预定义、广为人知的格式，要好过发明私有格式——这会导致本文讲述的最后一个反模式。</p>
<h2>破坏自描述性</h2>
<p>这种反模式是如此普遍，以至于几乎在每个、甚至那些由所谓的&#8220;REST狂热者们&#8221;（包括我在内）创建的REST应用里都可以看到：违反自描述性约束
（这一努力目标并不像人们最初想象的那样跟人工智能科幻小说有多大牵连）。理想情况下，一个消息（HTTP
请求或HTTP响应，包括报头与主体）应该包含足够信息，以便任何通用客户端、服务器或媒介（intermediary）能够处理它。例如，当你的浏览器
获取某个受保护资源的PDF表示（representation）时，你可以看到由标准达成的协定是如何起作用的：有些HTTP认证交换发生，可能会发生
一些缓存（caching）和/或再验证（revalidation），服务器发送的content-type报头（<a href="http://www.ietf.org/rfc/rfc3778.txt" title="RFC 3778: The application/pdf Media Type">application/pdf</a>）触发了你系统里注册的PDF阅读器，最后你得以在自己的屏幕上阅读该PDF。所有用户都可以用他们自己的基础设施来执行同样的请求。若服务器开发者另外增加一种内容类型，那么服务器的客户端（或服务的消费者）只需确保他们安装了正确的阅读器即可。</p>
<p>你要是发明自己的报头、格式或协议，那就一定程度上破坏了自描述性约束。极端地讲，所有没有被某个标准化组织官方标准化的东西都违反此约束，因而可
被认为符合本反模式。在实践中，你应努力做到尽可能遵循标准，并懂得&#8220;某些协定可能只在一个较小的领域（比方说，你的服务和客户端是专门针对它开发的）中
适用&#8221; 的道理。</p>
<h2>总结</h2>
<p>自从&#8220;四人组（Gang of Four）&#8221;出版了<a href="http://en.wikipedia.org/wiki/Design_Patterns" title="Design Patterns - Wikipedia, the free encyclopedia">书籍</a>、
掀起模式运动的开端以来，许多人误解了它，并试图在尽可能多的场合下应用模式——这已被其他人所取笑。模式应当仅在符合上下文时才被应用。同样地，可能有
人会不遗余力地在所有场合下虔诚地努力避免所有反模式。许多时候，你有充分理由违反某一规则，或者按REST的术语放松某一约束。这么做是没问题的——但
了解实际情况、作出知情决策是有益的。</p>
<p>但愿本文能有助于你在开始首个REST项目时避免落入这些常见的陷阱。 </p>
<p>非常感谢Javier Botana和Burkhard Neppert对本文初稿的反馈。 </p>
<p><a href="http://www.innoq.com/blog/st/">Stefan Tilkov</a>是InfoQ SOA社区的首席编辑，以及位于德国/瑞士的<a href="http://www.innoq.com/">innoQ</a>公司的合伙人、首席顾问和主要的REST狂热主义者。</p>
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/261316.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-03-22 15:36 <a href="http://www.blogjava.net/hulizhong/archive/2009/03/22/261316.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 深入浅出REST</title><link>http://www.blogjava.net/hulizhong/archive/2009/03/22/261311.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Sun, 22 Mar 2009 06:30:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/03/22/261311.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/261311.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/03/22/261311.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/261311.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/261311.html</trackback:ping><description><![CDATA[转 http://www.infoq.com/cn/articles/rest-introduction<br />
<br />
&nbsp;&nbsp;&nbsp; 不知你是否意识到，围绕着什么才是实现异构的应用到应用通信的&#8220;正确&#8221;方式，一场争论正进行的如火如荼：虽然当前主流的方式明显地集中在基于SOAP、
WSDL和WS-*规范的Web
Services领域，但也有少数人用细小但洪亮的声音主张说更好的方式是REST，表述性状态转移（REpresentational State
Transfer）的简称。在本文中，我不会涉及争论的话题，而是尝试对REST和RESTful
HTTP应用集成做实用性的介绍。以我的经验，有些话题一旦触及就会引来众多的讨论，当涉及到这方面话题的时候，我会深入详细地阐述。<br />
&nbsp;&nbsp;&nbsp; <br />
<h2>REST关键原则</h2>
<p>大部分对REST的介绍是以其正式的定义和背景作为开场的。但这儿且先按下不表，我先提出一个简单扼要的定义：REST定义了应该如何正确地使用
（这和大多数人的实际使用方式有很大不同）Web标准，例如HTTP和URI。如果你在设计应用程序时能坚持REST原则，那就预示着你将会得到一个使用
了优质Web架构（这将让你受益）的系统。总之，五条关键原则列举如下：</p>
<ul>
    <li>为所有&#8220;事物&#8221;定义ID  </li>
    <li>将所有事物链接在一起  </li>
    <li>使用标准方法  </li>
    <li>资源多重表述  </li>
    <li>无状态通信 </li>
</ul>
<p>下面让我们进一步审视这些原则。</p>
<h2>为所有&#8220;事物&#8221;定义ID </h2>
<p>在这里我使用了&#8220;事物&#8221;来代替更正式准确的术语&#8220;资源&#8221;，因为一条如此简单的原则，不应该被淹没在术语当中。思考一下人们构建的系统，通常会找到一
系列值得被标识的关键抽象。每个事物都应该是可标识的，都应该拥有一个明显的ID——在Web中，代表ID的统一概念是：URI。URI构成了一个全局命
名空间，使用URI标识你的关键资源意味着它们获得了一个唯一、全局的ID。 </p>
<p>对事物使用一致的命名规则（naming
scheme）最主要的好处就是你不需要提出自己的规则——而是依靠某个已被定义，在全球范围中几乎完美运行，并且能被绝大多数人所理解的规则。想一下你
构建的上一个应用（假设它不是采用RESTful方式构建的）中的任意一个高级对象（high-level
object），那就很有可能看到许多从使用唯一标识中受益的用例。比如，如果你的应用中包含一个对顾客的抽象，那么我可以相当肯定，用户会希望将一个指
向某个顾客的链接，能通过电子邮件发送到同事那里，或者加入到浏览器的书签中，甚至写到纸上。更透彻地讲：如果在一个类似于Amazon.com的在线商
城中，没有用唯一的ID（一个URI）标识它的每一件商品，可想而知这将是多么可怕的业务决策。</p>
<p>当面对这个原则时，许多人惊讶于这是否意味着需要直接向外界暴露数据库记录（或者数据库记录ID）——自从多年以来面向对象的实践告诫我们，要将持
久化的信息作为实现细节隐藏起来之后，哪怕是刚有点想法都常会使人惊恐。但是这条原则与隐藏实现细节两者之间并没有任何冲突：通常，值得被URI标识的事
物——资源——要比数据库记录抽象的多。例如，一个定单资源可以由定单项、地址以及许多其它方面（可能不希望作为单独标识的资源暴露出来）组成。标识所有
值得标识的事物，领会这个观念可以进一步引导你创造出在传统的应用程序设计中不常见的资源：一个流程或者流程步骤、一次销售、一次谈判、一份报价请求——
这都是应该被标识的事物的示例。同样，这也会导致创建比非RESTful设计更多的持久化实体。 </p>
<p>下面是一些你可能想到的URI的例子：</p>
<pre>http://example.com/customers/1234<br />
http://example.com/orders/2007/10/776654<br />
http://example.com/products/4554<br />
http://example.com/processes/salary-increase-234 </pre>
<p>正如我选择了创建便于阅读的URI——这是个有用的观点，尽管不是RESTful设计所必须的——应该能十分容易地推测出URI的含义：它们明显地标识着单一&#8220;数据项&#8221;。但是再往下看：</p>
<pre>http://example.com/orders/2007/11<br />
http://example.com/products?color=green </pre>
<p>首先，这两个URI看起来与之前的稍有不同——毕竟，它们不是对一件事物的标识，而是对一类事物集合的标识（假定第一个URI标识了所有在2007年11月份提交的定单，第二个则是绿颜色产品的集合）。但是这些集合自身也是事物（资源），也应该被标识。</p>
<p>注意，使用唯一、全局统一的命名规则的好处，既适用于浏览器中的Web应用，也适用于机对机（machine-to-machine，m2m）通信。</p>
<p>来对第一个原则做下总结：使用URI标识所有值得标识的事物，特别是应用中提供的所有&#8220;高级&#8221;资源，无论这些资源代表单一数据项、数据项集合、虚拟亦或实际的对象还是计算结果等。</p>
<h2>将所有事物链接在一起 </h2>
<p>接下来要讨论的原则有一个有点令人害怕的正式描述：&#8220;超媒体被当作应用状态引擎（Hypermedia as the engine of application state）&#8221;，有时简写为HATEOAS。（严格地说，这不是我说的。）这个描述的核心是<strong>超媒体</strong>概念，换句话说：是<strong>链接</strong>的思想。链接是我们在HTML中常见的概念，但是它的用处绝不局限于此（用于人们网络浏览）。考虑一下下面这个虚构的XML片段： </p>
<pre><order self="http://example.com/customers/1234"> <br />
<amount>23</amount> <br />
<product ref="http://example.com/products/4554"> <br />
<customer ref="http://example.com/customers/1234"> <br />
</customer> </product>
</order></pre>
<p>如果你观察文档中product和customer的链接，就可以很容易地想象到，应用程序（已经检索过文档）如何&#8220;跟随&#8221;链接检索更多的信息。当然，如果使用一个遵守专用命名规范的简单&#8220;id&#8221;属性作为链接，也是可行的——<strong>但是仅限于应用环境之内</strong>。使用URI表示链接的优雅之处在于，链接可以指向由不同应用、不同服务器甚至位于另一个大陆上的不同公司提供的资源——因为URI命名规范是全球标准，构成Web的所有资源都可以互联互通。</p>
<p>超媒体原则还有一个更重要的方面——应用&#8220;状态&#8221;。简而言之，实际上服务器端（如果你愿意，也可以叫服务提供者）为客户端（服务消费者）提供一组链
接，使客户端能通过链接将应用从一个状态改变为另一个状态。稍后我们会在另一篇文章中探究这个方面的影响；目前，只需要记住：链接是构成动态应用的非常有
效的方式。</p>
<p>对此原则总结如下：任何可能的情况下，使用链接指引可以被标识的事物（资源）。也正是超链接造就了现在的Web。</p>
<h2>使用标准方法 </h2>
<p>在前两个原则的讨论中暗含着一个假设：接收URI的应用程序可以通过URI明确地<strong>做</strong>一些有意义的事情。如果你在公共汽车上看到一个URI，你可以将它输入浏览器的地址栏中并回车——但是你的浏览器如何知道需要对这个URI做些什么呢？</p>
<p>它知道如何去处理URI的原因在于所有的资源都支持同样的接口，一套同样的方法（只要你乐意，也可以称为操作）集合。在HTTP中这被叫做动词
（verb），除了两个大家熟知的（GET和POST）之外，标准方法集合中还包含PUT、DELETE、HEAD和OPTIONS。这些方法的含义连同
行为许诺都一起定义在HTTP规范之中。如果你是一名OO开发人员，就可以想象到RESTful
HTTP方案中的所有资源都继承自类似于这样的一个类（采用类Java、C#的伪语法描述，请注意关键的方法）：</p>
<pre>class Resource {<br />
Resource(URI u);<br />
Response get();<br />
Response post(Request r);<br />
Response put(Request r);<br />
Response delete();<br />
} </pre>
<p>由于所有资源使用了同样的接口，你可以依此使用GET方法检索一个<strong>表述</strong>（representation）——也
就是对资源的描述。因为规范中定义了GET的语义，所以可以肯定当你调用它的时候不需要对后果负责——这就是为什么可以&#8220;安全&#8221;地调用此方法。GET方法
支持非常高效、成熟的缓存，所以在很多情况下，你甚至不需要向服务器发送请求。还可以肯定的是，GET方法具有<strong>幂等性</strong>[译
注：指多个相同请求返回相同的结果]——如果你发送了一个GET请求没有得到结果，你可能不知道原因是请求未能到达目的地，还是响应在反馈的途中丢失了。
幂等性保证了你可以简单地再发送一次请求解决问题。幂等性同样适用于PUT（基本的含义是&#8220;更新资源数据，如果资源不存在的话，则根据此URI创建一个新
的资源&#8221;）和DELETE（你完全可以一遍又一遍地操作它，直到得出结论——删除不存在的东西没有任何问题）方法。POST方法，通常表示&#8220;创建一个新资
源&#8221;，也能被用于调用任<font color="#000000">意</font><font color="#000000">过程</font>，因而它既不安全也不具有幂等性。</p>
<p>如果你采用RESTful的方式暴露应用功能（如果你乐意，也可以称为服务功能），<strong>那这条原则和它的约束同样也适用于你</strong>。如果你已经习惯于另外的设计方式，则很难去接受这条原则——毕竟，你很可能认为你的应用包含了超出这些操作表达范围的逻辑。请允许我花费一些时间来让你相信不存在这样的情况。 </p>
<p>来看下面这个简单的采购方案例子： </p>
<p><img alt="Sample Scenario" _href="img://figure1.jpg" src="http://www.infoq.com/resource/articles/rest-introduction/zh/resources/figure1.jpg" width="200" /></p>
<p>可以看到，例子中定义了两个服务程序（没有包含任何实现细节）。这些服务程序的接口都是为了完成任务（正是我们讨论的
OrderManagement和CustomerManagement服务）而定制的。如果客户端程序试图使用这些服务，那它必须针对这些特定接口进行
编码——不可能在这些接口定义之前，使用客户程序去有目的地和接口协作。这些接口定义了服务程序的应用协议（application
protocol）。</p>
<p>在RESTful HTTP方式中，你将通过组成<strong>HTTP应用协议</strong>的通用接口访问服务程序。你可能会想出像这样的方式：</p>
<p><img alt="Sample Scenario, done RESTfully" _href="img://figure2.jpg" src="http://www.infoq.com/resource/articles/rest-introduction/zh/resources/figure2.jpg" width="400" /></p>
<p>可以看到，服务程序中的特定操作被映射成为标准的HTTP方法——为了消除歧义，我创建了一组全新的资源。&#8220;这是骗人的把戏&#8221;，我听见你叫嚷着。
不，这不是欺骗。标识一个顾客的URI上的GET方法正好相当于getCustomerDetails操作。有人用三角形形象化地说明了这一点：</p>
<p><img alt="Knobs one can turn" _href="img://figure3.jpg" src="http://www.infoq.com/resource/articles/rest-introduction/zh/resources/figure3.jpg" width="200" /></p>
<p>把三个顶点想象为你可以调节的按钮。可以看到在第一种方法中，你拥有许多操作，许多种类的数据以及固定数量的&#8220;实例&#8221;（本质上和你拥有的服务程序数
量一致）。在第二种方法中，你拥有固定数量的操作，许多种类的数据和许多调用固定方法的对象。它的意义在于，证明了通过这两种方式，你基本上可以表示任何
你喜欢的事情。</p>
<p>为什么使用标准方法如此重要？从根本上说，它使你的应用成为Web的一部分——应用程序为Web变成Internet上最成功的应用所做的贡献，与
它添加到Web中的资源数量成比例。采用RESTful方式，一个应用可能会向Web中添加数以百万计的客户URI；如果采用CORBA技术并维持应用的
原有设计方式，那它的贡献大抵只是一个&#8220;端点（endpoint）&#8221;——就好比一个非常小的门，仅仅允许有钥匙的人进入其中的资源域。</p>
<p>统一接口也使得所有理解HTTP应用协议的组件能与你的应用交互。通用客户程序（generic client）就是从中受益的组件的例子，例如curl、wget、代理、缓存、HTTP服务器、网关还有Google、Yahoo!、MSN等等。 </p>
<p>总结如下：为使客户端程序能与你的资源相互协作，资源应该正确地实现默认的应用协议（HTTP），也就是使用标准的GET、PUT、POST和DELETE方法。</p>
<h2>资源多重表述 </h2>
<p>到目前为止我们一直忽略了一个稍微复杂的问题：客户程序如何知道该怎样处理检索到的数据，比如作为GET或者POST请求的结果？原因是，HTTP
采取的方式是允许数据处理和操作调用之间关系分离的。换句话说，如果客户程序知道如何处理一种特定的数据格式，那就可以与所有提供这种表述格式的资源交
互。让我们再用一个例子来阐明这个观点。利用HTTP内容协商（content negotiation），客户程序可以请求一种特定格式的表述：</p>
<pre>GET /customers/1234 HTTP/1.1<br />
Host: example.com <br />
Accept: application/vnd.mycompany.customer+xml  </pre>
<p>请求的结果可能是一些由公司专有的XML格式表述的客户信息。假设客户程序发送另外一个不同的请求，就如下面这样：</p>
<pre>GET /customers/1234 HTTP/1.1<br />
Host: example.com <br />
Accept: text/x-vcard </pre>
<p>结果则可能是VCard格式的客户地址。（在这里我没有展示响应的内容，在其HTTP
Content-type头中应该包含着关于数据类型的元数据。）这说明为什么理想的情况下，资源表述应该采用标准格式——如果客户程序对HTTP应用协
议和一组数据格式都有所&#8220;了解&#8221;，那么它就可以用一种有意义的方式<strong>与世界上任意一个RESTful HTTP应用交互</strong>。
不幸的是，我们不可能拿到所有东西的标准格式，但是，或许我们可以想到在公司或者一些合作伙伴中使用标准格式来营造一个小环境。当然以上情况不仅适用于从
服务器端到客户端的数据，反之既然——倘若从客户端传来的数据符合应用协议，那么服务器端就可以使用特定的格式处理数据，而不去关心客户端的类型。 </p>
<p id="scroll_to_here">在实践中，资源多重表述还有着其它重要的好处：如果你为你的资源提供HTML和XML两种表述方式，那这些资源不仅可以被你的应用所用，还可以被任意标准Web浏览器所用——也就是说，你的应用信息可以被所有会使用Web的人获取到。</p>
<p>资源多重表述还有另外一种使用方式：你可以将应用的Web UI纳入到Web
API中——毕竟，API的设计通常是由UI可以提供的功能驱动的，而UI也是通过API执行动作的。将这两个任务合二为一带来了令人惊讶的好处，这使得
使用者和调用程序都能得到更好的Web接口。</p>
<p>总结：针对不同的需求提供资源多重表述。</p>
<h2>无状态通信</h2>
<p><strong>无状态通信</strong>是我要讲到的最后一个原则。首先，需要着重强调的是，虽然REST包含无状态性（statelessness）的观念，但这并不是说暴露功能的应用不能有状态——<br />
事
实上，在大部分情况下这会导致整个做法没有任何用处。REST要求状态要么被放入资源状态中，要么保存在客户端上。或者换句话说，服务器端不能保持除了单
次请求之外的，任何与其通信的客户端的通信状态。这样做的最直接的理由就是可伸缩性——
如果服务器需要保持客户端状态，那么大量的客户端交互会严重影响服务器的内存可用空间（footprint）。（注意，要做到无状态通信往往需要需要一些
重新设计——不能简单地将一些session状态绑缚在URI上，然后就宣称这个应用是RESTful。）</p>
<p>但除此以外，其它方面可能显得更为重要：无状态约束使服务器的变化对客户端是不可见的，因为在两次连续的请求中，客户端并不依赖于同一台服务器。一
个客户端从某台服务器上收到一份包含链接的文档，当它要做一些处理时，这台服务器宕掉了，可能是硬盘坏掉而被拿去修理，可能是软件需要升级重启——如果这
个客户端访问了从这台服务器接收的链接，它不会察觉到后台的服务器已经改变了。</p>
<h2>理论上的REST</h2>
<p>我承认：以上我所说的REST不是真正的REST，而且我可能有点过多地热衷于简单化。但因为我想有一个与众不同的开场，所以没有在一开始就介绍其正式的定义和背景。现在就让我们稍微简要地介绍一下这方面的内容。</p>
<p>首先，先前我并没有明确地区分HTTP、RESTful HTTP和REST。要理解这些不同方面之间的关系，我们要先来看看REST的历史。</p>
<p><a title="Roy T. Fielding" href="http://www.ics.uci.edu/%7Efielding/">Roy T. Fielding</a>在他的<a title="博士学位论文" href="http://www.ics.uci.edu/%7Efielding/pubs/dissertation/top.htm" id="ewd-">博士学位论文</a>（实际上你应该访问这个链接——至少对于一篇学术论文来说，它是相当易读的。此论文已被翻译成<a title="中文" href="http://www.redsaga.com/opendoc/REST_cn.pdf" id="nev8">中文</a>）
中定义了术语REST。Roy曾是许多基本Web协议的主要设计者，其中包括HTTP和URIs，并且他在论文中对这些协议提出了很多想法。（这篇论文被
誉为&#8220;REST圣经&#8221;，这是恰当的——毕竟，是作者发明了这个术语，所以在定义上，他写的任何内容都被认为是权威的。）在论文中，Roy首先定义一种方法
论来谈论<strong>架构风格</strong>——高级、抽象的模式，来表达架构方法背后的核心理念。每一个架构风格由一系列的<strong>约束</strong>（constraints）定义形成。架构风格的例子包括&#8220;没有风格&#8221;（根本没有任何约束）、管道和过滤器（pipe and filter）、客户端/服务器、分布式对象以及——你猜到它了——REST。</p>
<p>如果对你来说这些听起来都太抽象了，那就对了——REST在本质上是一个可以被许多不同技术实现的高层次的风格，而且可以被实例化——通过为它的抽
象特性赋上不同的值。比如，REST中包含资源和统一接口的概念——也就是说，所有资源都应该对这些相同的方法作出反应。但是REST并没有说明是哪些方
法，或者有多少方法。</p>
<p>REST风格的一个&#8220;化身&#8221;便是HTTP（以及一套相关的一套标准，比如URI），或者稍微抽象一些：Web架构自身。接着上面的例子，HTTP使
用HTTP动词作为REST统一接口的&#8220;实例&#8221;。由于Fielding是在Web已经（或者至少是大部分）&#8220;完善&#8221;了之后才定义的REST风格，有人可能
会争论两者是不是100%的匹配。但是无论如何，整体上来说Web、HTTP和URI仅仅是REST风格的一个主要实现。不过，由于Roy
Fielding即是REST论文的作者，又对Web架构设计有过深远的影响，两者相似也在情理之中。</p>
<p>最后，我在前面一次又一次地使用着术语&#8220;RESTful
HTTP&#8221;，原因很简单：许多使用HTTP的应用因为一些理由并没有遵循REST原则，有人会说使用HTTP而不遵循REST原则就等同于滥用HTTP。
当然这听起来有点狂热——事实上违反REST约束的原因通常是，仅仅因为每个约束带来的设计权衡可能不适合于一些特殊情况。但通常，违背REST约束的原
因可归咎于对其好处认知的缺乏。来看一个明显的反面案例：使用HTTP
GET调用类似于删除对象的操作，这违反了REST的安全约束和一般性常识（客户程序不应为此负责，服务器端开发人员大概不是有意而为之）。但在随后的文
章中，我会提及更多这样或那样的对HTTP的滥用。</p>
<h2>总结 </h2>
<p>本文试图对REST（Web架构）背后的概念提供快速的介绍。RESTful HTTP暴露功能的方式与RPC、分布式对象以及Web
Services是不相同的；要真正理解这些不同是需要一些心态的转变。不管你构建的应用是仅仅想暴露Web
UI还是想把API变成Web的一份子，了解下REST的原则还是有好处的。</p>
<p><a href="http://www.innoq.com/blog/st/"><strong>Stefan Tilkov</strong></a><strong>是InfoQ SOA社区的首席编辑，并且是位于德国和瑞士的</strong><a id="hdph" title="innoQ" href="http://www.innoq.com/"><strong>innoQ</strong></a><strong>公司的共同创始人、首席顾问和REST狂热分子首领。</strong></p>
<p><strong>&nbsp;&nbsp; </strong><strong>译者简介：</strong>苑永凯，软件设计师，毕业于山东大学。主要关注领域为Java EE企业应用、Java EE中间件技术以及敏捷开发方法实践，微有心得。他的Blog为<a href="http://blog.csdn.net/ai92">http://blog.csdn.net/ai92</a>，您也可以通过<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#121;&#117;&#97;&#110;&#121;&#107;&#64;&#103;&#109;&#97;&#105;&#108;&#46;&#99;&#111;&#109;">yuanyk[AT]gmail.com</a>与他联系。参与InfoQ中文站内容建设，请邮件至<a onclick="createEmailAddress(this);" name="china-editorial" href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#99;&#104;&#105;&#110;&#97;&#45;&#101;&#100;&#105;&#116;&#111;&#114;&#105;&#97;&#108;&#37;&#53;&#66;&#97;&#116;&#37;&#53;&#68;&#105;&#110;&#102;&#111;&#113;&#46;&#99;&#111;&#109;">china-editorial[at]infoq.com</a>。
</p>
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/261311.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-03-22 14:30 <a href="http://www.blogjava.net/hulizhong/archive/2009/03/22/261311.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 理解REST软件架构</title><link>http://www.blogjava.net/hulizhong/archive/2009/03/22/261310.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Sun, 22 Mar 2009 06:20:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/03/22/261310.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/261310.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/03/22/261310.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/261310.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/261310.html</trackback:ping><description><![CDATA[转 http://www.infoq.com/cn/articles/rest-architecure<br />
<br />
<p class="info">
作者
<strong>骆古道</strong>
发布于
2007年5月27日 下午8时18分
</p>
<dl class="tags2"><dt class="community">社区</dt><dd><a href="http://www.infoq.com/cn/ruby" name="ruby" id="741" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">Ruby</a>,</dd><dd><a href="http://www.infoq.com/cn/java" name="java" id="739" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">Java</a></dd><dt class="topics">主题</dt><dd><a href="http://www.infoq.com/cn/webframework" name="webframework" id="744" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">Web框架</a>,</dd><dd><a href="http://www.infoq.com/cn/rest" name="rest" id="891" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">REST</a>,</dd><dd><a href="http://www.infoq.com/cn/architecture_topic" name="architecture_topic" id="827" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">架构</a>,</dd><dd><a href="http://www.infoq.com/cn/rubyonrails" name="rubyonrails" id="755" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">Ruby on Rails</a></dd><dt class="topics">标签</dt><dd><a href="http://www.infoq.com/cn/ajax" name="ajax" id="867" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">AJAX</a>,</dd><dd><a href="http://www.infoq.com/cn/rails" name="rails" id="900" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;">Ruby on Rails</a></dd></dl>
<p>一
种思维方式影响了软件行业的发展。REST软件架构是当今世界上最成功的互联网的超媒体分布式系统。它让人们真正理解我们的网络协议HTTP本来面貌。它
正在成为网络服务的主流技术，同时也正在改变互联网的网络软件开发的全新思维方式。AJAX技术和Rails框架把REST软件架构思想真正地在实际中很
好表现出来。今天微软也已经应用REST并且提出把我们现有的网络变成为一个<a href="http://dannyayers.com/2007/05/01/astoria%E2%80%94microsoft-approaches">语义网</a>，这种网络将会使得搜索更加智能化。 <br />
</p>
<h4>REST与HTTP协议 </h4>
<p>REST软件架构是由Roy Thomas Fielding博士在2000年首次提出的。他为我们描绘了开发基于互联网的网络软件的蓝图。REST软件架构是一个抽象的概念，是一种为了实现这一互联网的超媒体分布式系统的<strong>行动指南</strong>。利用任何的技术都可以实现这种理念。而实现这一软件架构最著名的就是HTTP协议。通常我们把REST也写作为REST/HTTP，在实际中往往把REST理解为基于HTTP的REST软件架构，或者更进一步把REST和HTTP看作为等同的概念。 </p>
<p>今天，HTTP是互联网上应用最广泛的计算机协议。HTTP不是一个简单的运载数据的协议，而是一个具有丰富内涵的<strong>网络软件</strong>的
协议。它不仅仅能够对于互联网资源进行唯一定位，而且还能告诉我们对于该资源进行怎样运作。这也是REST软件架构当中最重要的两个理念。而REST软件
架构理念是真正理解HTTP协议而形成的。有了REST软件架构理念出现，才使得软件业避免了对HTTP协议的片面理解。只有正确的理论指导，才能避免在
软件开发的实际工作过程中少走弯路。 </p>
<h4>REST与URI（资源定位） </h4>
<p>REST软件架构之所以是一个超媒体系统，是因为它可以把网络上所有资源进行唯一的定位，不管你的文件是图片、文件Word还是视频文件，也不管你
的文件是txt文件格式、xml文件格式还是其它文本文件格式。它利用支持HTTP的TCP/IP协议来确定互联网上的资源。 </p>
<h4>REST与CRUD原则 </h4>
<p>REST软件架构遵循了CRUD原则，该原则告诉我们对于资源（包括网络资源）只需要四种行为：创建（Create）、获取（Read）、更新
（Update）和销毁（DELETE）就可以完成对其操作和处理了。其实世界万物都是遵循这一规律：生、变、见、灭。所以计算机世界也不例外。这个原则
是源自于我们对于数据库表的数据操作：insert（生）、select（见）、update（变）和delete（灭），所以有时候CRUD也写作为
RUDI，其中的I就是insert。这四个操作是一种原子操作，即一种无法再分的操作，通过它们可以构造复杂的操作过程，正如数学上四则运算是数字的最
基本的运算一样。 </p>
<h4>REST与网络服务 </h4>
<p>尽管在Java语言世界中网络服务目前是以SOAP技术为主，但是REST将是是网络服务的另一选择，并且是真正意义上的网络服务。基于REST思
想的网络服务不久的将来也会成为是网络服务的主流技术。REST不仅仅把HTTP作为自己的数据运输协议，而且也作为直接进行数据处理的工具。而当前的网
络服务技术都需要使用其它手段来完成数据处理工作，它们完全独立于HTTP协议来进行的，这样增加了大量的复杂软件架构设计工作。REST的思想充分利用
了现有的HTTP技术的网络能力。在德国电视台上曾经出现过一个这样的五十万欧元智力题：如何实现网络服务才能充分利用现有的HTTP协议？该问题给出了
四个答案：去问微软；WSDL2.0/SOAP1.2；WS-Transfer；根本没有。这个问题告诉我们HTTP并不是一个简单的数据传来传去的协
议，而是一个聪明的会表现自己的协议，这也许是REST = Representational State Transfer的真正含义。 </p>
<p>实际上目前很多大公司已经采用了REST技术作为网络服务，如Google、Amazon等。在Java语言中重要的两个以SOAP技术开始的网络服务框架XFire和Axis也把REST作为自己的另一种选择。它们的新的项目分别是<a href="http://cwiki.apache.org/confluence/display/CXF/Index">Apache CXF </a>和<a href="http://ws.apache.org/axis2/">Axis2 </a>。Java语言也制定关于REST网络服务规范：JAX-RS: Java API for RESTful Web Services (JSR 311)。相信还会出现更多与REST相关的激动人心的信息。 </p>
<h4>REST与AJAX技术 </h4>
<p>尽管AJAX技术的出现才不到两年时间，但是AJAX技术遵循了REST的一些重要原则。AJAX技术充分利用了HTTP来获取网络资源并且实现了
HTTP没有的对于异步数据进行传输的功能。AJAX技术还使得软件更好地实现分布性功能，在一个企业内只要一个人下载了AJAX引擎，其它企业内部的人
员，就可以共享该资源了。AJAX技术遵守REST准则的应用程序中简单和可伸缩的架构，凡是采用AJAX技术的页面简洁而又丰富，一个页面表现了丰富多
彩的形态。 </p>
<p>AJAX技术还使用了一种不同于XML格式的JSON文件格式，这个意义在哪里呢？在REST软件架构下我们不能对于XML文件进行序列化处理，这
样程序员必须要使用自己的XML绑定框架。而以序列化的JavaScript对象为基础的JSON已经获得了广泛认可，它被认为能以远比XML更好的方式
来序列化和传输简单数据结构，而且它更简洁。这对REST是一个极大贡献和补充。 </p>
<p>当前的网络应用软件还违背了REST的&#8220;无状态服务器&#8221;约束。REST服务器只知道自己的状态。REST不关心客户端的状态，客户端的状态自己来管
理，这是AJAX技术的应用之地。通过AJAX技术，可以发挥有状态网络客户机的优势。而REST的服务器关心的是从所有网络客户端发送到服务器操作的顺
序。这样使得互联网这样一个巨大的网络得到有序的管理。 </p>
<h4>REST与Rails框架 </h4>
<p>Ruby on
Rails框架（简称Rails或者Rails框架）是一个基于Ruby语言的越来越流行的网络应用软件开发框架。它提供了关于REST最好的支持，也是
当今应用REST最成功的一个软件开发框架。Rails框架（从版本1.2.x起）成为了第一个引入REST作为核心思想的主流网络软件开发框架。在
Rails框架的充分利用了REST软件架构之后，人们更加坚信REST的重要性和必要性。Rails利用REST软件架构思想对网络服务也提供了一流的
支持。从最直观的角度看待REST，它是网络服务最理想的手段，但是Rails框架把REST带到了网络应用软件开发框架。这是一次飞跃，让REST的思
想从网络服务的应用提升到了网络应用软件开发。利用REST思想的simply_restful插件已经成为了Rails框架的核心内容。 </p>
<h4>REST安全性 </h4>
<p>我们把现有基于SOAP的网络服务和基于REST/HTTP网络服务作个比喻，前者是一种传统的寄信方式，而后者是现代网络的电子邮件方式。要是是
寄信和电子邮件都有病毒存在的话，传统的寄信被送到对方就很危险，而电子邮件是开发的，电子邮件供应商比如Google为我们检查了电子邮件是否有病毒。
这里并不是说明SOAP网络服务消息包含义病毒，而是说明HTTP是无法处理SOAP信息包究竟好不好，需要额外的软件工具解决这一问题，包括防火墙也用
不上和管不了。 </p>
<p>REST/HTTP网络服务的信息包可以被防火墙理解和控制。你可以按照操作和链接进行过滤信息包，如你可以规定从外部来的只能读取（GET操作）
自己服务器的资源。这样对于系统管理员而言使得软件管理更为简单。REST的安全性还可以利用传输安全协议SSL/TLS、基本和摘要式认证（Basic
und Digest Authentication）。除了这些REST自身的安全性功能外，还可以利用像基于信息的Web Services
Security（JSR 155）作为REST不错的补充。 </p>
<h4>参考文献</h4>
<strong>中文参考文献</strong>
<ul>
    <li><a href="http://www.ibm.com/developerworks/cn/web/wa-ajaxarch/">http://www.ibm.com/developerworks/cn/web/wa-ajaxarch/</a> </li>
    <li><a href="http://www.ibm.com/developerworks/cn/java/j-cb08016/">http://www.ibm.com/developerworks/cn/java/j-cb08016/</a> </li>
</ul>
<strong>Roy Thomas Fielding博士论文中文版本</strong>
<ul>
    <li><a href="http://e2c.91yee.com/columns/">http://e2c.91yee.com/columns/</a> </li>
</ul>
<strong>Roy Thomas Fielding博士论文英文版本</strong>
<ul>
    <li><a href="http://www.ics.uci.edu/%7Efielding/pubs/dissertation/top.htm">http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm</a> </li>
</ul>
<hr />
<strong>作者简介：</strong>骆古道，网名Cnruby，八十年代初毕业于西北工业大学数理力学系，1988年公派留学德国，从事组合最优化理论研究，从九十年代初期起一直致力于计算机领域软件开发、设计和管理等方面工作，个人博客为&#8220;<a href="http://www.hhtong.com/">道喜技术日记</a>&#8221;。
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/261310.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-03-22 14:20 <a href="http://www.blogjava.net/hulizhong/archive/2009/03/22/261310.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Cookie, iframe 与 P3P 的那点事儿</title><link>http://www.blogjava.net/hulizhong/archive/2009/03/21/261194.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Sat, 21 Mar 2009 05:34:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/03/21/261194.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/261194.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/03/21/261194.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/261194.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/261194.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 看了<a title="http://www.dbanotes.net/" href="http://www.dbanotes.net/">Fenng</a>的一篇blog关于Cookie, iframe 与 P3P，自己不是很清楚，查询了相关资料，把相关心得贴出来。<br />
&nbsp;&nbsp;&nbsp;&nbsp; 应用场景：<br />
&nbsp;&nbsp;&nbsp;&nbsp; 比如一个 www.a.com中有个服务应用是引用b.com域的，
<br />
通常情况下用户的隐私策略设置为中或者更高.在b.com域的cookie是不能够正常读取的，但是服务端能够改变用户对b.com域的隐私策略使b.com域下的cookie正常存取.<br />
&nbsp;&nbsp;&nbsp;&nbsp; 也可以说是利用p3p解决第三方cookie存取的问题。<br />
&nbsp;&nbsp;&nbsp;&nbsp; 第一方Cookie:是来自当前正在查看的网站，或者发送到当前正在查看的网站。 <br />
&nbsp;&nbsp;&nbsp;&nbsp; 第三方Cookie:是来自当前正在查看的网站以外的网站，或者发送到当前正在查看的网站以外的网站。第三方网站通常提供正在查看的网站上的内容。例如，许多站点使用来自第三方网站的广告，或者iframe的别的网站的url，这些第三方的网站可能使用的Cookie。 <br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; p3p相关东东<br />
&nbsp;&nbsp;&nbsp;&nbsp; P3P是万维网联盟（W3C）公布的一项隐私保护推荐标准，旨在为网上冲浪的Internet用户提供隐私保护。现在有越来越多的网站在消费者访问时，都
会收集一些用户信息。制定P3P标准的出发点就是为了减轻消费者因网站收集个人信息所引发的对于隐私权可能受到侵犯的忧虑。P3P标准的构想是：Web
站点的隐私策略应该告之访问者该站点所收集的信息类型、信息将提供给哪些人、信息将被保留多少时间及其使用信息的方式，如站点应做诸如
&#8220;本网站将监测您所访问的页面以提高站点的使用率&#8221;或&#8220;本网站将尽可能为您提供更合适的广告&#8221;等申明。访问支持P3P网站的用户有权查看站点隐私报告，然后决定是否接受cookie或是否使用该网站。<br />
&nbsp;&nbsp;&nbsp;&nbsp; 通过指定用户隐私策略，就可以达到存取第三方cookie的目的，看到这也许会觉得跟web应用毫无关系，真正的问题是如何让服务器来指定用户浏览器的隐私策略？<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其实很简单，只要在响应用户请求的时候在http的头信息中增加关于p3p的配置信息就可以了。<br />
&nbsp;&nbsp;&nbsp;&nbsp; <span><span>response.setHeader(</span><span class="string">"P3P"</span><span>,</span><span class="string">"CP='IDC&nbsp;DSP&nbsp;COR&nbsp;ADM&nbsp;DEVi&nbsp;TAIi&nbsp;PSA&nbsp;PSD&nbsp;IVAi&nbsp;IVDi&nbsp;CONi&nbsp;HIS&nbsp;OUR&nbsp;IND&nbsp;CNT'"</span><span>)；<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不对之处，请大家指正！<br />
参考：<br />
http://viralpatel.net/blogs/2008/12/how-to-set-third-party-cookies-with-iframe.html<br />
http://www.dbanotes.net/web/cookie_p3p.html<br />
http://ding--lin.javaeye.com/blog/94336<br />
http://www.cnblogs.com/gudai/archive/2008/09/21/1295432.html<br />
http://cailin.javaeye.com/blog/175422&nbsp; <br />
</span></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src ="http://www.blogjava.net/hulizhong/aggbug/261194.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-03-21 13:34 <a href="http://www.blogjava.net/hulizhong/archive/2009/03/21/261194.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 不唐突的JavaScript的七条准则</title><link>http://www.blogjava.net/hulizhong/archive/2009/03/19/260809.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Thu, 19 Mar 2009 08:13:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/03/19/260809.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/260809.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/03/19/260809.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/260809.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/260809.html</trackback:ping><description><![CDATA[转 http://www.cnblogs.com/JustinYoung/articles/1350887.html<br />
<h1 class="postTitle">
<a id="AjaxHolder_ctl01_TitleUrl" class="postTitle2" href="http://www.cnblogs.com/JustinYoung/articles/1350887.html">不唐突的JavaScript的七条准则</a>
</h1>
<div class="postBody">
<p>经过多年的开发、教学和编写不唐突的JavaScript， 我发现了下面的一些准则。我希望它们可以帮助你对&#8220;为什么这样设计和执行JavaScript比较好&#8221;有一点理解。这些规则曾经帮助我更快地交付产品，并且产品的质量更高，也更容易维护。</p>
<p><strong>1.不要做任何假设</strong>     <br />
（JavaScript是一个不可靠的助手）</p>
<p>可能不唐突的JavaScript 的最重要的一个特性就是——你要停止任何假设：</p>
<ul>
    <li>不要假设JavaScript是可用的，你最好认为它很有可能是不可用的，而不是直接依赖于它。 </li>
    <li>在你经过测试确认一些方法和属性可以使用之前，不要假设浏览器支持它们。 </li>
    <li>不要假设HTML代码如你想象的那样正确，每次都要进行检查，并且当其不可用的时候就什么也不要做。 </li>
    <li>让JavaScript的功能独立于输入设备 </li>
    <li>要记住其他的脚本可能会影响你的JavaScript的功能，所以要保证你的脚本的作用域尽可能地安全。 </li>
</ul>
<p>在开始设计你的脚本之前，要考虑的第一件事情就是检查一下你要为其编写脚本的HTML代码，看看有什么东西可以帮助你达到目的。</p>
<p><strong>2.找出钩子和节点关系</strong>（HTML是脚本的基石）</p>
<p>在
开始编写脚本之前，要先看一下你要为之编写JavaScript的HTML。如果HTML是未经组织的或者未知的，那么你几乎不可能有一个好的脚本编写方
案——很可能就会出现下面的情况：要么是会用JavaScript创建太多标记，要么就是应用太依赖于JavaScript。</p>
<p>在HTML中有一些东西需要考虑，那就是钩子和节点关系。</p>
<p><strong>&lt;1&gt;.HTML 钩子</strong></p>
<p>HTML
最初的和最重要的钩子就是ID，而且ID可以通过最快的DOM方法——getElementById
访问到。如果在一个有效的HTML文档中所有的ID都是独一无二的话（在IE中关于name 和 ID
有一个bug，不过有些好的类库解决了这个问题），使用ID就是安全可靠的，并且易于测试。</p>
<p>其他一些钩子就是是HTML元素和CSS
类，HTML元素可以通过getElementsByTagName方法访问，而在多数浏览器中都还不能通过原生的DOM方法来访问CSS类。不过，有很
多外部类库提供了可以访问CSS类名（类似于 getElementsByClassName） 的方法。</p>
<p><strong>&lt;2&gt;.HTML 节点关系</strong></p>
<p>关于HTML的另外比较有意思的一点就是标记之间的关系，思考下面的问题：</p>
<ul>
    <li>要怎样才可以最容易地、通过最少的DOM遍历来到达目标节点？ </li>
    <li>通过修改什么标记，可以尽可能多地访问到需要修改的子节点？ </li>
    <li>一个给定的元素有什么属性或信息可以用来到达另外一个元素？ </li>
</ul>
<p>遍历DOM很耗资源而且速度很慢，这就是为什么要尽量使用浏览器中已经在使用的技术来做这件事情。</p>
<p><strong>3.把遍历交给专家来做</strong>（CSS，更快地遍历DOM）</p>
<p>有
关DOM的脚本和使用方法或属性（getElementsByTagName, nextSibling, previousSibling,
parentNode以及其它）来遍历DOM似乎迷惑了很多人，这点很有意思。而有趣的是，我们其实早已经通过另外一种技术—— CSS
——做了这些事情。</p>
<p>CSS 是这样一种技术，它使用CSS选择器，通过遍历DOM来访问目标元素并改变它们的视觉属性。一段复杂的使用DOM的JavaScript可以用一个CSS选择器取代：</p>
<p>var n = document.getElementById('nav');    <br />
if(n){     <br />
var as = n.getElementsByTagName('a');     <br />
if(as.length &gt; 0){     <br />
for(var i=0;as[i];i++){     <br />
as[i].style.color = &#8216;#369&#8242;;     <br />
as[i].style.textDecoration = &#8216;none&#8217;;     <br />
}     <br />
}     <br />
}</p>
<p>/* 下面的代码与上面功能一样 */</p>
<p>#nav a{    <br />
color:#369;     <br />
text-decoration:none;     <br />
}</p>
<p>这是一个可以好好利用的很强大的技巧。你可以通过动态为DOM中高层的元素添加class 或者更改元素ID来实现这一点。如果你使用DOM为文档的body添加了一个CSS类，那么设计师就很可以容易地定义文档的静态版本和动态版本。</p>
<p>JavaScript:</p>
<p>var dynamicClass = 'js';    <br />
var b = document.body;     <br />
b.className = b.className ? b.className + ' js' : 'js';</p>
<p>CSS:</p>
<p>/* 静态版本 */</p>
<p>#nav {    <br />
....     <br />
}</p>
<p>/* 动态版本 */</p>
<p>body.js #nav {    <br />
....     <br />
}</p>
<p><strong>4.理解浏览器和用户</strong>（在既有的使用模式上创建你所需要的东西）</p>
<p>不
唐突的JavaScript
中很重要的一部分就是理解浏览器是如何工作的（尤其是浏览器是如何崩溃的）以及用户期望的是什么。不考虑浏览器你也可以很容易地使用JavaScript
创建一个完全不同的界面。拖拽界面，折叠区域，滚动条和滑动块都可以使用JavaScript创建，但是这个问题并不是个简单的技术问题，你需要思考下面
的问题：</p>
<ul>
    <li>这个新界面可以独立于输入设备么？如果不能，那么可以依赖哪些东西？ </li>
    <li>我创建的这个新界面是否遵循了浏览器或者其它富界面的准则（你可以通过鼠标在多级菜单中直接切换吗？还是需要使用tab键？） </li>
    <li>我需要提供什么功能但是这个功能是依赖于JavaScript的? </li>
</ul>
<p>最
后一个问题其实不是问题，因为如果需要你就可以使用DOM来凭空创建HTML。关于这点的一个例子就是&#8220;打印&#8221;链接，由于浏览器没有提供一个非
JavaScript的打印文档功能，所以你需要使用DOM来创建这类链接。同样地，一个实现了展开和收缩内容模块的、可以点击的标题栏也属于这种情况。
标题栏不能被键盘激活，但是链接可以。所以为了创建一个可以点击的标题栏你需要使用JavaScript将链接加入进去，然后所有使用键盘的用户就可以收
缩和展开内容模块了。</p>
<p>解决这类问题的极好的资源就是设计模式库。至于要知道浏览器中的哪些东西是独立于输入设备的，那就要靠经验的积累了。首先你要理解的就是事件处理机制。</p>
<p><strong>5.理解事件</strong>（事件处理会引起改变）</p>
<p>事
件处理是走向不唐突的JavaScript的第二步。重点不是让所有的东西都变得可以拖拽、可以点击或者为它们添加内联处理，而是理解事件处理是一个可以
完全分离出来的东西。我们已经将HTML,CSS和JavaScript分离开来，但是在事件处理的分离方面却没有走得很远。</p>
<p>事件处理器会监听发生在文档中元素上的变化，如果有事件发生，处理器就会找到一个很奇妙的对象（一般会是一个名为e的参数），这个对象会告诉元素发生了什么以及可以用它做什么。</p>
<p>对
于大多数事件处理来说，真正有趣的是它不止发生在你想要访问的元素上，还会在DOM中较高层级的所有元素上发生（但是并不是所有的事件都是这
样，focus和blur事件是例外）。举例来说，利用这个特性你可以为一个导航列表只添加一个事件处理器，并且使用事件处理器的方法来获取真正触发事件
的元素。这种技术叫做事件委托，它有几点好处：</p>
<ul>
    <li>你只需要检查一个元素是否存在，而不需要检查每个元素 </li>
    <li>你可以动态地添加或者删除子节点而并不需要删除相应的事件处理器 </li>
    <li>你可以在不同的元素上对相同的事件做出响应 </li>
</ul>
<p>需
要记住的另一件事是，在事件向父元素传播的时候你可以停止它而且你可以覆写掉HTML元素（比如链接）的缺省行为。不过，有时候这并不是个好主意，因为浏
览器赋予HTML元素那些行为是有原因的。举个例子，链接可能会指向页面内的某个目标，不去修改它们能确保用户可以将页面当前的脚本状态也加入书签。</p>
<p><strong>6.为他人着想</strong>（命名空间，作用域和模式）</p>
<p>你的代码几乎从来不会是文档中的唯一的脚本代码。所以保证你的代码里没有其它脚本可以覆盖的全局函数或者全局变量就显得尤为重要。有一些可用的模式可以来避免这个问题，最基础的一点就是要使用 var 关键字来初始化所有的变量。假设我们编写了下面的脚本：</p>
<p>var nav = document.getElementById('nav');   <br />
function init(){    <br />
// do stuff    <br />
}    <br />
function show(){    <br />
// do stuff    <br />
}    <br />
function reset(){    <br />
// do stuff    <br />
}</p>
<p>上面的代码中包含了一个叫做nav的全局变量和名字分别为 init,show 和 reset 的三个函数。这些函数都可以访问到nav这个变量并且可以通过函数名互相访问：</p>
<p>var nav = document.getElementById('nav');   <br />
function init(){    <br />
show();    <br />
if(nav.className === 'show'){    <br />
reset();    <br />
}    <br />
// do stuff    <br />
}    <br />
function show(){    <br />
var c = nav.className;    <br />
// do stuff    <br />
}    <br />
function reset(){    <br />
// do stuff    <br />
}</p>
<p>你可以将代码封装到一个对象中来避免上面的那种全局式编码，这样就可以将函数变成对象中的方法，将全局变量变成对象中的属性。 你需要使用&#8220;名字+冒号&#8221;的方式来定义方法和属性，并且需要在每个属性或方法后面加上逗号作为分割符。</p>
<p>var myScript = {   <br />
nav:document.getElementById('nav'),    <br />
init:function(){    <br />
// do stuff    <br />
},    <br />
show:function(){    <br />
// do stuff    <br />
},    <br />
reset:function(){    <br />
// do stuff    <br />
}    <br />
}</p>
<p>所有的方法和属性都可以通过使用&#8220;类名+点操作符&#8221;的方式从外部和内部访问到。</p>
<p>var myScript = {   <br />
nav:document.getElementById('nav'),    <br />
init:function(){    <br />
myScript.show();    <br />
if(myScript.nav.className === 'show'){    <br />
myScript.reset();    <br />
}    <br />
// do stuff    <br />
},    <br />
show:function(){    <br />
var c = myScript.nav.className;    <br />
// do stuff    <br />
},    <br />
reset:function(){    <br />
// do stuff    <br />
}    <br />
}</p>
<p>这种模式的缺点就是，你每次从一个方法中访问其它方法或属性都必须在前面加上对象的名字，而且对象中的所有东西都是可以从外部访问的。如果你只是想要部分代码可以被文档中的其他脚本访问，可以考虑下面的模块（module）模式：</p>
<p>var myScript = function(){   <br />
//这些都是私有方法和属性    <br />
var nav = document.getElementById('nav');    <br />
function init(){    <br />
// do stuff    <br />
}    <br />
function show(){    <br />
// do stuff    <br />
}    <br />
function reset(){    <br />
// do stuff    <br />
}    <br />
//公有的方法和属性被使用对象语法包装在return 语句里面    <br />
return {    <br />
public:function(){</p>
<p>},   <br />
foo:'bar'    <br />
}    <br />
}();</p>
<p>你
可以使用和前面的代码同样的方式访问返回的公有的属性和方法，在本示例中可以这么访问：myScript.public() 和
myScript.foo
。但是这里还有一点让人觉得不舒服：当你想要从外部或者从内部的一个私有方法中访问公有方法的时候，还是要写一个冗长的名字（对象的名字可以非常长）。为
了避免这一点，你需要将它们定义为私有的并且在return语句中只返回一个别名：</p>
<p>var myScript = function(){   <br />
// 这些都是私有方法和属性    <br />
var nav = document.getElementById('nav');    <br />
function init(){    <br />
// do stuff    <br />
}    <br />
function show(){    <br />
// do stuff    <br />
// do stuff    <br />
}    <br />
function reset(){    <br />
// do stuff    <br />
}    <br />
var foo = 'bar';    <br />
function public(){</p>
<p>}</p>
<p>//只返回指向那些你想要访问的私有方法和属性的指针   <br />
return {    <br />
public:public,    <br />
foo:foo    <br />
}    <br />
}();</p>
<p>这就保证了代码风格一致性，并且你可以使用短一点的别名来访问其中的方法或属性。</p>
<p>如果你不想对外部暴露任何的方法或属性，你可以将所有的代码封装到一个匿名方法中，并在它的定义结束后立刻执行它：</p>
<p>(function(){   <br />
// these are all private methods and properties    <br />
var nav = document.getElementById('nav');    <br />
function init(){    <br />
// do stuff    <br />
show(); // 这里不需要类名前缀    <br />
}    <br />
function show(){    <br />
// do stuff    <br />
}    <br />
function reset(){    <br />
// do stuff    <br />
}    <br />
})();</p>
<p>对于那些只执行一次并且对其它函数没有依赖的代码模块来说，这种模式非常好。</p>
<p>通过遵循上面的那些规则，你的代码更好地为用户工作，也可以使你的代码在机器上更好地运行并与其他开发者的代码和睦相处。不过，还有一个群体需要考虑到。</p>
<p><strong>7.为接手的开发者考虑</strong>（使维护更加容易）</p>
<p>使你的脚本真正地unobtrusive的最后一步是在编写完代码之后仔细检查一遍，并且要照顾到一旦脚本上线之后要接手你的代码的开发者。考虑下面的问题：</p>
<ul>
    <li>所有的变量和函数名字是否合理并且易于理解？ </li>
    <li>代码是否经过了合理的组织？从头到尾都很流畅吗？ </li>
    <li>所有的依赖都显而易见吗？ </li>
    <li>在那些可能引起混淆的地方都添加了注释吗？ </li>
</ul>
<p>最重要的一点是：要认识到文档中的HTML和CSS代码相对于JavaScript来说更有可能被改变（因为它们负责视觉效果）。所以不要在脚本代码中包含任何可以让终端用户看到的class和ID，而是要将它们分离出来放到一个保存配置信息的对象中。</p>
<p>myscript = function(){   <br />
var config = {    <br />
navigationID:'nav',    <br />
visibleClass:'show'    <br />
};    <br />
var nav = document.getElementById(config.navigationID);    <br />
function init(){    <br />
show();    <br />
if(nav.className === config.visibleClass){    <br />
reset();    <br />
};    <br />
// do stuff    <br />
};    <br />
function show(){    <br />
var c = nav.className;    <br />
// do stuff    <br />
};    <br />
function reset(){    <br />
// do stuff    <br />
};    <br />
}();</p>
<p>这样维护者就知道去哪里修改这些属性，而不需要改动其他代码。</p>
<p><strong>更多信息</strong></p>
<p>以上就是我发现的七条准则。如果你想要了解更多与上面所探讨的主题相关的东西，可以看看下面的链接：</p>
<ul>
    <li><a href="http://developer.yahoo.com/ypatterns/">Yahoo! Design Pattern Library </a></li>
    <li><a href="http://icant.co.uk/sandbox/eventdelegation/">Event Delegation </a></li>
    <li><a href="http://yuiblog.com/blog/2007/01/17/event-plan/">Event Driven JavaScript Application Design</a></li>
    <li><a href="http://www.klauskomenda.com/code/javascript-programming-patterns/">JavaScript Programming Patterns</a></li>
    <li><a href="http://www.wait-till-i.com/2006/02/16/show-love-to-the-object-literal/">Show love to the Object Literal </a></li>
    <li><a href="http://yuiblog.com/blog/2007/06/12/module-pattern/">A JavaScript Module Pattern</a></li>
</ul>
</div>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/260809.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-03-19 16:13 <a href="http://www.blogjava.net/hulizhong/archive/2009/03/19/260809.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 RichClient/RIA原则与实践（上）</title><link>http://www.blogjava.net/hulizhong/archive/2009/03/18/260387.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Wed, 18 Mar 2009 01:52:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/03/18/260387.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/260387.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/03/18/260387.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/260387.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/260387.html</trackback:ping><description><![CDATA[转 http://www.infoq.com/cn/articles/thoughtworks-practice-partiii<br />
<br />
<h1>RichClient/RIA原则与实践（上）</h1>
<p class="info">作者 <strong>陈金洲</strong> 发布于 2009年3月10日 上午4时8分 </p>
<dl class="tags2">
<dt class="community">社区
<dd><a id="740" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" href="http://www.infoq.com/cn/dotnet" name="dotnet">.NET</a>,
<dd><a id="743" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" href="http://www.infoq.com/cn/agile" name="agile">Agile</a>,
<dd><a id="739" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" href="http://www.infoq.com/cn/java" name="java">Java</a>
<dt class="topics">主题
<dd><a id="777" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" href="http://www.infoq.com/cn/ria" name="ria">RIA</a>,
<dd><a id="830" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" href="http://www.infoq.com/cn/rich-client-desktop" name="rich-client-desktop">富客户端/桌面</a>
<dt class="topics">标签
<dd><a id="4,681" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" href="http://www.infoq.com/cn/Principles" name="Principles">原则</a> </dd></dl>
<p>Web领域的经验在过去十多年的不断的使用和锤炼中，整个 开发领域的技术、理念、缺陷已经趋于成熟。JavaEE Stack, .NET Stack, Ruby On Rails等框架代表了目前这个技术领域的所有经验积累。这样我们在开始一个新的项目的时候，只需要选择对应语言的最佳实践，基本上不会犯大的错误。例 如，如果使用Java开发一个新的Web应用，那么基本上Spring/Guice+Hibernate/iBatis/+Struts /SpringMVC这种架构是不会产生重大的架构问题的；如果使用RoR那么你已经在使用最佳实践了；系统的分层：领域层，数据库层，服务层，表现层等 等；为了保证系统的可扩展性，服务器端应当是无状态架构，等等。总而言之，web开发领域，它丰富的积累使得开发者逐渐将更多的精力投入到应用本身。</p>
<p>来看富客户端，或者富互联网应用。在我看来，今天的RichClient与RIA已经没有分别：只要代表着丰富界面元素和丰富用户体验，需要与服务器进行 交互的应用都可以称为RichClient或者RIA，虽然感觉上RichClient更&#8220;企业化&#8221;一些（服务器往往在企业内部），RIA更&#8220;个人化&#8221;一 些（服务器往往处于公网）。从最小的层面来说，我现在正在使用的离线模式的GoogleDoc就是一个RichClient应用──虽然它没有那么 Rich，采用和microsoft office一样土的界面; 我现在正在听音乐的Last.fm客户端显然是一个非常典型的RIA──它所有的个人喜好信息、音乐全都来自远在美国的服务器。本地的这个界面，只是提供 收集个人和音乐信息，以及控制音乐的播放和停止；目前拥有1150万玩家的魔兽世界，则是一个挣钱最多的，最&#8220;富&#8221;的客户端，10多G的客户端包含了电影 品质的广阔场景，华丽的魔法效果和极其复杂的人机交互。</p>
<p>如今的用户需求已经达到了一个新的高度，那些灰色的，方方正正的界面已经逐渐不能够满足客户的需求。从我们工作的客户看来，他们除了对&#8220;完成功能&#8221;有着基 本的期待外，对于将应用做得&#8220;酷&#8221;，也抱有极大的热情。我工作的上一个项目是一个CRM系统，它是基于.NET Framework 3.5的一个RichClient应用。它的主窗口是一个带着红色渐变背景的无边框窗口，还有请专业美工制作的图标，点击某一个菜单还有华丽的二级菜单滑 动效果。我们在这个项目中获得了很多，有些值得借鉴，有些仍然值得反思。我仍然记得我们在项目的不同阶段，做一个技术决定是如此的彷徨和忐忑：因为在当时 的RichClient企业开发领域，几乎没有任何丰富的经验可以借鉴，我们重新发明了一些轮子，然后又推翻它；我们偏离了UI框架给我们提供的各种便利 而自己实现种种基础特性，只是因为他们偏离了我们所倡导的测试性的原则。在写下本文的时候，我尝试搜索了一下，仍然没有比较深入的实践性文章来介绍企业环 境下RichClient开发。大多数的书，如Swing、JavaFX、.NET WPF开发等等，偏向于小规模特性介绍，而在大规模的企业应用中，这些小的技巧对于架构决策往往帮助很小。</p>
<p>我的工作经历应当是和大多数开始进行RichClient开发的开发者类似：有着丰富的Web开发的经验之后开始进行RichClient开发。加入 ThoughtWorks之后参加了多个不同的RichClient项目的开发工作，使用/尝试过的语言包括Java Swing, Flex/Adobe Air, .NET WinForm/.NET WPF. 对于不同平台之间的种种有些体会。在这里我将这些实践和原则总结如下。例子很可能过时，毕竟华丽的界面框架层出不穷，但原则应当通用的。使用和遵循这些原 则将会帮助你少犯错误──至少比我们过去犯的错误要少。如果你拥有一定的web开发经验，那么这篇文章你读起来会很亲切。</p>
<p>这些原则/实践往往不是孤立的，我尝试将他们之间用图的方式关联起来，帮助你在使用的过程中进行选择。例如，你遵循了&#8220;一切皆异步&#8221;的原则，那么很可能你 需要进行&#8220;线程管理&#8221;和&#8220;事件管理&#8221;；如果你需要引入&#8220;缓存与本地存储&#8221;，那么&#8220;数据交互模式&#8221;你也需要进行考虑。希望这张图能够帮助读者理解不同原则之间的联系。</p>
<p class="image-wide"><img alt="" src="http://www.infoq.com/resource/articles/thoughtworks-practice-partiii/zh/resources/image1.jpg" _href="img://image1.jpg" /></p>
<p>下面列出的这些原则或者实践没有严格意义上的区分。按照上面的图，我推荐是，一旦你考虑到了某一个实践，那么与它直接关联的实践你最好也要实现。它会使得你的架构更全面，经得起用户功能的需求和交互的需求。</p>
<p>为了让这些实践更加通用，我采用伪代码书写。相信读者能够转化成相应的语言──Java, C#, ActionScript或者其他。这些实践并非与某一种语言相关。在某些特定的例子中，我会采用特定语言，但大多数都是伪代码描述的。</p>
<h2>1 一切皆异步</h2>
<p>所有耗时的操作都应当异步进行。这是第一条、也是最重要的原则，违背了这条原则将会导致你的应用完全不可用。</p>
<p>考虑这样的一个功能：点击一个"更新股票信息"按钮，系统会从股票市场（第三方应用）获得最新的股票信息，并将信息更新到主界面。丝毫不考虑用户体验的写法：</p>
<pre>void updateStockDataButton_clicked() {
&nbsp;&nbsp;&nbsp; stockData = stockDataService.getLatest(); // 从远程获取股票信息
&nbsp;&nbsp;&nbsp; updateUI(stockData); // 这个方法会更新界面
}</pre>
<p>那么，当用户点击<code>updateStockDataButton</code>的时候，会有什么反应？难说。如果是一个无限带宽、无限计算资源的世界，这段代码直观又易 懂，而且工作的非常好：它会从第三方股票系统读到股票数据，并且更新到界面上。可惜不是。这段代码在现实世界工作的时候，当用户点击这个按钮，整个界面会 冻结──知道那种感觉吗？就是点完这个按钮，界面不动了；如果你在使用Windows, 然后尝试拽住窗口到处移动，你会发现这个窗口经过的地方都是白的。你的客户不会理解你的程序实际上在很努力的从股票市场获得数据，他们只会很愤怒的说，这 个东西把我的机器弄死了！他们的思路被打断了。于是他们不再使用你的程序，你们的合作没了。你没钱了。你的狗也跑了。</p>
<p>出现界面冻结的原因是，耗时操作阻塞了UI线程。UI线程一般负责着渲染界面，响应用户交互，如果这个线程被阻塞，它将无法响应所有的用户交互请求，甚至 包括拖拽窗口这样简单的操作。所有的界面框架，无论是Java/.NET/ActionScript/JavaScript, 都只有一个UI线程，这个估计永远都不会变。</p>
<p>用户看到的应用通常与程序员大相径庭。用户对应用的期待级别分别是：能用、可用、好用、好看。而我观察到的大多数程序员停留在第一阶段：能用。&#8220;一切皆异步&#8221;这个原则说来简单，做起来也不会很难。把上面的代码稍作改动，如下：</p>
<pre>void updateStockDataButton_clicked() {
&nbsp;&nbsp;&nbsp; <strong>runInAnotherThread</strong>( function () {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stockData = stockDataService.getLatest(); // 从远程获取股票信息
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; updateUI(stockData); // 这个方法在UI线程更新界面
&nbsp; &nbsp; }
}</pre>
<p>注意加粗部分。<code>runInAnotherThread</code>是跟语言平台特定的。对于.net C#，可以是一个<code>Dispatcher+delegate</code>或者<code>ThreadPool.QueueUserWorkItem</code>；对于Java，可以干脆是一个<code>Runable</code>。对于AJAX, 可以是<code>XMLHttpRequest</code>或者把这个计算扔到一个<code>IFrame</code>中；对于ActionScript, 似乎没有什么好的方法，把获取数据的部分交给<code>XML.load</code>然后通过事件回调的方式来进行界面刷新吧。</p>
<p>耗时操作一般两种来源产生：网络带来的延迟以及大规模运算。两者对应的异步实现方式有所不同。前者往往可以通过特定语言、平台的获取数据的方式来进行异步，特别是缺乏多线程特性的动态语言。例如典型的AJAX方式：</p>
<pre>xhr = new XmlHttpRequest()
xhr.send("POST", '/stockData/MSFT', function() {
&nbsp;&nbsp;&nbsp; doSomethingWith(xhr.responseText);&nbsp; // 只有当数据返回的时候，才会调用
})</pre>
<p>大规模运算带来的耗时在Java/C#等支持多线程的语言环境中很容易实现，而对于JavaScript/ActionScript等很难，折衷的方式是 将复杂运算延迟到服务器端进行；或者将复杂运算拆解成若干个耗时较少的小运算，例如ActionScript的伪多线程实现方式。</p>
<p>&#8220;一切皆异步&#8221;这个原则说来容易，但要在企业应用中以一种一致的方式进行实现很难。上例中<code>runInAnotherThread</code>的方式貌似简单，也可能出 现在各种GUI框架的介绍中，但绝不是一个稍具规模的RichClient应当采用的方式。它很难作为一种编程范式被遵循，你绝不会希望看到在你的代码中 所有用到异步的地方都<code>new Runnable(){...}</code>。这样带来的问题不仅仅是异步被不被管理的到处乱扔，还带来了测试的复杂性。为了解决这些只有在至少有点规模的 RichClient中才出现的问题，你最好也实现了&#8220;4 线程管理&#8221;（见下篇），能够实现&#8220;3 事件管理&#8221;（见下篇）更好。终极方式是将这些抽象到应用的基础框架中，使得所有的开发人员以一种一致的方式进行编程。</p>
<h2>2 视图管理</h2>
<h3>2.1 视图生命周期管理</h3>
<p>视图这个概念在WEB开发中几乎被忽略。这里所说的视图是指页面、页面块等界面元素。在WEB开发中，视图的生命周期很短：在进入页面的时候创建，在离开页面的时候销毁。一不小心页面被弄糟了，或者不能按照预期的渲染了，点下<strong>刷新</strong>按钮，整个世界一片清净。</p>
<p>WEB下的视图导航也是如此自然。基于超链接的方式，每点击一次，就能够打开一个新的页面，旧的页面被浏览器销毁，新的页面诞生。（这里不考虑AJAX或者其他JavaScript特效）</p>
<p>如果把这种想法带入到RichClient开发，后果会很糟糕。每当点击按钮或者进行其他操作需要导航到新的窗口，你不加任何限制的创建新窗口或者新的视 图。然而CPU不是无限的。创建一个新的视图通常是很耗CPU和内存的。系统响应会变慢。用户会抱怨，拒绝付钱，于是因为饥饿，你的狗再次离开了你。</p>
<p>每次新创建视图产生的严重后果并不仅仅是非功能性的，还包括功能性的缺失。如果你用过Skype，当你在给张三通话的时候，再次点击张三并且进行通话，你 会发现刚刚的通话界面会弹出来，而不是开启新窗口。在我们的一个项目中，有一个功能：点击软件界面上的电话号码就能开启一个新窗口，并直接连到桌上的电话 拨号通话。可以想象，如果每次都会弹出新的窗口，软件的逻辑是根本错误的。</p>
<p>如何解决这个问题？最简单的方式是将所有已知的视图全都保存到本地的一个缓存中，我们命名为<code>ViewFactory</code>，当需要进行获取某个视图的时候，直接从<code>ViewFactory</code>拿到，如果没有创建，那么创建，并放到Cache中：</p>
<pre>class ViewFactory {
&nbsp;&nbsp;&nbsp;&nbsp; cache = {}
&nbsp;&nbsp;&nbsp;&nbsp; View getView(Object key) {
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; if cache.contains(key) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return cache[key]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; cache[key] = createView(key)
&nbsp; &nbsp; &nbsp; &nbsp; return cache[key] &nbsp;
&nbsp;&nbsp;&nbsp; }
}</pre>
<p>需要注意的是，<code>ViewFactory</code>中<code>key</code>的选择。对于简单的应用，<code>key</code>可以干脆就是某个单独窗口的类名。例如整个系统中往往只有一个配置窗口，那 么key就是这个类名；对于需要复用的窗口，往往需要根据其业务主键来创建相应的视图。例如代码中只有一个<code>UserDetailWindow</code>, 需要用来展示不同用户的信息。当需要同时显示两个以上的用户信息的时候，用同一个窗口实例显然不对。这时候<code>key</code>的选择可以是类名+用户ID。</p>
<h3>2.2 视图导航</h3>
<p>上面的方案并没有解决导航的问题。导航需要解决的问题有两个，如何导航以及如何在导航时传递数据。这时候不得不羡慕WEB的解决方式。我要访问<code>ID</code>为<code>1</code>的用户信息，只需要访问类似于<code>users/1</code>的页面就好；需要访问搜索结果第5页，只需要访问<code>/search?q=someword&amp;page=5</code>就好。这里<code>/search</code>是视图，<code>q=someword</code>和<code>page=5</code>是传递的数据。目前我还没有发现任何一本书来讲述如何进行视图导航。我们的方式是实现一个<code>Navigator</code>类用来导航，<code>Navigator</code>依赖于前面提到的<code>ViewFactory</code>：</p>
<pre>class Navigator {
&nbsp;&nbsp;&nbsp; Navigator(ViewFactory viewFactory) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; this.viewFactory = viewFactory;
&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; void goTo(Object viewKey) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; this.viewFactory.getView(viewKey).show()
&nbsp;&nbsp; &nbsp;}
}</pre>
<p>（这个类看起来跟<code>ViewFactory</code>没什么大的差别，但他们逻辑上是完全不同，并且下面的扩展中会增强）</p>
<p>这样是可以解决问题的。如果要在不同的视图之间传递数据，只需要对<code>Navigator.goTo</code>方法稍加扩展，多添加一个参数就能够传递参数了。例如，在用户列表窗口点击用户名，发送一条消息并打开聊天窗口，可以写为：</p>
<pre>void messageButton_clicked() {
&nbsp;&nbsp; &nbsp;Navigator.goTo("ChatWindow#userId", "<font size="2">聊天消息</font>")
}</pre>
<p>然而这种方式并不完美。当你发现大量的数据在窗口之间交互的时候，这种将主动权交给调用方控制的方式，会给状态同步带来不少麻烦；如果你使用了本地存储，它越过存储层直接与服务器交互的方式也会带来不少的不便之处。更好的方式是使用&#8220;3 事件管理&#8221;（见下篇）。当然，如果窗口之间导航不存在数据传递，基于<code>Navigator</code>的方式仍然简单并且可用。</p>
<p><strong>相关阅读：</strong></p>
<p>[ ThoughtWorks实践集锦（1）] <a href="http://www.infoq.com/cn/articles/thoughtworks-practice-parti" target="_blank">我和敏捷团队的五个约定</a>。</p>
<p>[ ThoughtWorks实践集锦（2）] <a href="http://www.infoq.com/cn/articles/thoughtworks-practice-partii" target="_blank">如何在敏捷开发中做好数据迁移</a>。</p>
<hr />
<p><strong>作者介绍</strong>：陈金洲，Buffalo <a title="" href="http://www.blogjava.net/hulizhong/articles/112412.html" >AJAX中文问题</a> Framework作者，ThoughtWorks咨询师，现居北京。目前的工作主要集中在RichClient开发，同时一直对Web可用性进行观察，并对其实现保持兴趣。</p>
<p>给InfoQ中文站投稿或者参与内容翻译工作，请邮件至<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#100;&#105;&#116;&#111;&#114;&#115;&#64;&#99;&#110;&#46;&#105;&#110;&#102;&#111;&#113;&#46;&#99;&#111;&#109;">editors@cn.infoq.com</a>。也欢迎大家加入到<a href="http://groups.google.com/group/InfoQChina" target="_blank">InfoQ中文站用户讨论组</a>中与我们的编辑和其他读者朋友交流。</p><img src ="http://www.blogjava.net/hulizhong/aggbug/260387.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-03-18 09:52 <a href="http://www.blogjava.net/hulizhong/archive/2009/03/18/260387.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转 RichClient/RIA原则与实践（下）</title><link>http://www.blogjava.net/hulizhong/archive/2009/03/18/260385.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Wed, 18 Mar 2009 01:46:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/03/18/260385.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/260385.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/03/18/260385.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/260385.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/260385.html</trackback:ping><description><![CDATA[转 http://www.infoq.com/cn/articles/thoughtworks-practice-partiii-ii<br />
<br />
<h1>RichClient/RIA原则与实践（下）</h1>
<p class="info">作者 <strong>陈金洲</strong> 发布于 2009年3月11日 下午10时7分 </p>
<dl class="tags2">
<dt class="community">社区
<dd><a id="740" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" href="http://www.infoq.com/cn/dotnet;jsessionid=7B04B1E455E6435129B74685E3D93775" name="dotnet">.NET</a>,
<dd><a id="743" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" href="http://www.infoq.com/cn/agile;jsessionid=7B04B1E455E6435129B74685E3D93775" name="agile">Agile</a>,
<dd><a id="739" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" href="http://www.infoq.com/cn/java;jsessionid=7B04B1E455E6435129B74685E3D93775" name="java">Java</a>
<dt class="topics">主题
<dd><a id="777" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" href="http://www.infoq.com/cn/ria;jsessionid=7B04B1E455E6435129B74685E3D93775" name="ria">RIA</a>,
<dd><a id="830" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" href="http://www.infoq.com/cn/rich-client-desktop;jsessionid=7B04B1E455E6435129B74685E3D93775" name="rich-client-desktop">富客户端/桌面</a>
<dt class="topics">标签
<dd><a id="4,681" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" href="http://www.infoq.com/cn/Principles;jsessionid=7B04B1E455E6435129B74685E3D93775" name="Principles">原则</a> </dd></dl>
<h2>3 事件管理</h2>
<p>事件管理应当是整个RichClient/RIA开发中的最难以把握的部分。这部分控制的好，你的程序用起来将如行云流水，用户的思维不会被打断。任何一 个做RichClient开发的程序员，可以对其他方面毫无所知，但这部分应当非常熟悉。事件是RichClient的核心，是&#8220;<strong>一切皆异步</strong>&#8221;的终极实现。前面所说的例子，实际上可以被抽象为事件，例如第一个，获取股票数据，从事件的观点看，应该是：</p>
<ul>
    <li>开始获取股票数据
    <li>正在获取股票数据
    <li>获取数据完成
    <li>获取数据失败 </li>
</ul>
<br />
<p>看起来相当复杂。然而这样去考虑的时候，你可以将执行计算与界面展现清晰的分开。界面只需要响应事件，运算可以在另外的地方 悄悄的进行，并当任务完成或者失败的是时候报告相应的事件。从经验看来，往往同样的数据会在不同的地方进行不同的展示，例如skype在通话的时候这个人 的头像会显示为占线，而具体的通话窗口中又是另外不同的展现；MSN的个人签名在好友列表窗口中显示为一个点击可以编辑控件，而同时在聊天窗口显示为一个 不能点击只能看的标签。这是RichClient的特性，你永远不知道同一份数据会以什么形式来展现，更要命的是，当数据在一个地方更新的时候，其他所有 能展现的地方都需要同时做相应的更新。如果我们仍然以第一部分的例子，简单采用<code>runInAnoterThread</code>是完全不能解决这个问题的。</p>
<p>我们曾经犯过一些很严重的错误，导致最终即便重构都积重难返。无视事件的抽象带来的影响是架构级别的，小修小补将无济于事。</p>
<p>事件的实现方式可以有很多种。对于没有事件支持的语言，接口或者干脆某一个约束的方法就可以。有事件支持的语言能够享受到好处，但仍然是语法级别的，根本 是一样的。观察者模式在这里很好用。仍然以股票为例，被观察的对象就是获取股票数据对象<code>StockDataRetriver</code>，观察的就是<code>StockWindow</code>：</p>
<pre>StockDataRetriver {
observers: []
retrieve() {
try {
theData = ...// 从远程获取数据
observers.each {|o| o.stockDataReady(theData)}  // 触发数据获取成功事件
} catch {
observers.each { |o| o.stockDataFailed() }  // 触发事件获取失败事件
}
}
}
StockDataRetriver.observers.add(StockWindow)  // 将StockWindow加入到观察者队列
StockWindow {
stockDataReady(theData) {
showDataInUIThread(); // 在UI线程显示数据
}
stockDataFailed() {
showErrorInUIThread(); // 在UI线程显示错误
}
}
</pre>
<p>你会发现代码变得简单。UI与计算之间的耦合被事件解开，并且区分UI线程与运算线程之间也变得容易。当尝试以事件的视角去观察整个应用程序的时候，你会更关注于用户与界面之间的交互。</p>
<p>让我们继续抽象。如果把&#8220;获取股票数据&#8221;这个按钮点击，让<code>StockDataRetriver</code>去获取数据当作事件来处理，应该怎么写呢？将按钮作为被观察 者，<code>StockDataRetriver</code>作为观察者显然不好，好不容易分开的耦合又黏在一起。引入一个中间的<code>Events</code>看起来不错：</p>
<pre>Events {
listeners: {}
register(eventId, listener) {
listeners[eventId].add(listener)
}
broadcast(eventId) {
listeners[eventId].observers.each{|o| o.doSomething(); }
}
}
</pre>
<p><code>Events</code>中维护了一个<code>listeners</code>的列表，它是一个简单的Hash结构，key是<code>eventId</code>，value是<code>observer</code>的列表；它提供了两个方法，用来注册事件监听以及通知事件产生。对于上面的案例，可以先注册<code>StockDataRetriver</code>为一个观察者，观察<code>start_retrive_stock_data</code>事件：</p>
<pre>Events.register('start_retrive_stock_data', StockDataRetriever)</pre>
<p>当点击&#8220;获取股票数据&#8221;按钮的时候，可以是这样：</p>
<pre>Events.broadcast('start_retrive_stock_data')</pre>
<p>你会发现<code>StockDataRetriver</code>能够老老实实的开始获取数据了。</p>
<p>需要注意的是，并非将所有事件定义为全局事件是一个好的实践。在更大规模的系统中，将事件进行有效整理和分级是有好处的。在强类型的语言（如 Java/C#）中，抽象出强类型的<code>EventId</code>，能够帮助理解系统和进行编程，避免到处进行强制类型转换。例如，<code>StockEvent</code>：</p>
<pre>StockDataLoadedEvent {
StockData theData;
StockDataLoadedEvent(StockData theData);
}
Event.broadcast(new StockDataLoadedEvent(loadedData))
</pre>
<p>这个事件的监听者能够不加类型转换的获得<code>StockData</code>数据。上面的例子是不支持事件的语言，C#语言支持自定义强类型的事件，用起来要自然一些：</p>
<pre>delegate void StockDataLoaded(StockData theData)</pre>
<p>事件管理原则我相信并不难理解。然而困难的是具体实现。对一个新的UI框架不熟悉的时候，我们经常在&#8220;代码的优美&#8221;与&#8220;界面提供的特性&#8221;之间徘徊。实现这 样的一个事件架构需要在项目一开始就稍具雏形，并且所有的事件都有良好的命名和管理。避免在命名、使用事件的时候的随意性，对于让代码可读、应用稳定有非 常大的意义。一个好的事件管理、通知机制是一个良好RichClient应用的根本基础。一般说来，你正在使用的编程平台如Swing/WinForm /WPF/Flex等能够提供良好的事件响应机制，即监听事件、onXXX等，但一般没有统一的事件的监听和管理机制。对于架构师，对于要使用的编程平台 对于这些的原生支持要了熟于心，在编写这样的事件架构的时候也能兼顾这些语言、平台提供给你的支持。</p>
<p>采用了事件的事件后，你不得不同时实践&#8220;<strong>线程管理</strong>&#8221;，因为事件一般来说意味着将耗时的操作放到别的地方完成，当完成的时候进行事件通知。简单的模式下，你可以在所有需要进行异步运算的地方，将运算放到另外一个线程，如<code>ThreadPool.QueueUserWorkItem</code>, 在运算完成的时候通知事件。但从资源的角度考虑，将这些线程资源有效的管理也是很重要的，在&#8220;线程管理&#8221;部分有详细的阐述。另外，如果能将你的应用转变为 数据驱动的，你需要关注&#8220;<strong>缓存以及本地存储</strong>&#8221;。</p>
<h2>4 线程管理</h2>
<p>在WEB开发几乎无需考虑线程，所有的页面渲染由浏览器完成，浏览器会异步的进行文字和图片的渲染。我们只需要写界面和JavaScript就好。如果你认同&#8220;一切皆异步&#8221;，你一定得考虑线程管理。</p>
<p>毫无管理的线程处理是这样的：凡是需要进行异步调用的地方，都新起一个线程来进行运算，例如前面提到的<code>runInThread</code>的实现。这种方式如果托管在 在&#8220;事件管理&#8221;之下，问题不大，只会给测试带来一些麻烦：你不得不wait一段时间来确定是否耗时操作完成。这种方式很山寨，也无法实现更高级功能。更好 的的方式是将这些线程资源进行统筹管理。</p>
<p>线程的管理的核心功能是用来统一化所有的耗时操作，最简单的<code>TaskExecutor</code>如下：</p>
<pre>TaskExecutor {
void pendTask(task) { //task: 耗时操作任务
runInThread {
task.run(); // 运行任务
}
}
}
RetrieveStockDataTask extends Task {
void run() {
theData = ... // 直接获取远程数据，不用在另外线程中执行
Events.broadcast(new StockDataLoadedEvent(theData)) // 广播事件
}
}
</pre>
<p>需要进行这个操作的时候，只需要执行类似于下面的代码：</p>
<pre>TaskExecutor.pendTask(new RetrieveStockDataTask())</pre>
<p>好处很明显。通过引入<code>TaskExecutor</code>，所有线程管理放在同一个地方，耗时操作不需要自行维护线程的生命周期。你可以在<code>TaskExecutor</code>中灵活定义线程策略实现一些有趣的效果，如暂停执行，监控任务状况等，如果你愿意，为了更好的进行调试跟踪，你甚至可以将所有的任务以同步的方式执行。</p>
<p>耗时任务的定义与执行被分开，使得在任务内部能够按照正常的方式进行编码。测试也很容易写了。</p>
<p>不同的语言平台会提供不同的线程管理能力。.NET2.0提供了<code>BackgroundWorker</code>, 提供了一序列对多线程调用的封装，事件如开始调用，调用，跨线程返回值，报告运算进度等等。它内部也实现了对线程的调度处理。在你要开始实现类似的TaskExecutor时，参考一下它的API设计会有参考价值。Java 6提供的Executor也不错。</p>
<p>一个完善的<code>TaskExecutor</code>可以包含如下功能：</p>
<ul>
    <li><code>Task</code>的定义：一个通用的任务定义。最简单的就是<code>run()</code>，复杂的可以加上生命周期的管理：<code>start()</code>、<code>end()</code>、<code>success()</code>、<code>fail()</code>..取决于要控制到多么细致的粒度。
    <li><code>pendTask</code>，将任务放入运算线程中
    <li><code>reportStatus</code>，报告运算状态
    <li>事件：任务完成
    <li>事件：任务失败 </li>
</ul>
<p>写这样的一个线程管理的不难。最简单的实现就是每当<code>pendTask</code>的时候新开线程，当运算结束的时候报告状态。或者使用像<code>BackgroundWorker</code>或者<code>Executor</code>这样的高级API。对于像ActionScript/JavaScript这样的，只能用伪线程， 或者干脆将无法拆解的任务扔到服务器端完成。</p>
<h2>5 缓存与本地存储</h2>
<p>纯粹的B/S结构，浏览器不持有任何数据，包括基本不变的界面和实际展现的数据。RichClient的一大进步是将界面部分本地持有，与服务器只作数据通讯，从而降低数据流量。像《魔兽世界》10多G的超大型客户端，在普通的拨号网络都可以顺畅的游戏。</p>
<p>缓存与本地存储之间的差别在于，前者是在线模式下，将一段时间不变的数据缓存，最少的与服务器进行交互，更快的响应客户；后者是在离线模式下，应用仍然能 够完成某些功能。一般来说，凡是需要类似于&#8220;查看XXX历史&#8221;功能的，需要&#8220;点击列表查看详细信息&#8221;的，都会存在本地存储的必要，无论这个功能是否需要向 用户开放。</p>
<p>无论是缓存还是本地存储，最需要处理的问题如何处理本地数据与服务器数据之间的更新机制。当新数据来的时候，当旧数据更新的时候，当数据被删除的时候，等 等。一般来说，引入这个实践，最好也实现基于数据变化的&#8220;事件管理&#8221;。如果能够实现&#8220;客户机-服务器数据交互模式&#8221;那就更完美了。</p>
<p>我们犯过这样一个错误。系统启动的时候，将当前用户的联系人列表读取出来，放到内存中。当用户双击这个联系人的时候，弹出这个联系人的详细信息窗口。由于 没有本地存储，由于采用了Navigator方式的导航，于是很自然的采用了<code>Navigator.goTo('ContactDetailWindow', theContactInfo)</code>。由于列表页面一般是不变的，因此显示出来的永远是那份旧的数据。后来有了编辑联系人信息的功能，为了总是显示更新的数 据，我们将调用更改为<code>Navigator.goTo('ContactDetailWindow', 'contactId')</code>，然后在<code>ContactDetailWindow</code>中按照<code>contactId</code>把联系人信息重新读取一次。远在南非的用户抱怨慢。还 好我没养狗，没有狗离开我。后来我们慢慢的实现了本地存储，所有的数据读取都从这个地方获得。当数据需要更新的时候，直接更新这个本地存储。</p>
<p>本地存储会在根本上影响RichClient程序的架构。除非本地不保存任何信息，否则本地存储一定需要优先考虑。某些编程平台需要你在本地存储界面和数 据，如Google Gears的本地存储，置于Adobe Air的AJAX应用等，某些编程平台只需要存储数据，因为界面完全是本地绘制的，如Java/JavaFX/WinForm/WPF等。缓存界面与缓存 数据在实现上差别很大。</p>
<p>本地存储的存储机制最好是采用某一种基于文件的关系数据库，如SQLite、H2（HypersonicSQL）、Firebird等。一旦确定要采用本地存储，就从成熟的数据库中选择一个，而不要尝试着自己写基于文件的某种缓存机制。你会发现到最后你实现了一个山寨版的数据库。</p>
<p>在没有考虑本地存储之前，与远端的数据访问是直接连接的：</p>
<p class="image-wide"><img alt="" src="http://www.infoq.com/resource/articles/thoughtworks-practice-partiii-ii/zh/resources/1.JPG;jsessionid=7B04B1E455E6435129B74685E3D93775" _href="img://1.JPG" /></p>
<p>我们上面的例子说明，一旦考虑使用本地存储，就不能直接访问远程服务器，那么就需要一个中间的数据层：</p>
<p class="image-wide"><img alt="" src="http://www.infoq.com/resource/articles/thoughtworks-practice-partiii-ii/zh/resources/2.JPG;jsessionid=7B04B1E455E6435129B74685E3D93775" _href="img://2.JPG" /></p>
<p>数据层的主要职责是维护本地存储与远程服务器之间的数据同步，并提供与应用相关的数据缓存、更新机制。数据更新机制有两种，一种是Proxy（代理）模式，一种是自动同步模式。</p>
<p>代理模式比较容易理解。每当需要访问数据的时候，将请求发送到这个代理。这个代理会检查本地是否可用，如果可用，如缓存处于有效期，那么直接从本地读取数 据，否则它会真正去访问远端服务器，获取数据，更新缓存并返回数据。这种手工处理同步的方式简单并且容易控制。当应用处于离线模式的时候仍然可以工作的很 好。</p>
<p class="image-wide"><img alt="" src="http://www.infoq.com/resource/articles/thoughtworks-practice-partiii-ii/zh/resources/3.JPG;jsessionid=7B04B1E455E6435129B74685E3D93775" _href="img://3.JPG" /></p>
<p>自动同步模式下，客户端变成都针对本地数据层。有一个健壮的自动同步机制与服务器的保持长连接，保证数据一直都是更新的。这种方式在应用需要完全本地可运行的时候工作的非常好。如果设计得好，自动同步方式健壮的话，这种方式会给编程带来极大的便利。</p>
<p class="image-wide"><img alt="" src="http://www.infoq.com/resource/articles/thoughtworks-practice-partiii-ii/zh/resources/4.JPG;jsessionid=7B04B1E455E6435129B74685E3D93775" _href="img://4.JPG" /></p>
<p>说到同步，很多人会考虑数据库自带的自动同步机制。我完全不推荐数据库自带的机制。他们的设计初衷本身是为了数据库备份，以及可扩展性 （Scalability）的考虑。在应用层面，数据库的同步机制往往不知道具体应用需要进行哪些数据的同步，同步周期等等。更致命的是，这种机制或多或 少会要求客户端与服务器端具备类似的数据库表结构，迁就这样的设计会给客户端的缓存表设计带来很大的局限。另外，它对客户机-服务器连接也存在一定的局限 性，例如需要开放特定端口，特定服务等等。对于纯粹的Internet应用，这种方式更是完全不可行的，你根本不知道远程数据库的结构，例如 Flickr, Google Docs.</p>
<p>当本地存储+自动同步机制与&#8220;事件管理&#8221;都实现的时候，应用会是一种全新的架构：基于数据驱动的事件结构。对于所有本地数据的增删改都定义为事件，将关心 这些数据的视图都注册为响应的观察者，彻底将数据的变化于展现隔离。界面永远只是被动的响应数据的变化，在我看来，这是最极致的方式。</p>
<h2>结尾</h2>
<p>限于篇幅，这篇文章并没有很深入的讨论每一种原则/实践。同时还有一些在RichClient中需要考虑的东西我们并没有讨论：</p>
<ul>
    <li><strong>纯Internat应用离线模式的实现</strong>。像AdobeAir/Google Gears都有离线模式和本地存储的支持，他们的特点是缓存的不仅仅是数据，还包括界面。虽然常规的企业应用不太可能包含这些特性，但也具备借鉴意义。
    <li><strong>状态的控制</strong>。例如管理员能够看到编辑按钮而普通用户无法看见，例如不同操作系统下的快捷键不同。简单情况下，通过if-else或者对应编程平台下提供的绑定能够完成，然而涉及到更复杂的情况时，特别是网络游戏中大量互斥状态时，一个设计良好的分层状态机模型能够解决这些问题。如何定义、分析这些状态之间的互斥、并行关系，也是处理超复杂
    <li><strong>测试性</strong>。如何对RichClient进行测试？特别是像WPF、JavaFX、Adobe Air等用Runtime+编程实现的框架。它们控制了视图的创建过程，并且倾向于绑定来进行界面更新。采用传统的MVP/MVC方式会带来巨大的不必要的工作量（我们这么做过！），而且测试带来的价值并没有想象那么高。
    <li><strong>客户机-服务器数据交互模式</strong>。如何进行客户机服务器之间的数据交互？最简单的方式是类似于Http Request/Response。这种方式对于单用户程序工作得很好，但当用户之间需要进行交互的时候，会面临巨大挑战。例如，股票代理人关注亚洲银行板块，刚好有一篇新的关于这方面的评论出现，股票代理人需要在最多5分钟内知道这个消息。如果是Http Request/Response, 你不得不做每隔5分钟刷一次的蠢事，虽然大多数时候都不会给你数据。项目一旦开始，就应当仔细考虑是否存在这样的需求来选择如何进行交互。这部分与本地存储也有密切的关系。
    <li><strong>部署方式</strong>。RichClient与B/S 直接最大的差异就是，它需要本地安装。如何进行版本检测以及自动升级？如何进行分发？在大规模访问的时候如何进行服务器端分布式部署？这些问题有些被新技术解决了，例如Adobe Air以及Google Gears，但仍然存在考虑的空间。如果是一个安全要求较高的应用，还需要考虑两端之间的安全加密以及客户端正确性验证。新的UI框架层出不穷。开始一个新的RichClient项目的时候，作为架构师/Tech Lead首先应当关注的不是华丽的界面和效果，应当观察如何将上述原则和时间华丽的界面框架结合起来。就像我们开始一个web项目就会考虑domain 层、持久层、服务层、web层的技术选型一样，这些原则和实践也是项目一开始就考虑的问题。 </li>
</ul>
<h2>感谢</h2>
<p>感谢我的同事周小强、付莹在我写作过程中提供的无私的建议和帮助。小强推荐了介绍Google Gears架构的链接，让我能够写作&#8220;本地存储&#8221;部分有了更深的体会。</p>
<p>这篇文章是我近两年来在RichClient工作、网络游戏、WebGame众多思考的一个集合。我尝试过JavaFX/WPF/AdobAir 以及相关的文章，然而大多数的例子都是从华丽的界面入手，没有实践相关的内容。有意思的反而是《大型多人在线游戏开发》这本书，给了我在企业 RichClient开发很多启发。我们曾经犯了很多错误，也获得了许多经验，以后我们应当能做得更好。</p>
<h2>参考</h2>
<ul>
    <li>.NET的BackgroundWorker定义：<a href="http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx" target="_blank">http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx</a>
    <li>ActionScript的伪线程：<a href="http://blogs.adobe.com/aharui/2008/01/threads_in_actionscript_3.html" target="_blank">http://blogs.adobe.com/aharui/2008/01/threads_in_actionscript_3.html</a>
    <li>Google Gears架构：<a href="http://code.google.com/apis/gears/architecture.html" target="_blank">http://code.google.com/apis/gears/architecture.html</a> </li>
</ul>
<p><strong>相关阅读：</strong></p>
<p>[ ThoughtWorks实践集锦（1）] <a href="http://www.infoq.com/cn/articles/thoughtworks-practice-parti;jsessionid=7B04B1E455E6435129B74685E3D93775" target="_blank">我和敏捷团队的五个约定</a>。</p>
<p>[ ThoughtWorks实践集锦（2）] <a href="http://www.infoq.com/cn/articles/thoughtworks-practice-partii;jsessionid=7B04B1E455E6435129B74685E3D93775" target="_blank">如何在敏捷开发中做好数据迁移</a>。</p>
<p>[ ThoughtWorks实践集锦（3）] <a href="http://www.infoq.com/cn/articles/thoughtworks-practice-partiii;jsessionid=7B04B1E455E6435129B74685E3D93775" target="_blank">RichClient/RIA原则与实践（上）</a>。</p>
<hr />
<p><strong>作者介绍</strong>：陈金洲，Buffalo <a title="" href="http://www.blogjava.net/hulizhong/articles/112412.html" >AJAX中文问题</a> Framework作者，ThoughtWorks咨询师，现居北京。目前的工作主要集中在RichClient开发，同时一直对Web可用性进行观察，并对其实现保持兴趣。</p>
<p>给InfoQ中文站投稿或者参与内容翻译工作，请邮件至<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#100;&#105;&#116;&#111;&#114;&#115;&#64;&#99;&#110;&#46;&#105;&#110;&#102;&#111;&#113;&#46;&#99;&#111;&#109;">editors@cn.infoq.com</a>。也欢迎大家加入到<a href="http://groups.google.com/group/InfoQChina" target="_blank">InfoQ中文站用户讨论组</a>中与我们的编辑和其他读者朋友交流。</p><img src ="http://www.blogjava.net/hulizhong/aggbug/260385.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-03-18 09:46 <a href="http://www.blogjava.net/hulizhong/archive/2009/03/18/260385.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转:web应用的跨越访问的解决方案</title><link>http://www.blogjava.net/hulizhong/archive/2009/02/17/255084.html</link><dc:creator>二胡</dc:creator><author>二胡</author><pubDate>Tue, 17 Feb 2009 06:23:00 GMT</pubDate><guid>http://www.blogjava.net/hulizhong/archive/2009/02/17/255084.html</guid><wfw:comment>http://www.blogjava.net/hulizhong/comments/255084.html</wfw:comment><comments>http://www.blogjava.net/hulizhong/archive/2009/02/17/255084.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hulizhong/comments/commentRss/255084.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hulizhong/services/trackbacks/255084.html</trackback:ping><description><![CDATA[转：http://www.blogjava.net/soddabao/archive/2007/06/17/124809.html<br />
做过跨越多个网站的Ajax开发的朋友都知道，如果在A网站中，我们希望使用Ajax来获得B网站中的特定内容，如果A网站与B网站不在同一个域中，那么就出现了跨域访问问题。<br />
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Ajax的跨域访问问题是现有的Ajax开发人员比较常遇到的问题。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IE对于跨域访问的处理是，弹出警告框，提醒用户。如果用户将该网站纳入可信任网站，或者调低安全级别，那么这个问题IE就不会在提醒你。&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FireFox等其它非微软的浏览器遇到跨域访问，则解决方案统一是拒绝访问。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;有
人说，IE是主流浏览器，只要它能正常使用就好了。此言差已，IE虽然能够处理，但是是有前提的，要么用户不厌其烦地在页面弹出警告框之后点击是（点击否
就不执行该Ajax调用了），要么用户将该网站纳入可信任站点。这两种做法，在企业管理系统的应用中倒是比较常见，因为系统管理员可以以行政手段保证用户
的行为。但是对于互联网上的网站或者门户开发，这种做法则不行。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最近遇到了这个问题，需要在跨域访问结束之后完成使主窗口出现一些特效，搜索了一些资料，通过不断尝试以及在不同浏览器中进行兼容性测试，找到了几个可行的方案：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1、
Web代理的方式。即用户访问A网站时所产生的对B网站的跨域访问请求均提交到A网站的指定页面，由该页面代替用户页面完成交互，从而返回合适的结果。此
方案可以解决现阶段所能够想到的多数跨域访问问题，但要求A网站提供Web代理的支持，因此A网站与B网站之间必须是紧密协作的，且每次交互过程，A网站
的服务器负担增加，且无法代用户保存session状态。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2、on-Demand方式。MYMSN的门户就用的这种方式，不过
MYMSN中不涉及跨域访问问题。动态控制script标记的生成，通过修改script标记的src属性完成对跨域页面的调用。此方案存在的缺陷
是，script的src属性完成该调用时采取的方式时get方式，如果请求时传递的字符串过大时，可能会无法正常运行。不过此方案非常适合聚合类门户使
用。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3、iframe方式。查看过醒来在javaeye上的一篇关于跨域访问的帖子，他提到自己已经用iframe的方式解决
了跨域访问问题。数据提交跟获取，采用iframe这种方式的确可以了，但由于父窗口与子窗口之间不能交互（跨域访问的情况下，这种交互被拒绝），因此无
法完成对父窗口效果的影响。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4、用户本地转储方式：IE本身依附于windows平台的特性为我们提供了一种基于
iframe，利用内存来&#8220;绕行&#8221;的方案，即两个window之间可以在客户端通过windows剪贴板的方式进行数据传输，只需要在接受数据的一方设置
Interval进行轮询，获得结果后清除Interval即可。FF的平台独立性决定了它不支持剪贴板这种方式，而以往版本的FF中存在的插件漏洞又被
fixed了，所以FF无法通过内存来完成暗渡陈仓。而由于文件操作FF也没有提供支持（无法通过Cookie跨域完成数据传递），致使这种技巧性的方式
只能在IE中使用。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5、我自己用于解决这类问题的方式：结合了前面几种方式，在访问A网站时，先请求B网站完成数据处理，再根
据返回的标识来获得所需的结果。这种方法的缺点也很明显，B网站的负载增大了。优点，对session也实现了保持，同时A网站与B网站页面间的交互能力
增强了。最重要的一点，这种方案满足了我的全部需要.</p>
<br />
<br /><img src ="http://www.blogjava.net/hulizhong/aggbug/255084.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hulizhong/" target="_blank">二胡</a> 2009-02-17 14:23 <a href="http://www.blogjava.net/hulizhong/archive/2009/02/17/255084.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>