<?xml version="1.0" encoding="gb2312"?>

<!-- RSS generated by oioj.net on 4/16/2004 ; 感谢LeXRus提供 RSS 2.0 文档; 此文件可自由使用，但请保留此行信息 --> 
<!-- Source download URL: http://blogger.org.cn/blog/rss2.asp       -->
<rss version="2.0">

<channel>
<title>Tonny's Life</title>
<link>http://blogger.org.cn/blog/blog.asp?name=tonny1982</link>
<description>tonny的博客</description>
<copyright>blogger.org.cn</copyright>
<generator>W3CHINA Blog</generator>
<webMaster>webmaster@blogger.org.cn</webMaster>
<item>
<title><![CDATA[现在什么都致癌：（]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=5055</link>
<author>tonny1982</author>
<pubDate>2005/4/18 12:48:42</pubDate>
<description><![CDATA[<DIV align=left><SPAN class=style10><STRONG>【关注】</STRONG>根据美国最新研究显示，数十种牙膏、洗手液、洗洁精等抗菌清洁用品，当中包括高露洁等品牌的产品，含有三氯生的化学物质，与经氯消毒的水接触后会产生强的心脏血管抑制剂 “哥罗芳”，长期使用可致癌。</SPAN><SPAN class=style11>[<A href="http://finance.sina.com.cn/xiaofei/consume/20050418/01461524708.shtml" target=_blank>全文</A>][<A class=style12 href="http://comment.news.sina.com.cn/cgi-bin/comment/comment.cgi?channel=cj2&amp;newsid=31-1-1524708&amp;face=&amp;style=1" target=_blank><FONT color=red>评论</FONT></A>]</SPAN></DIV>]]></description>
</item><item>
<title><![CDATA[身体倒是没什么不适，但是还是咳嗽]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=5053</link>
<author>tonny1982</author>
<pubDate>2005/4/18 12:00:33</pubDate>
<description><![CDATA[<P>一早就被自己的咳嗽声吵醒，真是郁闷，狂郁闷</P>
<P>不过本来也是要早期的，因为越了学工处的老师，要过去一下。可能是因为我上次显得有点不耐烦，所以今天那个老师特别客气。回来的时候还不停说，这几天你安心写论文吧，我尽量少来麻烦你，等论文完了你可要帮我做点事啊。有点意外：）我想他们是怕我罢工，其实说实话学校里面我还真不想和老师有什么不开心，毕竟还要在这里呆上几年，要知道“人总有走窄了的时候”</P>
<P>别人对我客气我当然也会对别人和客气了，所以今天特别有耐心，心情也算差，但是还是咳嗽（不会是sars又来了吧：）</P>]]></description>
</item><item>
<title><![CDATA[怎样安排肌肉练习的顺序zz]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=5017</link>
<author>tonny1982</author>
<pubDate>2005/4/16 19:53:30</pubDate>
<description><![CDATA[<P>&nbsp;一、大肌群先练 这是应当遵守的一条训练原则。不可由着性子，拿起器械就随意练。腿部、胸部、和背部的肌群属于大肌群，这些肌群的练习需要使用较重的负荷，否则难于取得效果。所以，当你精力充沛、能够克服较重的负荷时，就要先练这些肌群。若等到训练课快结束时再练这些肌群，那就力不从心了，效果也会大打折扣，甚至会出现伤害事故。 例如，某人常以三组90公斤杠铃的卧推练习开始训练，课的后半部是用30公斤杠铃做三组三头肌下推练习。一天，他颠倒了训练顺序，先用34公斤的杠铃做三头肌下推练习，到练卧推时，最多只能推起79公斤的杠铃了。这是因为三头肌在下推练习中已经受到了一定负荷的训练，疲劳了，无力再完成三组90公斤杠铃的卧推了。在这种情况下，对疲劳的三头肌来说，79公斤的重量已接近极限负荷，但对发展胸和肩带肌肉来说却远远不够。显然，先完成有较多肌肉群参加的练习，再完成单一肌肉的练习，效果要好得多。因为单一肌肉练习时使用较小的重量并不影响其它肌群，而单一肌肉本身却能获得大的应力刺激。</P>
<P>&nbsp;二、肌肉要轮流交替训练 这也是一条训练原则。同一块肌肉不要连续进行练习。交替练习的肌群在每一次练习后能得到一定的恢复，因而在每二次练习时能相对承受更大的负荷。由于肌肉力量与体积的发展与训练强度紧密相关，所以交替练习更有利于肌肉体积和力量的增长。 例如，在卧推和三头肌下推练习中，三头肌是原动肌。练习者做完卧推后立即做三头肌下推练习，能克服30公斤的阻力。若在两组练习间完成一组站立肘屈伸练习，那他在做下推练习时就能克服34公斤的阻力，因为疲劳的三头肌得到了一定的恢复。训练者若有意对同块肌肉进行连续刺激，那就要注意，恢复间歇必须足够。</P>]]></description>
</item><item>
<title><![CDATA[生病了，心情也变得不好：（]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4940</link>
<author>tonny1982</author>
<pubDate>2005/4/14 13:20:26</pubDate>
<description><![CDATA[<P>昨天还好好的，但是一早就醒过来，感觉自己生病了。没有发烧，没有咳嗽，只是全身没有力气，头晕...起来没五分钟，又在下铺的床上睡了（陈QQ起来吓了一大跳）</P>
<P>&nbsp; 没睡一会儿，学工处的老师打电话过来了，一个字：烦。但是表明上还是得应付着，真是可怜。打完睡下，没一会儿又有电话来了，其实不是我不想接电话，真是因为没有力气说很多话，sorry：（</P>
<P>&nbsp; 看来今天一天都不会过得很爽。中午随便买了一个盒饭，两个字：难吃。吃了一半也算填饱肚子。</P>
<P>该走了，还有点事情做，然后4：00还有可爱的选修课：）</P>]]></description>
</item><item>
<title><![CDATA[你会用Google？估计你会用一半就不错了！[zz]]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4721</link>
<author>tonny1982</author>
<pubDate>2005/4/8 16:41:02</pubDate>
<description><![CDATA[<BR><BR>你会用Google？估计你会用一半就不错了！ 
<DIV>
<UL>
<LI>&nbsp;&nbsp;&nbsp;&nbsp;来源: 转载 &nbsp;&nbsp;&nbsp;&nbsp;日期：03-23 09:43 &nbsp;&nbsp; 阅读数：<FONT class=pageview> 10359</FONT> </LI></UL></DIV>
<DIV class=content id=Zoom>
<P>　　google检索引擎因其准确和快速的检索服务而深受广大网民欢迎，成为他们手中查询信息的必备工具，Google检索引擎本身也不辜负网民的期望，不断推出新的检索功能，除了为帮助人们更精确查找信息而常用的网站内容(site)、网页链路(inurl)、网页标题(intitle)、各种格式文件(filetype)和被链接(link)等检索功能之外，最近，还推出了一系列新的检索服务，总体来说，这些新的检索功能更加人性化，更贴近人们的日常生活和需要，因而很容易被用户接受和使用。<BR>　　1。　汉语拼音输入检索<BR>　　为了方便使用中文的用户在网上搜索，Google允许用户直接在键盘上输入汉语拼音来检索相关事物，例如：<BR>　　输入　shanghaishikebiao<BR>　　检索结果提示：您是不是要找：　上海时刻表　<BR>　　这正是我们需要查找的关键词，用户可以据此浏览相关结果，这包括上海地区的各种交通工具的时刻表。如果需要查找更详细的资料“上海飞机航班时刻表　”，则只要在原来的检索结果“上海时刻表”中间输入feijihangban　，例如：<BR>　　上海feijihangban时刻表<BR>　　Google的这项新功能，可以免除用户在中文和拼音输入方面的互相转换。用户在输入拼音时，不要留有空格，否则Google会误认为英文。Google把拼音与常用的字或者词组一一对应，因此，过于生僻的字或词组不适合于用这个方法查找。</P>
<P>　　2。　中英文字典<BR>　　经常使用计算机的用户手头上自然会有一、两个字典软件，用于查找和翻译中英文的词义。作为一种使用频率较高的工具，Google也提供了一个中英文字典，很方便使用。用户可以按照下列方法查找词义查找英文的中文词义则输入　　fy　　computer　查找中文的英文词义则输入　　翻译　　计算机</P>
<P>　　3。　天气查询<BR>　　天气情况也是人们经常要查询的信息之一，Google　提供的天气查询来自于一个更新及时的中文气象网站，适合中国人使用。用户输入中文和英文都可以查询，例如要了解奥运会期间雅典的天气情况，可以按照下列方法输入：<BR>　　雅典　天气　　athens tq　返回的查询结果中会在第一条出现一个　“雅典天气预报”，用户点击后就可以看到当天雅典的天气情况。如果地名相同者，用户还需要进行一次选择。</P>
<P>　　4。　股票查询<BR>　　查询股票的网站已经很多了，Google提供的股票查询只是更方便一些而已，用户可以按照股票名称、股票代码或者股票名称的声母字母查询，下列为查询“中国联通”股票行情的举例。<BR>　　中国联通　股票　zglt gp<BR>　　gp 600050</P>
<P>　　5。　邮政编码和区号查询<BR>　　人们时常需要查询邮政编码和电话区号，Google提供了这样一个实用的功能，用户据此能够获得所要查询的省市名称，邮政编码及长途电话区号，下面为查询举例：<BR>　　邮编　杭州　　　　　　　<BR>　　区号　绍兴　　　　　　　<BR>　　yb　　杭州　<BR>　　qh　　绍兴<BR>　　需要注意的是用户只能查询到城市级别的邮政编码和区号，而无法进一步查询区县的具体信息。</P>
<P>　　6。　手机归属地查询<BR>　　用户在输入手机号码后可以获得号码段、归属地、卡类型、邮政编码和电话区号的信息，但是从实用的角度看，其中只有手机归属地的信息较为有用。</P>
<P>　　7。　计算器使用<BR>　　Google有计算器的功能，例如在google检索框中输入45*86+35/7，就会得到结果：<BR>　　(45　*　86)　+　(35　/　7)　=　3 875<BR>　　有兴趣的用户，或者经常在计算机上进行运算的用户不妨可以试一试。</P>
<P>　　8。　购物检索<BR>　　Google新开设的购物检索称之为Froogle，网址为http：//froogle。google。com/<BR>　　用户只要输入商品的名称，就可以看到该商品的图片和价格，用户还可以限定一种商品的价格进行检索，并且将检索结果按需要从高价到低价排序列出，许多商品可以直接在网上订购，这种图文并茂的检索购物方式较受用户青睐。</P>
<P>　　9。　美国实用生活信息查询<BR>　　如果用户生活在美国或者短期去美国出差和学习，下面的查询功能或许能对他们又帮助，因为这些检索服务目前仅限于美国。<BR>　　9.1　地区代码查询<BR>　　用户输入美国的地区代码，在返回的检索结果中的第一项就是该地区的地图，用户可以通过不断的点击来找到一条具体的大街。<BR>　　9.2　条形码查询<BR>　　用户输入一个商品的条形码就可以找到有关该产品的说明。<BR>　　9.3　飞机航班查询<BR>　　用户输入一家航空公司的名称和航班号，即可获得该航班的出发地和目的地，起飞和到达时间，以及实际航行情况，目前是否开始检票，抵达目的地候机大楼的舱门号。<BR>　　9.4　车牌号查询<BR>　　用户输入车牌号，可以获得一辆车的名称、型号、出厂年份，车身和发动机情况。<BR>　　9.5　邮件查询<BR>　　用户输入一个邮件的号码，可以获悉该邮件目前的状况，譬如邮件已经抵达或者正在路途上等。</P>
<P>　　10。　检索工具栏<BR>　　Google的检索工具栏功能强大，有拖放和右击检索功能，新闻阅读，广告拦截，网站排名显示和搜索字词标明等，工具栏可以附在浏览器下，使用更加方便。用户可以首先在Google网站下载并安装一个检索工具栏，然后根据需要在工具栏的选项中进行设置，我们推荐用户使用这个检索工具栏，它将给用户带来许多意想不到的方便。</P>
<P>　　10.1 Google　新闻<BR>　　Google　的新闻来源于4500家报刊杂志和通讯社，在精选以后持续播出世界各国的新闻，每条新闻都有更新的时间，并联接相关的新闻。用户可以使用免费的新闻通知服务，通过电子邮件收到自己感兴趣的新闻。<BR>　　10.2　网站排名<BR>　　Google　通过对　Web　链接结构和许多其它变量自动计算，对网页的重要程度进行评定。网站排名根据这个评定能够显示用户正在访问的网站的重要程度，用一根绿色线条显示，直观而明确。<BR>　　10.3　搜索字词标明<BR>　　搜索字词标明通过鲜艳的色彩标明用户所检索的字词在每个网页上的位置，便于用户查阅，单击“搜索字词标明”按钮可以打开和关闭标明状态。<BR>　　10.4　拖放和右击检索<BR>　　拖放检索功能使检索更为方便，用户可以把在网页上所选取的字词直接拖放到检索框中，Google会自动进行检索。对于与　Web浏览器同时运行的许多文字处理应用程序中的文字，也可以使用此功能。右击检索是指用户可以在网页上选取文字后，右击鼠标，然后从弹出式菜单中选择“Google　搜索”便可以自动检索。</P>
<P>　　Google搜索引擎为我们提供了搜索的方便，她正在不断推出新的服务功能，例如为移动电话特别设计的无线搜寻等，相信随着技术的不断发展，我们会从中享受更多的搜索乐趣。　</P></DIV>]]></description>
</item><item>
<title><![CDATA[其实心情也需要像电脑一样整理：）]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4720</link>
<author>tonny1982</author>
<pubDate>2005/4/8 16:32:42</pubDate>
<description><![CDATA[<P>开心ing，速度真是成倍提升－以后不能偷懒了</P>
<P>其实不只是电脑，很多东西都是要靠自己去维护的，心情也是。</P>
<P>有时候郁闷了，可以出去走走，散散心......</P>]]></description>
</item><item>
<title><![CDATA[天气热得像猪一样]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4684</link>
<author>tonny1982</author>
<pubDate>2005/4/7 18:29:57</pubDate>
<description><![CDATA[<P><A><IMG src="http://blogger.org.cn/blog/images/emot/face32.gif">　</A>四月份就是29度的天气，真是有点变态，也不知道是怎么了？</P>
<P>下午去上课，本来人就不是很多的课，也因为天气的原因变得更加荒凉。不过这些课我比较喜欢所以绝不逃课（本来我可是在打乒乓的啊）</P>
<P>真是敬佩自己变成好学生：P坚持...坚持...再坚持<IMG src="http://blogger.org.cn/blog/images/emot/face8.gif"></P>]]></description>
</item><item>
<title><![CDATA[腰酸背痛腿抽筋：（]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4584</link>
<author>tonny1982</author>
<pubDate>2005/4/6 10:18:01</pubDate>
<description><![CDATA[<P>救命啊，好累啊</P>
<P>运动过量，快死了</P>]]></description>
</item><item>
<title><![CDATA[能吃能睡，能拉屎就是最大的幸福]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4513</link>
<author>tonny1982</author>
<pubDate>2005/4/4 22:40:34</pubDate>
<description><![CDATA[<P>"能吃能睡，能拉屎就是最大的幸福"看似颓废，看似俗，但仔细想想确实对人生的一大感悟</P>
<P>&nbsp;</P>]]></description>
</item><item>
<title><![CDATA[PHPZip[zz]]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4474</link>
<author>tonny1982</author>
<pubDate>2005/4/4 12:44:23</pubDate>
<description><![CDATA[<BR>刚在网上找的一个类。<A href="http://www.pconline.com.cn/pcedu/empolder/wz/php/0501/acc/25phpzip.inc.zip">点击下载类文件(zip压缩文件，2.25K)</A><BR><BR>$z = new PHPZip(); //新建立一个zip的类<BR><BR>　　方法一：<BR><BR>$z -&gt; Zip("", "out1.zip"); //添加当前目录和子目录下的所有档案<BR><BR><BR>　　方法二：<BR><BR>$files=array('1.txt','gb.txt');<BR>$files[]='5.txt';<BR>$z -&gt; Zip($files, "out2.zip"); //添加文件列表<BR><BR><BR>　　方法三：<BR><BR>$z -&gt; Zip("/usr/local/sext/", "out3.zip"); //添加指定目录]]></description>
</item><item>
<title><![CDATA[搞笑小小图片（吃大便）]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4441</link>
<author>tonny1982</author>
<pubDate>2005/4/3 14:54:31</pubDate>
<description><![CDATA[QQ上出现一张很搞笑的图片，共享共享：<IMG style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" src="http://blogger.org.cn/blog/uploadfile/200543145642228.GIF" border=0>]]></description>
</item><item>
<title><![CDATA[怎么样的生活算小资]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4433</link>
<author>tonny1982</author>
<pubDate>2005/4/3 14:17:36</pubDate>
<description><![CDATA[<P>小资一样的生活</P>
<P>今天去新概念理发，觉得那边的按摩还算不错，所以办了一个消费卡，以后可以经常去去。回到宿舍的时候心里突然冒出一个词：小资</P>
<P>于是问在边上的郭西部(因为此人马上要赶赴西部），他的解释我没听明白。</P>
<P>其实在我看来，人活一辈子快乐就可以了，小资其实是那种快乐而不颓废的生活方式<IMG src="http://blogger.org.cn/blog/images/emot/face31.gif"></P>]]></description>
</item><item>
<title><![CDATA[学习工作，生活]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4372</link>
<author>tonny1982</author>
<pubDate>2005/4/2 10:39:17</pubDate>
<description><![CDATA[<P>【学习工作】懒惰了好几天，今天开始好好工作，希望在下个礼拜中把接的project全部收尾，也算是了却一笔心事，然后再去关心我另一个心事：可恶的毕业论文，一点头绪都没有<IMG src="http://blogger.org.cn/blog/images/emot/face12.gif"></P>
<P>&nbsp;&nbsp;&nbsp; 【生活】全身酸痛，但是感觉不错，今天去理发然后按摩一下：）爽就一个字<IMG src="http://blogger.org.cn/blog/images/emot/face24.gif"></P>]]></description>
</item><item>
<title><![CDATA[电商传奇完成部分（作者不是tonny1982）]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4266</link>
<author>tonny1982</author>
<pubDate>2005/3/30 13:35:40</pubDate>
<description><![CDATA[<P>如果有版权和隐私问题即使和我联系</P>
<P>未经同意不得转载<IMG src="http://blogger.org.cn/blog/images/emot/face12.gif"></P>
<P><IMG src="http://blogger.org.cn/blog/images/file/zip.gif" border=0><A href="http://blogger.org.cn/blog/uploadfile/2005330134420262.RAR" target=_blank>电商传奇.rar</A></P>
<P>&nbsp;</P>]]></description>
</item><item>
<title><![CDATA[Java Web Framework综述 [zz]]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4265</link>
<author>tonny1982</author>
<pubDate>2005/3/30 13:32:30</pubDate>
<description><![CDATA[<P>&nbsp;</P>
<P>0.简介<BR>本文介绍Java Web Framework的基本工作原理，和一些常用的开源Web MVC Framework(Struts, Web Work, Tapestry, Echo, JSF, Maverick, Spring MVC, Turbine, Cocoon, Barracuda)。</P>
<P>Web开发的最重要的基本功是HTTP；Java Web开发的最重要的基本功是Servlet Specification。HTTP和Servlet Specification对于Web Server和Web Framework的开发实现来说，是至关重要的协议规范。</P>
<P>应用和剖析开源Web Framework，既有助于深入掌握HTTP &amp; Servlet Specification, 也有助于了解一些现代的B/S Web框架设计思想，如MVC，事件处理机制，页面组件，IoC，AOP等。在这个现代化的大潮中，即使Servlet规范本身也不能免俗，不断引入Filter、Listener等现代框架设计模式。同是Sun公司出品的JSF更是如此。</P>
<P>关于MVC模型、项目简介、配置文件、入门示例等基础知识，网上已经有大量的重复资料信息，本文不再赘述。</P>
<P>文中会提到一些相关的开源项目，和一些编程思想，如有需要，可以用相关的关键字在网上搜索，获取基本的背景知识。</P>
<P>本文力图言简意赅，突出重点。着重描述其他资料没有提到、或很少提到的较重要内容，如运行原理、主流用法，相关知识，关键特性等。</P>
<P>1. Java Web程序工作原理<BR>Tomcat的Server.xml文件中定义了网络请求路径到主机本地文件路径的映射。比如，&lt;context path="/yourapp" docBase="yourapp_dir/webapp"/&gt;</P>
<P>&nbsp;</P>
<P>我们来看一下，一个HTTP Request-Response Cycle的处理过程。</P>
<P>HTTP Request URL一般分为三段：host, context, path。</P>
<P>如<A href="http://yourhost/yourapp/en/index.html">http://yourhost/yourapp/en/index.html</A>这个URL，分为host=yourhost, context=yourapp, path=en/index.html三段。其中，Context部分由request.getContext()获得，path部分由request.getServletPath()获得（返回结果是“/en/index.html”）。</P>
<P>yourhost主机上运行的Tomcat Web Server接收到这个URL，根据Context定义，把yourapp这个网络路径映射为yourapp_dir/webapp，并在此目录下定位en/index.html这个文件，返回到客户端。</P>
<P>&nbsp;</P>
<P>如果我们这个URL更换为<A href="http://yourhost/yourapp/en/index.jsp">http://yourhost/yourapp/en/index.jsp</A>，这个时候Tomcat会试图把yourapp_dir/webapp/en/index.jsp文件编译成Servlet，并调用运行这个Servlet。</P>
<P>我们再把这个URL更换为<A href="http://yourhost/yourapp/en/index.do">http://yourhost/yourapp/en/index.do</A>。</P>
<P>注意，戏剧化的事情就发生在这个时候，Servlet规范中最重要的类RequestDispatcher登场了。RequestDispatcher根据WEB-INF/web.xml配置文件的定义，调用对应的Servlet来处理en/index.do这个路径。</P>
<P>假设web.xml里面有这样的定义。</P>
<P>&nbsp; &lt;servlet&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;servlet-name&gt;DispatchServlet&lt;/servlet-name&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;servlet-class&gt;yourapp.DispatchServlet&lt;/servlet-class&gt;</P>
<P>&nbsp; &lt;/servlet&gt;</P>
<P>&nbsp; &lt;servlet-mapping&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;servlet-name&gt;DispatchServlet&lt;/servlet-name&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;url-pattern&gt;*.do&lt;/url-pattern&gt;</P>
<P>&nbsp; &lt;/servlet-mapping&gt;</P>
<P>那么，RequestDispatcher会调用yourapp.DispatchServlet类处理这个路径。</P>
<P>如果web.xml没有定义对应en/index.do这个路径的Servlet，那么Tomcat返回“您请求的资源不存在”。</P>
<P>RequestDispatcher用于Web Server中，也可以用于应用程序中进行处理转向，资源定位。比如，我们在处理en/index.do的代码中调用，</P>
<P>request.getRequestDispatcher(“cn/index.jsp”).forward(request, response), 就可以转交另外的资源cn/index.jsp来处理。</P>
<P>&nbsp;</P>
<P>几乎所有的Web Framework都需要定义自己的Dispatch作用的Servlet，并调用RequestDispatcher进行转向处理。</P>
<P>阅读Web Framework源代码，有两条主要线索，(1)根据web.xml找到对应的Servlet类；(2)搜索包含“RequestDispatcher”词的代码文件。</P>
<P>&nbsp;</P>
<P>我们看到，request, response&nbsp; 这两个参数，被RequestDispatcher在各种Servlet之间传来传去（JSP也是Servlet）。所以，request的setAttribute()和getAttribute()方法是Servlet之间传送数据的主要方式。</P>
<P>在MVC结构中，一般的处理流程如下：</P>
<P>处理HTTP Request的基本单位一般称为Action，是一个比Servlet轻量得多的接口定义，通常只有一两个方法，如execute(perform), validate等。</P>
<P>我们知道，URL-&gt;Servlet映射，定义在Web.xml配置文件里，但MVC框架通常会有另外一个定义URL-&gt; Action映射的配置文件。</P>
<P>入口Dispatcher Servlet根据URL -&gt; Action的映射关系，把请求转发给Action。</P>
<P>Action获得输入参数，调用商业逻辑，并把结果数据和View标识给（Model &amp; View）返回给Dispatcher Servlet。</P>
<P>Dispatcher Servlet根据这个View 标识，定位相应的View Template Path，把处理转交给View（JSP +TagLib, Velocity, Free Marker, XSL等）。</P>
<P>View一般通过request.getAttribute()获得结果数据，并显示到客户端。至于是谁把结果数据设置到request.attribute里面，有两种可能：Action或Dispatcher Servlet。</P>
<P>2. Struts<BR><A href="http://struts.apache.org/">http://struts.apache.org/</A></P>
<P>Struts是目前用户群最大、开发厂商支持最多的开源Web Framework。</P>
<P>Struts劳苦功高，为普及MVC框架作出了不可磨灭的贡献。显赫的声望，趋于老化的厚重结构，令Struts成为很多现代Web Framework参照、挑战的目标。</P>
<P>&nbsp;</P>
<P>Struts应用主要包括3件事情: 配置struts-config.xml文件,实现Action类，实现View；还有一些高级扩展用法。下面分别讲述。</P>
<P>&nbsp;</P>
<P>1. 配置struts-config.xml文件：</P>
<P>Struts支持多级配置文件，具体用法和限制，详见Struts文档。这里只讨论struts-config.xml主流配置的内容。:-)</P>
<P>&nbsp;</P>
<P>(1) URL Path到Action的映射。</P>
<P>如&lt;action path="/LogonSubmit" type="app.LogonAction" ... /&gt;</P>
<P>&nbsp;</P>
<P>Struts的入口Servlet是ActionServlet。</P>
<P>ActionServlet需要此信息把URL Path调用对应的Action类处理。</P>
<P>在Struts运行期间，一个URL Path，只存在一个对应的Struts Action实例。所有的该URL Path的请求，都经过这同一个Struts Action实例处理。所以Struts Action必须线程安全。</P>
<P>想想看，其实这个要求并不过分，Action只是一个处理程序，不应该保存跨HTTP请求的状态数据，按理来说，也应该做成线程安全的。</P>
<P>&nbsp;</P>
<P>(2) Template Name到View Template Path的映射。</P>
<P>&lt;forward name="success" path="/pages/Welcome.jsp"/&gt;</P>
<P>&nbsp;</P>
<P>Action类返回一个Template Name，ActionServlet根据这个Template Name获得对应的View Template Path，然后调用</P>
<P>request.getRequestDispatcher(“View Template Path”)，把处理转向路径对应的Servlet。在这个例子中，是转向/pages/Welcome.jsp编译后的Servlet。</P>
<P>&nbsp;</P>
<P>我们来看一个一个Velocity的例子。</P>
<P>&lt;include name="success" path="/pages/Welcome.vm"/&gt;</P>
<P>web.xml的定义如下</P>
<P>&lt;servlet&gt;</P>
<P>&nbsp; &lt;servlet-name&gt;velocity&lt;/servlet-name&gt;</P>
<P>&lt;servlet-class&gt;org.apache.velocity.tools.view.servlet.VelocityViewServlet&lt;/servlet-class&gt;</P>
<P>&lt;/servlet&gt;</P>
<P>&lt;servlet-mapping&gt;</P>
<P>&nbsp; &lt;servlet-name&gt;velocity&lt;/servlet-name&gt;</P>
<P>&nbsp; &lt;url-pattern&gt;*.vm&lt;/url-pattern&gt;</P>
<P>&lt;/servlet-mapping&gt;</P>
<P>&nbsp;</P>
<P>这时，request.getRequestDispatcher(“/pages/Welcome.vm”)会调用VelocityViewServlet，由VelocityViewServlet负责装并驱动运行/pages/Welcome.vm这个模板文件。</P>
<P>这里面有一个问题，如果调用的是DispatchRequester.include()方法，那么如何才能把pages/Welcome.vm传给VelocityViewServlet呢？</P>
<P>如前所说，RequestDispatcher传递的参数只有两个，request和response。那么只能通过request attribute。正是为了解决这个问题，Servlet2.3规范之后，加入了javax.servlet.include.servlet_path这个属性。</P>
<P>参见VelocityViewServlet的代码（velocity-tool开源项目）</P>
<P>// If we get here from RequestDispatcher.include(), getServletPath()</P>
<P>// will return the original (wrong) URI requested.&nbsp; The following special</P>
<P>// attribute holds the correct path.&nbsp; See section 8.3 of the Servlet</P>
<P>// 2.3 specification.</P>
<P>String path = (String)request.getAttribute("javax.servlet.include.servlet_path");</P>
<P>&nbsp;</P>
<P>从这里我们可以看出，为什么通晓Servlet Specification对于通晓Web Framework至关重要。</P>
<P>&nbsp;</P>
<P>(3) Form Bean的定义</P>
<P>如&lt;form-bean name="logonForm" type="app.LogonForm"/&gt;<BR>Struts Form Bean需要继承ActionForm类。<BR>Form Bean类，主要有三个作用：</P>
<P>[1]根据bean的定义，利用reflection机制，自动把request参数转化为需要的数据类型，填入到bean的属性当中。ActionForm类名中虽然有Form这个词，但不仅能够获取Form提交后的HTTP Post参数，也可以获取URL后缀的HTTP Get参数。</P>
<P>[2]输入验证。用户可以配置validation.xml，定义各属性的验证规则。</P>
<P>[3]当作View Object来用。用户需要熟练掌握Struts HTML TagLib的用法，才能把Form Bean的属性正确显示出来。</P>
<P>&nbsp;</P>
<P>(4)其他定义。详见Struts文档。不再赘述。</P>
<P>&nbsp;</P>
<P>2.实现Action。</P>
<P>Action类从Form Bean或直接从request中获得输入参数，调用商业逻辑，把结果数据（也许会包装成View Object），用request.setAttribute()放到request中，最后返回一个用ForwardMapping类包装的Template Name。</P>
<P>&nbsp;</P>
<P>3.实现View。</P>
<P>Struts View的标准实现方法是JSP + Struts TagLib，其中最重要的就是Struts HTML TagLib。</P>
<P>html:form tag则是整个HTML Tag的核心，其它的如html:input, html:select等tag，都包含在html:form tag里面。</P>
<P>html:form tag用来映射Form Bean（也可以通过适当定义，映射其他的bean，但使用上会有很多麻烦）。html:form tag包含的其他Struts html tag用来映射Form Bean的属性。</P>
<P>&nbsp;</P>
<P>Struts Bean TagLib的用法比较臃肿，一般情况下可以用JSTL代替。当然，如果需要用到bean:message tag实现国际化，那又另当别论。</P>
<P>Struts Tile TagLib用于页面布局。开源Portal项目Liferay使用了Struts Tile TagLib做为布局控制。</P>
<P>&nbsp;</P>
<P>4.高级扩展用法</P>
<P>用户可以重载Struts的一些控制类，引入自己的一些定制类。详见Struts文档。</P>
<P>本文不是Struts专题，只讲述最重要的主流用法，其它边边角角的，不再赘述。</P>
<P>3. WebWork<BR><A href="http://www.opensymphony.com/webwork/">http://www.opensymphony.com/webwork/</A></P>
<P>WebWork由于灵活的可插拔特性，受到很多资深程序员的欢迎。似乎很有可能大肆流行起来。</P>
<P>WebWork项目建立在XWork项目上。入口Servlet是WebWork项目中定义的ServletDispatcher，而Action在XWork项目中定义。</P>
<P>XWork Action接口的execute()方法没有参数，不像Struts Action那样接受request, response参数，所以XWork Action能够脱离Web环境被直接调用，便于单元测试。</P>
<P>这里引入了一个问题。没有了request参数，那么XWork Action如何获得request parameters作为输入数据？又通过什么桥梁（Struts用request.setAttribute）把结果数据传送到View层？</P>
<P>在Web Work中，只能通过Action本身的getter, setter属性来传送输入参数和输出结果。</P>
<P>比如，我们有这样一个实现了XWork Action接口的类，</P>
<P>YourAction implements Action{</P>
<P>&nbsp; int productId = null;</P>
<P>&nbsp; String productName = null;</P>
<P>&nbsp;</P>
<P>&nbsp; public void setProductId(int productId){this.productId = productId;}</P>
<P>&nbsp; public String getProductName(){return productName;}</P>
<P>&nbsp;</P>
<P>&nbsp; public String execute(){</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; productName = findNameById(productId);</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return “success”;</P>
<P>&nbsp; }</P>
<P>}</P>
<P>这个类里面的productId将接受request输入参数，productName是输出到页面显示的结果。</P>
<P>比如，这样的请求，<A href="http://yourhost/yourapp/MyAction.action?productId=1">http://yourhost/yourapp/MyAction.action?productId=1</A> </P>
<P>Web Work会把1填到YourAction的productId里面，然后执行execute()方法，JSP里的语句&lt;ww:property value=“productName”&gt;会把YourAction的productName显示在页面上。</P>
<P>&nbsp;</P>
<P>如果一个Web Framework采用了这种屏蔽Action的request, response参数的设计方式，一般也同时会采用这种Action和输入输出数据结合成一体的解决方式。类似的情形也存在于Tapestry和Maverick中，后面会讲到。</P>
<P>当WebWork ServletDispatcher接收到HTTP Request的时候，首先把所有相关的信息（包括request, response, session, servlet config, servelt context, 所有request参数）等存放到AcationContext中，然后根据Interceptor配置信息，生成一个YourAction的动态代理类对象。实际上运行的正是这个代理对象，如同Servlet Filter的工作机制一般，所有注入的Interceptor方法会先于Actio方法运行。</P>
<P>我们来看一下Action和Interceptor的地位：Action没有参数，无法获得ActionContext；而Interceptor接受的ActionInvoication参数拥有包括ActionContext在内的所有重要信息。</P>
<P>这种权力分配的不平等，注定了Action的作用非常有限，只限于调用商业逻辑，然后返回一个成功与否标志。所有与外部Web世界打交道、协调内部工作流程的重担，都责无旁贷地落在Interceptor的肩上。</P>
<P>我们可以设想一个极端的例子。我们声明一批不做任何事情的空Action，我们只是需要它们的空壳类名；我们制作一批对应的Interceptor，所有的转发控制、商业逻辑都在Interceptor上实现，然后把Interceptor都注入到对应的空Action。这在理论上是完全可行的。</P>
<P>在Web海洋的包围中，Action可少，Interceptor不可少。Action是一个孤岛,如果没有外来盟友Interceptor的协助，只能在自己的小范围内独立作战（比如Unit Test），而对整体大局的作战目标无法产生影响。</P>
<P>下面我们来看一下Action是如何在Interceptor的全程监管下工作的。</P>
<P>&nbsp;</P>
<P>在WebWork中，我们需要如下配置XWork.xml。</P>
<P>&lt;xwork&gt;</P>
<P>&lt;!-- Include webwork defaults (from WebWork-2.1 JAR). --&gt;</P>
<P>&lt;include file="webwork-default.xml" /&gt;</P>
<P>&nbsp;</P>
<P>&lt;!-- Configuration for the default package. --&gt;</P>
<P>&lt;package name="default" extends="webwork-default"&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;!-- Default interceptor stack. --&gt; </P>
<P>&nbsp;&nbsp;&nbsp; &lt;default-interceptor-ref name=" defaultStack" /&gt; </P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;!-- Action: YourAction. --&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;action name="youraction" class="yourapp.YourAction"&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;result name="success" type="dispatcher"&gt;</P>
<P>YourAction.jsp</P>
<P>&lt;/result&gt;</P>
<P>&lt;/action&gt;</P>
<P>&lt;/package&gt;</P>
<P>&lt;/xwork&gt;</P>
<P>&nbsp;</P>
<P>webwork-default.xml里面的相关定义如下：</P>
<P>&lt;interceptors&gt;</P>
<P>&lt;interceptor name="validation" class="com.opensymphony.xwork.validator.ValidationInterceptor"/&gt;</P>
<P>&nbsp;</P>
<P>&lt;interceptor name="static-params" class="com.opensymphony.xwork.interceptor.</P>
<P><BR>StaticParametersInterceptor"/&gt;</P>
<P>&lt;interceptor name="params" class="com.opensymphony.xwork.interceptor.ParametersInterceptor</P>
<P>"/&gt;</P>
<P>&lt;interceptor name="conversionError" class="com.opensymphony.webwork.interceptor.</P>
<P><BR>WebWorkConversionErrorInterceptor"/&gt;</P>
<P>&lt;interceptor-stack name="defaultStack"&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;interceptor-ref name="static-params"/&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;interceptor-ref name="params"/&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;interceptor-ref name="conversionError"/&gt;</P>
<P>&lt;/interceptor-stack&gt;</P>
<P>&lt;/interceptors&gt;</P>
<P>&nbsp;</P>
<P>从上述的配置信息中可以看出，YourAction执行execute()方法的前后，会被</P>
<P>defaultStack所定义的三个Intercepter截获。这些Interceptor的任务之一就是把输入参数设置到Action的对应属性当中。</P>
<P>如果我们需要加入对YourAction的属性的验证功能，只要把上述定义中的validation Interceptor加入到defaultStack中就可以了。当然，实际工作还没有这么简单，一般来说，还要为每个进行属性验证的Action的都配置一份validation.xml。</P>
<P>XWork Interceptor能够在Package和Action级别上，进行截获处理。</P>
<P>Servlet Filter能够在URL Patten级别上，进行截获处理。虽然实际上，Servlet Filter截获的是Servlet，但某些情况下，可以达到和截获一批Action的同样效果。</P>
<P>比如，在Web Work中，我们可以为所有admin package的Action，加入一个Interceptor，当检查到当前Session的用户没有admin权限时，统一返回一个警告页面：您没有足够的权限执行这个操作。</P>
<P>我们看到也可以为所有URL Pattern为“admin/*.action”的URL定义一个Servlet Filter，当检查到当前Session的用户没有admin权限时，统一返回一个警告页面：您没有足够的权限执行这个操作。</P>
<P>&nbsp;</P>
<P>WebWork的Interceptor配置是相当灵活的，相当于对Action实现了AOP。Interceptor相当于Aspect，基类AroundInterceptor的before(), after()方法相当于Advice。</P>
<P>另外，XWork也提供了从XML配置文件装配Component的机制，相当于实现了对于Component的IoC。</P>
<P>提到AOP和IoC，顺便多讲两句。Spring AOP能够截获所有Interface，不限于某个特定接口；Spring框架支持所有类型的IoC，不限于某种特定类型。</P>
<P>&nbsp;</P>
<P>要知道，AOP, IoC可是现在最时髦的东西，一定不要错过啊。:D</P>
<P>相关概念导读（如果需要，请用如下关键字搜索网络）：</P>
<P>AOP -- Aspect Oriented Programming -- 面向方面编程。</P>
<P>IoC – Inversion of Control --控制反转</P>
<P>Dynamic Proxy -- 动态代理，JDK1.4引入的特性。还可以进一步参考CGLib, ASM等开源项目。</P>
<P>&nbsp;</P>
<P>WebWork直接支持所有主流View -- XSL,Velocity, FreeMarker,JSP。WebWork还提供了自己的TagLib。“直接支持”的意思是说，不用像Struts那样，使用Velocity的时候，还需要引入辅助桥梁Velocity-tool。</P>
<P>WebWork中用到一种功能和XPath类似的对象寻径语言ONGL，是一个开源项目。ONGL同样用在下面要介绍的Tapestry项目中。</P>
<P>Opensymphony下还有一个SiteMesh项目，通过Servlet Filter机制控制布局。可以和WebWork组合使用。</P>
<P>&nbsp;</P>
<P>4. Tapestry<BR><A href="http://jakarta.apache.org/tapestry/">http://jakarta.apache.org/tapestry/</A></P>
<P>Tapestry近来突然火了起来，令我感到吃惊。也许是JSF带来的Page Component风潮令人们开始关注和追逐Tapestry。</P>
<P>Tapestry的重要思想之一就是Page Component。</P>
<P>前面讲到，XWork能够自动把request参数映射到Action的属性当中。Tapestry走得更远，甚至能够根据request参数，映射到Action（Tapestry里面称为Page）的方法，并把request参数映射为Page方法需要的参数，进行正确的调用。就这样，Tapestry不仅把输入输出数据，而且把事件方法也绑定到了Page上面。</P>
<P>在Tapestry框架中，Action的概念已经非常模糊，而换成了Page的概念。而Tapestry Page是拥有属性和事件的页面组件，其中的事件处理部相当于Action的职责，而属性部分起着Model的作用。</P>
<P>除了使用Page和其它的Tapestry页面组件，用户也可以自定义页面组件。</P>
<P>&nbsp;</P>
<P>这种页面组件/属性事件的编程模型，受到一些程序员的欢迎。当然，这种编程模型并不是没有代价的，每个Tapestry模板文件都需要一个对应的.page文件。这些.page文件定义了页面组件的属性、事件、Validator等信息。</P>
<P>&nbsp;</P>
<P>我们来看一下B/S结构中，组件的属性、事件和HTTP Request绑定的基本原理。一个能够发出请求的页面组件（比如Link和Button），在输出自己的HTML的时候，需要输出一些特殊的信息来标志本组件的属性/事件，这样下次HTTP Request来的时候，会把这些信息带回来，以便Web Framework加以辨认识别，发给正确的Page Component处理。</P>
<P>这些特殊信息通常包含在URL参数或Hidden Input里面，必要的时候，还需要生成一些Java Script。Tapestry，Echo，JSF都是这种原理。</P>
<P>Tapestry的例子如下：</P>
<P>&lt;a href="#" jwcid="@DirectLink" parameters="ognl:currentItem.itemId" listener="ognl:listeners.showItem"&gt;</P>
<P>JSF用TagLib实现页面组件，也提供了类似的CommandLink和CommandButton Tag。其中对应Tapestry listener的Tag属性是action。后面会讲解。</P>
<P>&nbsp;</P>
<P>Tapestry的模板标签是HTML标签的扩展，具有良好的“所见即所得”特性，能够直接在浏览器中正确显示，这也是Tapestry的一个亮点。</P>
<P>5. Echo<BR><A href="http://sourceforge.net/projects/echo">http://sourceforge.net/projects/echo</A></P>
<P>Echo提供了一套类似Swing的页面组件，直接生成HTML。</P>
<P>从程序员的角度看来，用Echo编写Web程序，和用Swing编写Applet一样，属于纯面向组件事件编程，编程模型也以Event/Listener结构为主体。</P>
<P>Echo没有Dispatcher Servlet，也没有定义URL-&gt;Action映射的配置文件。</P>
<P>Echo的Action就是实现了ActionListener接口（参数为ActionEvent）的Servlet（继承EchoServer类）。</P>
<P>所以，Echo直接由Web Server根据web.xml配置的URL -&gt; Servlet的映射，进行转发控制。</P>
<P>&nbsp;</P>
<P>Echo也没有明显的View层，Echo在页面组件方面走得更远，所有的HTML和JavaScript都由框架生成。你不必（也没有办法）写HTML，只需要（也只能）在Java代码中按照类似Swing编程方式，生成或操作用户界面。用户也可以定制自己的Echo组件。</P>
<P>Echo的UI Component的实现，采用了两个重要的模式。一个是Peer（Component -&gt; ComponentPeer）模式，一个是UI Component -&gt; Renderer模式。</P>
<P>虽然Echo的API更类似于Swing，但实现上却采用更接近于AWT的Peer模式。每个Component类（代表抽象的组件，比如Button），都有一个对应的ComponentPeer类（代表实际的组件，比如windows桌面的Button，Linux桌面的Button，HTML Button等）。</P>
<P>先别急，这个事情还没有完。虽然ComponentPeer落实到了具体的界面控件，但是它还是舍不得显示自己，进一步把显示工作交给一个Renderer来执行。</P>
<P>比如，在Echo里面，Button类对应一个ButtonUI（继承了ComponentPeer）类，而这个ButtonUI类会把最终显示交给ButtonRender来处理。</P>
<P>据说多了这么一步，能够让显示控制更加灵活丰富。比如，同一个Renderer可以处理不同的UI Component，同一个UI Component也可以交给不同的Renderer处理。</P>
<P>JSF的页面组件也采用了UI Component -&gt; Renderer模式，后面会讲到。</P>
<P>6. JSF<BR><A href="http://java.sun.com/j2ee/javaserverfaces/index.jsp">http://java.sun.com/j2ee/javaserverfaces/index.jsp</A></P>
<P><A href="http://wwws.sun.com/software/communitysource/jsf/download.html">http://wwws.sun.com/software/communitysource/jsf/download.html</A> download source</P>
<P>&nbsp;</P>
<P>JSF的中心思想也是页面组件/属性事件。一般来说，JSF的页面组件是一个三件套{ UI Component, Tag, Renderer}。</P>
<P>UI Component有可能对应Model，Event，Listener。Tag包含componentType和rendererType两个属性，用来选择对应的的UI Component和Renderer。</P>
<P>JSF的应用核心无疑是JSF TagLib。JSF TagLib包含了对应所有重要HTML元素的Tag，而且Input Tag可以直接包含Validator Tag或者Validator属性，来定义验证手段。</P>
<P>&nbsp;</P>
<P>我们通过JSF携带的cardemo例子，来看JSF的处理流程。</P>
<P>(1) carDetail.jsp有如下内容：</P>
<P>&lt;h:commandButton action="#{carstore.buyCurrentCar}" value="#{bundle.buy}" /&gt;</P>
<P>可以看到，这个button的submit action和carstore.buyCurrentCar方法绑定在一起。我们在Tapestry里面曾经看到过类似的情景。</P>
<P>&nbsp;</P>
<P>(2) carstore在faces-config.cml中定义：</P>
<P>&nbsp; &lt;managed-bean&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;managed-bean-name&gt; carstore &lt;/managed-bean-name&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;managed-bean-class&gt; carstore.CarStore &lt;/managed-bean-class&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;managed-bean-scope&gt; session &lt;/managed-bean-scope&gt;</P>
<P>&nbsp; &lt;/managed-bean&gt;</P>
<P>&nbsp;</P>
<P>(3) carstore.CarStore类中的buyCurrentCar方法如下：</P>
<P>&nbsp;&nbsp;&nbsp; public String buyCurrentCar() {</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getCurrentModel().getCurrentPrice();</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "confirmChoices";</P>
<P>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;</P>
<P>(4) confirmChoices转向在faces-config.cml中定义：</P>
<P>&nbsp; &lt;navigation-rule&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;from-view-id&gt;/carDetail.jsp&lt;/from-view-id&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;navigation-case&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;description&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Any action that returns "confirmChoices" on carDetail.jsp should</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cause navigation to confirmChoices.jsp</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/description&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;from-outcome&gt;confirmChoices&lt;/from-outcome&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;to-view-id&gt;/confirmChoices.jsp&lt;/to-view-id&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;/navigation-case&gt;</P>
<P>&nbsp; &lt;/navigation-rule&gt;</P>
<P>&nbsp;</P>
<P>(5)于是转到页面confirmChoices.jsp。</P>
<P>&nbsp;</P>
<P>除了Interceptor之外，JSF几乎包含了现代Web Framework应该具备的所有特性：页面组件，属性事件，IoC (ManagedBean)，Component -&gt; Renderer，类似于Swing Component的Model-Event-Listener。</P>
<P>也许设计者认为，众多庞杂的模式能够保证JSF成为一个成功的框架。Portal开源项目eXo就是建立在JSF框架上。</P>
<P>&nbsp;</P>
<P>可以看出这样一个趋势，现代Web Framework认为B/S结构的无状态特性和HTML界面是对编程来说是需要极力掩盖的一个缺陷，所以尽量模拟C/S结构的组件和事件机制，以吸引更多的程序员。</P>
<P>7. Maverick<BR><A href="http://mav.sourceforge.net/">http://mav.sourceforge.net/</A></P>
<P>Maverick是一个轻量而完备的MVC Model 2框架。Maverick的Action不叫Action，直截了当的称作Controller。</P>
<P>Controller只接受一个ControllerContext参数。request，response, servlet config, servelt context等输入信息都包装在ControllerContext里面，而且Model也通过ControllerContext的model属性返回。整个编程结构清晰而明快，令人赞赏。</P>
<P>但这个世界上难有十全十美的事情，由于ControllerContext只有一个model属性可以传递数据，程序员必须把所有需要的数据都打包在一个对象里面设置到model属性里。这种麻烦自然而然会导致这样的可能用法，直接把Controller本身设置为model，这又回到了Controller(Action)和Model一体的老路。</P>
<P>&nbsp;</P>
<P>前面讲到，WebWork也把所有的输入信息都包装在ActionContext里面，但Action并没有权力获取。而在Maverick中，Controller对于ControllerContext拥有全权的控制，两者地位不可同日而语。当然，由于参数ControllerContext包含request，reponse之类信息，这也意味着，Maverick Controller不能像WebWork Action那样脱离Web环境独立运行。</P>
<P>当然，这也并不意味着任何结构性缺陷。程序的结构由你自己控制，你完全可以把需要Unit Test的那部分从Web环境脱离开来，放到Business层。</P>
<P>如同WebWork，Maverick直接支持所有的主流View。Maverick的配置文件采Struts, Cocoon两家之长，URL -&gt; Action -&gt; View映射的主体结构类似于Struts，而View定义部分对Transform的支持则类似于Cocoon。如：</P>
<P>&lt;command name="friends"&gt;</P>
<P>&lt;controller class="org.infohazard.friendbook.ctl.Friends"/&gt;</P>
<P>&lt;view name="success" path="friends.jsp"&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;transform path="trimInside.jsp"/&gt;</P>
<P>&lt;/view&gt;</P>
<P>&lt;/command&gt;</P>
<P>8. Spring MVC<BR><A href="http://www.springframework.com/">http://www.springframework.com/</A></P>
<P>Spring MVC是我见过的结构最清晰的MVC Model 2实现。</P>
<P>Action不叫Action，准确地称做Controller；Controller接收request, response参数，干脆利落地返回ModelAndView（其中的Model不是Object类型，而是Map类型）。</P>
<P>其它的Web Framework中， Action返回值一般都只是一个View Name；Model则需要通过其它的途径（如request.attribute，Context参数，或Action本身的属性数据）传递上去。</P>
<P>&nbsp;</P>
<P>Spring以一招IoC名满天下，其AOP也方兴未艾。“Spring出品，必属精品”的观念已经深入人心。我这里多说也无益，强烈建议读者去阅读Spring Doc &amp; Sample &amp; Code本身。</P>
<P>9. Turbine<BR><A href="http://jakarta.apache.org/turbine/">http://jakarta.apache.org/turbine/</A></P>
<P>Turbine是一个提供了完善权限控制的坚实框架（Fulcrum子项目是其基石）。Turbine的个人用户不多，但不少公司用户选择Turbine作为框架，开发一些严肃的应用（我并没有说，用其它框架开发的应用就不严肃^_^）。Portal开源项目JetSpeed建立在Turbine上。</P>
<P>Turbine用RunData来传递输入输出数据。如同Maverick的ControllerContext，RunData是整个Turbine框架的数据交换中心。除了request, response等基本信息，RunData直接包括了User/ACL等权限控制相关的属性和方法，另外还包括Action Name和Target Template Name等定位属性。</P>
<P>Module是Turbine里面除了RunData之外的又一个核心类，是Turbine框架的基本构件，Action是Module，Screen也是Module。Turbine提供了LoginUser和LogoutUser两个Action作为整个系统的出入口。而其余流量的权限控制则由类似于Servlet Filter机制的Pipeline控制。</P>
<P>Turbine Pipeline的编程模型和Servlet Filter一模一样：Turbine Pipeline的Valve就相当于Servlet Filter，而ValveContext则相当于Filter Chain。还有更相近的例子，Tomcat源代码里面也有Valve和ValueContext两个类，不仅编程模型一样，而且名字也一样。</P>
<P>&nbsp;</P>
<P>权限控制贯穿于Turbine框架的始终。要用好Turbine，首先要通晓子项目Fulcrum 的Security部分的权限实现模型。</P>
<P>Fulcrum Security的权限实体包括四个-- User, Group, Role, Permission。</P>
<P>实体之间包含{Role，Permission}和{ Group, User, Role}两组关系。</P>
<P>{Role，Permission}是多对多的关系，一个Role可以具有各种Permission；{ Group, User, Role}之间是多对多的关系，一个Group可包含多个User，并可以给User分配不同的Role。</P>
<P>权限模型的实现同样采用Peer模式，Entity -&gt; EntityPeer, Entity -&gt; ManagerPeer。</P>
<P>Entity和EntityManger代表抽象的模型概念，而EntityPeer和ManagerPeer代表具体的实现。</P>
<P>用户可以根据模型，提供不同的实现，比如，用内存结构中实现，用数据表结构实现，与Windows NT权限验证机制结合，与OSWorkflow的权限控制模型结合，等等。其中，用数据表结构实现，又可以选择用Torque实现，或者用Hibernate实现。（Torque是Turbine的O/R Mapping子项目）</P>
<P>&nbsp;</P>
<P>例如，Falcrum.property配置文件包含如下Security相关选项：</P>
<P># -------------------------------------------------------------------</P>
<P>#&nbsp; S E C U R I T Y&nbsp; S E R V I C E</P>
<P># -------------------------------------------------------------------</P>
<P>services.SecurityService.user.class=org.apache.fulcrum.security.impl.db.entity.TurbineUser</P>
<P>services.SecurityService.user.manager=org.apache.fulcrum.security.impl.db.DBUserManager</P>
<P>services.SecurityService.secure.passwords.algorithm=SHA</P>
<P># -------------------------------------------------------------------</P>
<P>#&nbsp; D A T A B A S E&nbsp; S E R V I C E</P>
<P># -------------------------------------------------------------------</P>
<P>services.DatabaseService.database.newapp.driver=org.gjt.mm.mysql.Driver</P>
<P>services.DatabaseService.database.newapp.url=jdbc:mysql://127.0.0.1/newapp</P>
<P>services.DatabaseService.database.newapp.username=turbine</P>
<P>services.DatabaseService.database.newapp.password=turbine</P>
<P>&nbsp;</P>
<P>这说明，权限控制实现由数据库提供，需要根据权限模型创建如下数据表：</P>
<P>TURBINE_USER，TURBINE_ROLE，TURBINE_GROUP，</P>
<P>TURBINE_PERMISSION，TURBINE_ROLE_PERMISSION，</P>
<P>TURBINE_USER_GROUP_ROLE。</P>
<P>&nbsp;</P>
<P>10. Cocoon<BR><A href="http://cocoon.apache.org">http://cocoon.apache.org</A></P>
<P>Cocoon项目是一个叫好不叫做的框架。采用XML + XSLT Pipeline机制，Java程序只需要输出XML数据，Cocoon框架调用XSL文件把XML数据转换成HTML、WML等文件。</P>
<P>Cocoon强大灵活的XSL Pipeline配置功能，XSLT的内容/显示分离的承诺，一直吸引了不少程序员fans。怎奈天不从人愿，由于复杂度、速度瓶颈、XSL学习难度等问题的限制，Cocoon一直主要限于网站发布出版领域，向CMS和Portal方向不断发展。另外，Cocoon开发了XSP脚本和Cocoon Form技术。</P>
<P>Cocoon的sitemap.xmap配置文件比较复杂，与其它的Web Framework差别很大。</P>
<P>主体Pipelines配置部分采用Pattern Match的方式，很像XSL语法，也可以类比于Web.xml里面Servlet Mapping的定义。比如，一个典型的URL-&gt;Action的映射定义看起来是这个样子：</P>
<P>&lt;map:pipelines&gt;</P>
<P>&lt;map:pipeline&gt;</P>
<P>&lt;map:match pattern="*-dept.html"&gt;</P>
<P>&nbsp; &lt;map:act set="process"&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;map:parameter name="descriptor"</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value="context://docs/department-form.xml"/&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;map:parameter name="form-descriptor"</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value="context://docs/department-form.xml"/&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;map:generate type="serverpages" src="docs/confirm-dept.xsp"/&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;map:transform src="stylesheets/apache.xsl"/&gt;</P>
<P>&nbsp;&nbsp;&nbsp; &lt;map:serialize/&gt;</P>
<P>&nbsp; &lt;/map:act&gt;</P>
<P>&nbsp; &lt;map:generate type="serverpages" src="docs/{1}-dept.xsp"/&gt;</P>
<P>&nbsp; &lt;map:transform src="stylesheets/apache.xsl"/&gt;</P>
<P>&nbsp; &lt;map:serialize/&gt;</P>
<P>&lt;/map:match&gt;</P>
<P>&lt;/map:pipeline&gt;</P>
<P>&lt;/map:pipelines&gt;</P>
<P>11. Barracuda<BR><A href="http://barracudamvc.org/Barracuda/index.html">http://barracudamvc.org/Barracuda/index.html</A></P>
<P>Barracuda是一个HTML DOM Component + Event/Listener结构的框架。</P>
<P>根据模板文件或配置文件生成静态Java类，并在代码中使用这些生成类，是Barracuda的一大特色。</P>
<P>Barracuda需要用XMLC项目把所有的HTML或WML模板文件，静态编译成DOM结构的Java类，作为页面组件。XMLC会根据HTML元素的id定义，生成相应DOM结点的简便操作方法。</P>
<P>&nbsp;</P>
<P>Barracuda的事件类也需要用Barracuda Event Builder工具把event.xml编译成Java类，引入到工程中。Barracuda直接用Java类的继承关系映射事件之间的父子层次关系。比如，ChildEvent是ParentEvent的子类。</P>
<P>Barracuda的事件分为两类：Request Events（Control Events）和Response Events（View Events）。</P>
<P>&nbsp;</P>
<P>Barracuda事件处理过程很像Windows系统消息队列的处理机制。</P>
<P>(1) Barracuda根据HTTP Request生成Request Event，放入到事件队列中。</P>
<P>(2) EventDispatcher检查事件队列是否为空，如果为空，结束。如果非空，按照先进先出的方式，从事件队列中取出一个事件，根据这个事件的类型，选择并调用最合适的EventListener，参数Event Context包含事件队列。</P>
<P>&nbsp;“根据事件类型，选择最合适的EventListener对象”的过程是这样的：比如，</P>
<P>EventDispatcher从时间队列里取出来一个事件，类型是ChildEvent；Barracuda首先寻找注册了监听ChildEvent的EventListener，如果找不到，再上溯到ChildEvent的父类ParentEvent，看哪些EventListener对ParentEvent感兴趣。</P>
<P>详细过程参见Barracuda的DefaultEventDispatcher类。</P>
<P>(3) EventListener根据Event Context包含的request信息，调用商业逻辑，获得结果数据，然后根据不同情况，把新的事件加入到Event Context的事件队列中。</P>
<P>(4) 控制交还给EventDispatcher，回到第(2)步。</P>
<P>&nbsp;</P>
<P>&nbsp;</P>]]></description>
</item><item>
<title><![CDATA[[转]Struts的几个精细之处]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4257</link>
<author>tonny1982</author>
<pubDate>2005/3/29 22:44:30</pubDate>
<description><![CDATA[
<TABLE style="TABLE-LAYOUT: fixed; WORD-BREAK: break-all" cellSpacing=0 cellPadding=0 width="90%" align=center border=0>
<TBODY>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD>
<DIV align=right><SPAN><A class=categorylink href="http://blogger.org.cn/blog/list.asp?classid=14" target=_blank></A></SPAN>&nbsp;</DIV>
<DIV align=right><SPAN></SPAN>&nbsp;</DIV></TD></TR></TBODY></TABLE><SPAN><BR><SPAN class=myp111><FONT id=zoom>最近在网上看到一篇N. Alex Rupp写的“Beyond MVC: A New Look at the Servlet Infrastructure”文章，意思大致是说MVC被Struts等框架错误地应用到了Servlet架构中。我想只有对Struts有足够的了解再加上在MVC方面有足够深的功力，才敢发此言论，不是经常听人说：最熟悉自己的人是你的敌人。本人功力尚浅，没有引领风潮的能力，而且生活还得继续，只能先来熟悉熟悉Struts。 <BR><BR><B>申明：</B> 强烈建议在阅读本文之前先阅读一下N. Alex Rupp老兄的文章，如果你赞同他的看法，可能你会觉得研究Struts就没什么意义了。 <BR><BR><B>说明：</B>本文所讲的Struts知识基于Struts 1.1版本，除非特别说明，本文中的Struts都特指Struts 1.1这个版本。 <BR><BR><B>目录： <BR><BR><A href="http://tech.ccidnet.com/pub/article/c1078_a82580_p1.html#1">精细之处一：“利用Token解决重复提交”背后的前提</A><BR><A href="http://tech.ccidnet.com/pub/article/c1078_a82580_p1.html#2">精细之处二：页面流转控制中的职责分配</A></B> <BR><BR><A name=1><B>精细之处一：“利用Token解决重复提交”背后的前提</B></A> <BR><BR>我们知道，可以利用同步令牌（Token）机制来解决Web应用中重复提交的问题，Struts也给出了一个参考实现。服务器端在处理到达的请求之前，会将请求中包含的令牌值与保存在当前用户会话中的令牌值进行比较，看是否匹配。在处理完该请求后，且在答复发送给客户端之前，将会产生一个新的令牌，该令牌除传给客户端以外，也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次提交的话，客户端传过来的令牌就和服务器端的令牌不一致，从而有效地防止了重复提交的发生。对应于这段描述，你可能会在你的Action子类中有这么一段代码： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>if (isTokenValid(request, true)) {
 // your code here
return mapping.findForward("success");
} else {
saveToken(request);
return mapping.findForward("submitagain");
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>其中isTokenValid()和saveToken()都是org.apache.struts.action.Action类中的方法，而具体的Token处理逻辑都在org.apache.struts.util.TokenProcessor类中。Struts中是根据用户会话ID和当前系统时间来生成一个唯一（对于每个会话）令牌的，具体实现可以参考TokenProcessor类中的generateToken()方法。 <BR><BR>不知道大家有没有注意到这样一个问题，因为Struts是将Token保存在Session的一个属性中，也就是说对于每个会话服务器端只保存而且只能保存一个最新Token值。对于这一点，我的同事就提出了疑问：那如果我在同一个会话中打开两个页面，那么后提交的那个页面肯定不能提交成功了。他还给出了一个实际的例子：比如现在需要把两个客户A和B的地址都改为某个值，那用户就可能同时打开两个页面，修改A，修改B，提交A，提交B，按照Struts中的处理逻辑，B的修改提交就肯定不能成功，但是这个提交操作对于用户来说并不存在操作不正确的地方。 <BR><BR>在这里，可能有人要问：怎么可能在同一个会话中打开两个页面呢？重新打开一个IE浏览器不是重新开始了一个会话吗？不错，这种情况下是两个会话，不存在任何问题。但是，你还可以通过菜单“文件”－“新建”－“窗口”（或者快捷键Ctrl+N）来复制当前窗口，这个时候你会发现该页面与原有页面同处在一个会话当中。其实，能够发现这个问题得归功于我的那位同事对IE习惯性的操作方法。 <BR><BR>这下我的那位同事不满意啦，他于是开始动手修改Struts中的实现方式，让每个页面（至少某类页面）在服务器端都保存有一个唯一的Token值。这样，前面所讲的客户A，B同时修改的限制就不存在了。但是不久，我的那位同事就开始意识到他正在走向一条危险的道路。首先，如果每个页面都在服务器端保存一个Token值，则服务器端保存的数据量将越来越大。而且，如果考虑这种同一个会话中打开多个页面的情况的话，就好像打开了潘多拉魔盒，将会给自己带来无穷无尽的麻烦。比如，首先打开页面P1，然后利用Ctrl+N得到页面P2，P1提交，P2提交，目前为止一切正常。但是如果此时，在P1，P2中点击“后退”按钮，然后再提交P1， P2呢，情况会是怎样？如果在P2中提交完后执行其它操作，而在P1中回退后提交，情况又是怎么样呢？如果有P1，P2，P3，那情况又是如何呢？太复杂啦！我想你也会和我们有同感，你需要考虑许多种可能的组合，而且有的时候结果并不是你想象中的那样简单。 <BR><BR>此路不通，还得回来看看Struts。其实经过以上一番折腾，我们可以发现在Struts中的Token机制背后隐藏着这样一个前提：不允许你（客户端）在同一会话中打开多个页面。注意是同一会话，如果打开两个IE浏览器，那已经是两个会话啦，不受该限制。其实，这个看似不合理的规定却自有其道理：一是它极大地简化了Token的实现，二个这种限定也符合大部分人的使用习惯。 <BR><BR><A name=2><B>精细之处二：页面流转控制中的职责分配</B></A> <BR><BR>我们知道，Struts的执行过程大致如下：首先，控制器接收到客户端请求，将这些请求映射至相应的Action，并调用Action的execute方法，这中间可能还涉及到ActionForm的创建和填充。Action的execute方法执行完以后，返回一个ActionForward对象，控制器根据该ActionForward对象将请求转发至下一个Action或JSP。最后，产生视图响应客户。在大的层面上，Struts是采用了MVC这种架构，没什么特别之处。但从一些小的地方，我们还是可以看出Craig R. McClanahan老兄的一些考虑。我们看到Action与控制器之间传递的是ActionForward对象，由于Action的execute方法要求返回一个ActionForward对象，所以你会经常在Action子类中看到如下语句： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>return (new ActionForward(mapping.getInput()));</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>或 <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>return (mapping.findForward("success"));</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>其实返回的就是一个ActionForward对象。在Action中我们根据程序执行的不同情况，决定接下来的页面走向（比如返回到输入页面或者转到下一个页面），并将这些信息保存在ActionForward对象中。而接下来控制器就可以直接利用该ActionForward对象来进行页面的流转。下面是org.apache.struts.action.RequestProcessor类的processForwardConfig()方法的摘录，该方法调用发生在Action实例调用后。 <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>protected void processForwardConfig(HttpServletRequest
                                        request,
                                        HttpServletResponse
                                        response,
                                        ForwardConfig forward)
        throws IOException, ServletException {
        …
        
        String forwardPath = forward.getPath();
        String uri = null;
                
             // paths not starting with / 
                 should be passed through without any  processing
            // (ie. they're absolute)
        if (forwardPath.startsWith("/")) {
            uri = RequestUtils.forwardURL(request, forward);
    // get module relative uri
        } else {
            uri = forwardPath;
        }
                if (forward.getRedirect()) {
            // only prepend context path for relative uri
            if (uri.startsWith("/")) {
                uri = request.getContextPath() + uri;
            }
            response.sendRedirect(response.encodeRedirectURL(uri));
                    }
 else {
            doForward(uri, request, response);
        }
    }</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR><B>注意：</B> ForwardConfig是ActionForward的父类 <BR><BR>该方法首先调用ForwardConfig的getPath()方法获得下一步流转的路径，在某些条件下还需要进行一些拼装得到正确的URI，最后根据该URI进行页面跳转。可见在processForwardConfig()方法中只是对ActionForward进行了一些“技术上”的处理，没有任何和业务相关的内容，这样就将控制器（ActionServlet）和Action完全分开来，两者互不影响，达到了功能模块之间松散耦合的目的。 <BR><BR>模块间（系统间）松散耦合一直是OO设计所追求的，但是具体如何去实现这样一种松散耦合却不是那么容易做到的。Struts中的设计给了我们一些启示：模块间相互关联影响因素的传递可以用对象的形式来包装起来。其实，个人觉得Struts中的做法还可以稍微有一点点改进，就是在ActionForward中提供一个getURI()方法来给出最终的URI岂不是更好？ <BR><BR><B>参考：</B> <BR><BR>1、<A href="http://today.java.net/pub/a/today/2003/12/11/mvc.html">Beyond MVC: A New Look at the Servlet Infrastructure</A> <BR><BR>2、<A href="http://www.javaworld.com/javaworld/jw-07-1999/jw-07-toolbox_p.html">Allen Holub的Build user interfaces for object-oriented systems系列文章</A>,可以从这篇文章中学到很多面向对象设计方面的知识，虽然作者并不认为MVC是一种面向对象的方法，但是我们这些MVC的实践者仍然可以从中学到面向对象的知识。 <BR><BR>3、<A href="http://www-900.ibm.com/developerWorks/cn/java/l-struts1-1/">Struts 1.1的介绍性文章：深入Struts 1.1</A> <BR><BR>4、<A href="http://jakarta.apache.org/struts/">Apache Struts Website</A> <BR><BR>5、关于重复提交问题的讨论及其解决方案，可以参考《Core J2EE Patterns》一书（中文版《J2EE核心模式》）。 <BR><BR>Deepak Alur,John Crupi,Dan Malks: Core J2EE Patterns－Best Practices and Design Strategies </FONT></SPAN></SPAN></TD></TR></TBODY></TABLE>]]></description>
</item><item>
<title><![CDATA[寝室夜话]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4239</link>
<author>tonny1982</author>
<pubDate>2005/3/29 12:51:10</pubDate>
<description><![CDATA[<P>每天熄灯后我们宿舍便会开始寝室夜话，话题没有任何限制，吃，住，行；政治，经济，体育；男人，女人，性......</P>
<P>题材之广泛，真的是前不见古人，后不见来者，这也算是无聊得大学生活中的一点点乐趣</P>
<P>昨天和apeng还谈到了去学散打，现在正苦于没有地方，真是痛苦。如果有门路的大侠透点风，thx<IMG src="http://blogger.org.cn/blog/images/emot/face29.gif"></P>]]></description>
</item><item>
<title><![CDATA[增加力量和肌肉[zz]]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4238</link>
<author>tonny1982</author>
<pubDate>2005/3/29 12:43:26</pubDate>
<description><![CDATA[转载自: <A href="http://www.5ifit.com/content/html/1801.htm" target=_blank>http://www.5ifit.com/content/html/1801.htm</A><BR><BR>增加力量和肌肉<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#1 使用差别组方法来获得最佳效果。用一个你只能完成1～2次的重量，尽全力去完成，然后将重物放下休息10秒钟，再用适中的重量做10次。你在这11～12次的运动中所做的“净”重量要比连贯做一组所用的重量要大，这可让你获得最佳训练效果。<BR><BR><A href="http://www.5ifit.com/shop/sorts_257_1.htm" target=_blank></A>&nbsp;&nbsp;&nbsp;&nbsp;#2 欺骗一下你的神经系统。将作用相反的肌群组成超级组来进行训练，可以帮助你更好地操纵体内的传导系统，使你举起更重的重量。这种技巧可以消除神经系统对于肌肉发力所产生的限制。你可使用重点交替的方式训练，例如先做1组杠铃卧推，再做1组划船，然后休息60～90秒钟。休息之后，先做划船，再进入杠铃卧推的训练。记住：要做的组数应该是偶数（2，4，6等），以维持肌肉平衡发展。这种方法对于肱二头肌/肱三头肌的组合和股四头肌/腘绳肌的组合同样有效。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#3在不增加训练时间的情况下增加肌肉等长收缩训练。或许最容易受到忽视的肌肉收缩方式就是等长收缩了。你可以在各组之间添加等长收缩练习以增加训练的强度。例如，在完成1组杠铃卧推之后，马上拿1个较轻的哑铃，将双侧手掌压在盘片的外侧，用力向内压，收缩5～10秒钟之后放松，马上再重复此动作。你也还可以使用训练球来完成这一训练。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#4 使用增强式训练来挑战身体的极限。你是否渴望提高你的推举或深蹲的成绩？那么在训练之前先做30秒钟的增强式训练，就能将成绩提高10～20磅（1磅=0.4536公斤）。在做深蹲之前，站在一条长凳上，然后跳下，当双脚着地时，马上使用爆发力尽可能高地向上跳，重复2次。在做杠铃卧推之前，做2次爆发式俯卧撑（在上升阶段的结尾，双手应离开地面数英寸（1英寸=2.54厘米））来热身。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#5 使用训练球来帮助你进步。大多数健美运动员都不愿使用训练球训练，因为在使用训练球时，身体的稳定性很难控制，所以不得不使用较轻的重量，这样肌肉受到的刺激就不如普通训练时那样强烈。下面将告诉你如何使用训练球来增加辅助肌群和躯干肌群的力量。以哑铃仰卧推举为例：先在一条水平长凳上做一个重负荷组（6～8次），然后马上在一个训练球上用你能做8~10次的重量做1组哑铃推举。因此时，你的胸肌已经疲劳了，在进行推举时使用较轻的重量也足以锻炼已经预疲劳了的胸肌。这样就能达到锻炼辅助肌群的目的。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#6+#7<BR>&nbsp;&nbsp;&nbsp;&nbsp;（肖恩·雷）IFBB职业健美运动员<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;培养一种竞争的态度。我常把训练伙伴看作是我的敌人，一个我在健身房中要击败的人。我的目标就是成为最好的运动员。如果有一个人能排在我前面，那我的任务就没法完成了。这种态度是帮助我能够在这一运动中名列前茅的原因。<BR>&nbsp;&nbsp;&nbsp;&nbsp;与弱点作斗争。如果你感觉自己已经很强壮了，那么将主攻方向放在那些你的不足之处上。若干天休息之后，在你能量充足之时再训练你身体的薄弱部位，让它们赶上来。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#8 像举重运动员那样进行训练。每过几个月，你就应花2个星期的时间进行高强度、低次数的训练，以突破训练的平台期。使用很重的重量，每组做4～6次，各组之间休息3分钟。注意在复合运动中（深蹲、卧推、划船）做较多的次数（5～6组），而在孤立运动中（屈伸、弯举和飞鸟）做的次数可以相对少些。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#9 在每次训练之后进行造型训练。这不仅能增加你的自信心，而且能够改善你肌肉的密度和质量。另外，还有助于你的神经肌肉连接，使你的肌肉更加精确、更加容易地增长。在你每次锻炼完之后，站在镜子前面，让身上的每一块肌肉都做15～30秒钟的顶峰收缩。重复3～4次。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#10 尝试着增加极端组。花3周的时间将你的训练组数增加50%，每年这样做2次。3周之后，休息4天，然后恢复到正常的训练中去。增加的组数能够刺激新的肌肉增长，而之后的休息能够让身体充分恢复。举个例子：如果通常你胸部做12组、背部做16组，那么分别将它们增加到18组和24组。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#11 分组训练去。你是不是无法按照规定的次数完成多组的引体向上？那么就不要勉强自己去完成3组10次的训练了，你应该将目标放在总次数30次上。你可以将这30次分成很多组来做，之间稍作休息。这不是作弊，而是休息—停顿原则的一种衍生。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#12+#13<BR>&nbsp;&nbsp;&nbsp;（罗尼·库尔曼）六届奥林匹亚先生得主。<BR>&nbsp;&nbsp;&nbsp;“我是通过减量组的方法来增加我大腿的肌肉的。在完成重负荷组之后，我会减轻盘片的重量，然后再尽可能地做最多的次数，继续减量，直至减完。剩下的就只有彻底疲劳的股四头肌了。<BR>&nbsp;&nbsp;&nbsp;&nbsp;对于同一个身体部位，交替使用两种完全不同的训练方案，而不要始终使用相同的方法。例如，在我的背部训练中，我将侧重于中负荷硬拉和划船的训练同使用各种引体向上和下拉运动，交替进行来锻炼背阔肌。这样做的好处是可以从不同的角度锻炼你的背阔肌”。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#14寻求较小肌肉的发展。如果忽视针对某些肌肉的精细训练，就等于放弃了挖掘身体全面发展的机会。例如，你大腿内侧和外侧的肌肉（外展肌群和内收肌群）并不是非常显眼，但是，如果不通过特殊的训练，你是没有办法开发出大腿全部潜力的。例如：要获得粗壮的手臂，你必须训练其前部深层的肌肉—肱肌。为这一肌肉进行一些特殊的训练，（例如4组反式托臂弯举）。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#15 参考一下奥林匹克运动。使用杠铃或者哑铃，每周做一次奥林匹克运动式的抓举和挺举训练。在前几周的时间里注意学习其中的技巧，然后使用你能做6～8次的重量，但只做3组，每组3次，用爆发力完成动作。将这一训练做6～8周，以锻炼你快速收缩肌纤维的能力。这能有效地增加你肌肉的力量和体积。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#16 尝试一下左右开弓。在你的手臂训练中，尝试一下这种方法：在你右手做单臂肱三头肌下压的同时，左手做单臂哑铃弯举。你的两侧手臂都会比平时显得更加有力。两侧手臂交换，然后重复。这也可以应用于拉力器十字交叉之中，用于锻炼背部肌肉和胸肌。具体动作：背对着一个拉力器架站立，左手握住滑轮把手（调至肩部高度），右手握住身前拉力器架上的滑轮把手（调至髋部高度）。在左手做肩上推举的同时，右手做拉力器划船。两侧手臂交换，然后重复。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#17+#18<BR>&nbsp;&nbsp;&nbsp;（杰伊·卡特）三届阿诺德经典赛冠军<BR>不要让你前部三角肌的发展领先于你的肩部和背部。要多做一些位于身后的训练，例如使用拉力器做侧平举和用哑铃做耸肩运动。<BR>&nbsp;&nbsp;&nbsp;&nbsp;用双杠臂屈伸运动来刻画出内侧胸肌的线条，打造出马蹄形的肱三头肌。我在双杠上做臂屈伸运动时不增加额外的重量。我下降得很低，在获得充分地伸展之后，将自己支撑起来，在最高点处做顶峰收缩，让我的肱三头肌发挥最大的作用。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#19特殊运动的训练并不是某些运动员的专利。每个人都可以从各种各样的运动中获益，生活就像是一种全方位的运动，所以你的训练也应该反映这种特点。你应该变得更快、更强、更平衡，从各个方面提高你的身体素质。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#20 以器械训练结束动作。自由负重训练可能是你的主要训练方案，但无论是在训练快要结束时还是一个复合组快要结束时，器械训练都有不可替代的优势。因为器械限定了运动的角度，保证了稳定性，所以你在使用它时，更容易做到完全疲劳的次数。你也可以充分利用预疲劳的方法，先做一个孤立运动（例如用哑铃侧平举来锻炼中部三角肌），然后接一个复合器械运动（例如器械肩上推举），做到完全疲劳为止。到训练结束时，你的三角肌应该已经在喊救命了。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#21 加快运动速度。你是否可以在保证安全的前提下，尽可能快地完成你的动作呢？是的，可以。如果你使用足够的重量，那么“尽可能快”就不会变得非常快，而只是相对而言。许多人认为他们应该在上升和下降的过程中控制重物，在伸展和收缩时肌肉的工作方式是一样的，但事实并非如此。（当然，如果你能以飞快的速度将重物甩起来，那么说明你使用的重量实在是太轻了。）<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#22 单独做手臂训练。如果你继续在胸部、背部或者肩部训练之后再训练你的手臂肌肉，那么你将无法完成一次真正高强度的手臂训练。将不同的训练分开安排一次单独行动，看看会发生什么样的不同。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#23+#24<BR>&nbsp;&nbsp;&nbsp;&nbsp;（迈克·马特拉佐）IFBB职业健美运动员<BR>&nbsp;&nbsp;&nbsp;&nbsp;在你外出时按照正确的方法安排训练和饮食是很有技巧的，所以你需要在出发之前就做好准备。我会预先烤3磅重的瘦牛排带在身上，这样我就始终能够获得充足的蛋白质来源，而不用担心找不到吃饭的地方了。我先将这些牛排在调味汁中浸泡数个小时，放上一些啤酒和意大利甜酱，然后用小火烤上4~5个小时。等出来的时候，你会发现这些牛排又香又嫩。<BR>&nbsp;&nbsp;&nbsp;&nbsp;这是在我比赛之前会做的一个杀手级的腹肌训练计划。我从仰卧起坐开始，在胸口放上一个盘片，然后做尽可能多的次数，然后将盘片放下，接着做至完全疲劳。接下来我会做负功运动，直至我快要无法呼吸时为止。最后，我在镜子前面以最大的努力进行造型训练，直至精疲力尽时为止。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#25 用躯干运动来保护你的下背部。在每次训练之前，先做几组腹肌训练，然后再做2组背屈伸运动来热身。身体躯干的运动能够“唤醒”脊柱周围错综复杂的肌肉，使得你在后面的训练中，能以更快的速度增加腹压，保持身体稳定。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#26 在你的下一次负重和腿部训练之前休息一天。确保你没有在别的身体活动中浪费你的能量，保证合理的饮食和充足的睡眠。这样，你将获得一次令人难以置信的训练！<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#27 换一个新伙伴。如果你已经超过了你的训练伙伴，那么寻找一个对你来说更有挑战性的。训练伙伴吧新的伙伴将促使你更加刻苦地训练、让你产生新的动力，并教你一些新的方法。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;保持苗条<BR>&nbsp;&nbsp;&nbsp;&nbsp;#28 用深蹲来减脂。如果你想要变得更加苗条，那么在每周训练结束之前的1～2次训练中，使用间隔深蹲的方法来激发你的新陈代谢速度。要使用较轻的重量，做5组深蹲，每组25～50次，各组之间只休息60秒钟，然后进入有氧运动训练。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#29 让激素提高有氧运动的效果。成功的有氧运动的关键之处在于：在消耗掉身体脂肪的同时，并不减少肌肉。虽然大量的有氧运动是对身体有益的，但是长时间的进行有氧运动能够使睾酮水平有所下降。你可以在长时间有氧运动前的30分钟服用200毫克咖啡因。空腹服用咖啡因能够促进体内分泌帮助脂肪燃烧的激素儿茶酚胺。在做完30分钟有氧运动之后，停下来服用10克支链氨基酸（BCAAs），然后接着训练30～40分钟。支链氨基酸不仅能够防止体内睾酮水平的下降，而且能够阻止身体分解肌肉组织，并促进脂肪的燃烧。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#30 为减少脂肪而寻求激素的支持。苗条的体形需要通过不同的方法来获得。先空腹服用6克精氨酸和400毫克左旋多巴，然后再训练。益处：精氨酸和左旋多巴可以提高生长激素的水平，并同空腹训练时的生长激素水平的增高而发生协同作用。高水平的生长激素能够促进脂肪的燃烧，并防止肌肉组织的分解。此外精氨酸能够激发体内一氧化氮的释放，增加肌肉的膨胀感。在训练之后，马上服用碳水化合物或乳清蛋白饮料，以保证肌肉的休息和恢复。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#31 用超重负荷刺激你的胸肌。在飞鸟机上使用较重的重量，异用部分运动的方式来完成训练。只使用1/4的运动幅度，做12～15次部分运动，让你的肌肉完全疲劳。注意一定要有一名训练伙伴帮助你进入开始姿势。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#32 减少水的含量能让你看起来更棒。要想减少体内多余的水分，你应该在6天的时间里增加对盐分的摄入量，并将水的摄入量增加至原来的2倍。体内主要起保留水分作用的激素醛固酮水平将明显下降，最终将体内多余的水分排除掉。在第7、8、9三天里，减少盐分的摄入，这样你就能去除掉体内多余的水分，暂时改善你的肌肉外形和清晰度。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#33 循环有氧运动让你更快地获得效果。你可能听说过碳水化合物循环摄入的方法—将高碳水化合物和低碳水化合物饮食交替进行，以促进脂肪的消耗。同样的方法也可被延伸到有氧运动中，帮助你消耗掉更多的热量。将你的有氧运动循环进行：在2天中全力以赴地做30分钟，接下来的2天则做间隔训练45分钟，1天以比较轻松的方式做60分钟。在开始下一个循环之前，休息2天。这不仅能让你的脂肪燃烧系统不断地加速和停止，以防止出现运动平台期，最大限度地消耗热量。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;运动的完成<BR>&nbsp;&nbsp;&nbsp;&nbsp;#34 做负重仰卧起坐。在锻炼肌肉耐力之前，你应该先提高它们最大的力量。当你处于仰卧起坐姿势时，让训练伙伴站在你的脚那边，双手压住你的肩部（将你压向地面），而你则用力对抗他/她的力量。如果他/她不够强壮来对抗你的力量，那么你也可以用手握住一个盘片。不管用哪种方式，你的双臂都应该交叉在胸前。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;#35 做阿诺德式推举。这并不是一种运动，但是每　　次当我们说到创造发明时，首先会想到的就是这一运动。这是一种最好的锻炼前部三角肌的运动，后者主要负责手臂的外展和内旋。这两种运动都是通过前三角肌来启动的。<BR><BR>　　#36使用拉力器十字交叉设备来完成反式剪蹲。将拉力器十字交叉的两个把手绑在一起，中间垫上一个垫子。将这个垫子靠在你的上背部（而不是颈后），然后做反式剪蹲。这样做可以同时锻炼你的肌肉和身体的平衡性。<BR><BR>　　#37 向外伸展。当你做深蹲的时候，应该在整个过程中都始终努力将双膝向两侧伸展，并将力量集中于脚趾的外侧。这不仅能保持髋部的压力，还能帮助你举起更重的重量。<BR><BR>　　#38 用下拉弯举来锻炼肱二头肌。将一个短杠挂在一个下拉拉力器上。像做拉力器下拉一样坐下，掌心向上握住短杠，手臂伸直过顶。不要运动肩部，让肘部弯曲，将短杠弯举至颈后。<BR><BR>　　#39 使用老式技巧取得超级收获。以肱二头肌弯举为例，介绍一下此版本的减量组训练技巧。用你能够弯举15次的杠铃重量，然后将训练弹力带调至你正好能做15次弯举。在握住弹力带的同时做杠铃弯举，通常应该是7～8次。放开弹力带，仅用杠铃做尽可能多的次数。减去约1/3的重量，再做尽可能多的次数。休息2～3分钟，将整个组重复2～3次。 <BR><BR>　　#40 你在做仰卧曲杠推举时双肘是否外展？如果是这样，尝试一下这种技术来帮助你更好地单独训练肱三头肌：在你的肘部上方绑上负重带，然后让训练伙伴将曲杠杠铃递给你，好好“折磨”一下你的肱三头肌吧。<BR><BR>　　#41 减少肌二头肌的参与。这是一种帮助你在背部训练中减少肱二头肌的参与的上佳方法。先收缩背部肌肉，将胸部前挺，然后再弯曲肘部，将重物上拉。<BR><BR>　　#42 用上斜哑铃推举锻炼胸肌。你是不是像通常那样沿宽阔的弧线将哑铃推向胸部上方？正确的姿式是让你的双手更靠近胸部向上推，当它们相互接触后，直接往上推6～10英寸，然后沿同轨迹下放。这一运动同时还锻炼内侧胸肌和肱三头肌。<BR><BR>　　#43腹肌的训练。这是一种对你的腹肌来说真正艰苦的训练。将脚踝套连接在拉力器低位滑轮上，并将一根绳索连接在相反方向的低位滑轮上。在你的臀下垫上一个垫子将你抬高数英寸。用手握住绳索，同时将脚踝套套在腿上。膝关节弯曲，然后按照反式仰卧起坐的方式，使你的膝关节靠近你的胸部。同时将肩胛骨抬离垫子。尝试着让你的膝盖与双肘接触，同时将臀部抬离垫子。<BR><BR>　　#44 用毛巾卷来帮助你。在做上斜运动时，在颈部下面垫一个毛巾卷可以帮助你增加力量。其中的机理我们还不知道，但是人们认为这可能与保持脊柱的正确姿势有关。<BR><BR>　　#45 做一个双杠臂屈伸专家。你不应该将重点放在臂屈伸次数上，而应更加关注时间。在下降的过程中，你应该用足4秒钟，大声地数出来？要想更多地锻炼胸肌，你的身体就应该向前倾斜，让你的双肘向两侧伸展。如果你的身体保持竖直，并将双肘内收，那么更多的将是锻炼你的肱三头肌。]]></description>
</item><item>
<title><![CDATA[不小心把blog百合模板给覆盖了，unhappy]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4216</link>
<author>tonny1982</author>
<pubDate>2005/3/28 12:59:13</pubDate>
<description><![CDATA[<P>真的很不很不开心，辛苦做的百合模板被覆盖了，又没有备份</P>
<P>这个blog真的不过人性化，<IMG src="http://blogger.org.cn/blog/images/emot/face14.gif"><IMG src="http://blogger.org.cn/blog/images/emot/face12.gif"><IMG src="http://blogger.org.cn/blog/images/emot/face13.gif"><IMG src="http://blogger.org.cn/blog/images/emot/face22.gif"></P>
<P>真是的，我要哭了</P>]]></description>
</item><item>
<title><![CDATA[好好锻炼，天天向上]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=tonny1982&amp;id=4215</link>
<author>tonny1982</author>
<pubDate>2005/3/28 12:39:18</pubDate>
<description><![CDATA[
<P>昨天运动过度（下午游泳2hours-&gt;健身房10mins-&gt;0.5hour排球-&gt;晚上健身房1hour)，现在感觉有点疲劳。</P>
<P>经过差不多三个礼拜的坚持，收获不少，身体练了，心情也舒畅了不少<IMG src="http://blogger.org.cn/blog/images/emot/face24.gif">，还认识了一些高手</P>
<P>不过有一点比较郁闷，南大的健身房中老外占了一半，韩国人特别多。真是的，中国全民健身的意识这么这么差a<IMG src="http://blogger.org.cn/blog/images/emot/face16.gif"></P>
<P>有没有想去的，快报名a<IMG src="http://blogger.org.cn/blog/images/emot/face13.gif"></P>]]></description>
</item>
</channel>
</rss>