﻿<?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-☆蓝色梦想☆-随笔分类-J2EE</title><link>http://www.blogjava.net/zlsunnan/category/5660.html</link><description>世界总是反反覆覆错错落落地飘去 来不及叹息 生活不是平平淡淡从从容容的东西 不能放弃</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 12:09:06 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 12:09:06 GMT</pubDate><ttl>60</ttl><item><title>成为一个好的系统分析员</title><link>http://www.blogjava.net/zlsunnan/archive/2006/08/28/66158.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Mon, 28 Aug 2006 05:41:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2006/08/28/66158.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/66158.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2006/08/28/66158.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/66158.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/66158.html</trackback:ping><description><![CDATA[
		<span class="javascript" id="text87199" style="FONT-SIZE: 12px">
				<font face="Verdana">如何成为一个好的系统分析员<br />truely眼中的设计定义：设计的过程就是将事务处理抽象成计算机模型的过程。 <br />1. 首先要明白设计远比编程重要。 <br />2. 平时注重训练自己的思维严谨性和从全局考虑问题的能力。建立冷静思考问题的处事态度。 <br />3. 设计时（尤其是数据库设计时）不要完全被规矩约束，设计好比作诗，懂得韵律是对的，但完全被韵 律所束缚，就作不出好诗了。 <br />4. 多做设计，经常总结自己的不足之处和成功之处，向他人请教。 <br />5. 专门去找别人设计的漏洞和不足，也是提高自己设计水平的重要手段。 <br />（记住：这个好方法不要顺便外传，自己知道就行了，嘻嘻-:） <br />6. 经验是重要的，但如果观念老化而不善于总结提高，所谓的经验就成为束缚自己进步的枷锁。 <br />7. 学好数学特别是理论数学如数学分析、运筹学、数学模型等。多玩策略性经营游戏也是有益的。推荐 《帝国时代》和《模拟首都3000》以及《大富翁4》。（但不要沉陷在里面） <br />8. 根据项目情况和开发平台工具的特点确定最佳的设计方法。模块化设计方法和面向对象设计。两种设 计方法的结合使用。 <br />9. 将复杂无序的过程用模块化的方法进行分解，但要注重事务间的联系，并且用开放的眼光去设计。 <br />10. 设计时对严谨性、灵活性、开发效率、客户要求四个方面做衡量取舍。 <br />11. 设计时还要根据整个工程的进度安排和客户对软件的要求而决定是否设计得足够灵活和严谨。 <br />12. 复杂而无条理是最糟的设计，简单实用并不一定是最好的，但一定不是最坏的。（不要说我偷懒哟） <br />13. 训练自己良好的表达能力，能用清晰明确而且简单的描述表达出自己的基本思路。 <br />14. 在一个项目中建立统一的系统分析模式和文档模板，同时，一个项目中必须至少有一个人对整个系统 设计进行检查和进行全局的考虑。 <br />再谈如何成为一个好的系统分析员？ <br /><br />bylsfboy <br /><br />系统分析员基本功： <br /><br />好的系统分析员都是从优秀的程序员中产生的，坚实的编程功底、丰富的经验是今后做系统分析的基础。 <br />没有对系统本身进行过透彻剖析过，很难领会到其中一些难以言述的精华。但并不等于好的程序员就能够 成为好的系统分析员。 <br /><br />合理的知识结构。语言能力、文字表达能力、技术的全面性等是对系统分析员的基本要求。比如说c/s和3 层开发，如果仅仅对netscape公司的产品熟悉还不够，还需要了解比如微软等产品，并且要了解他们中产 生历史,发展思路，技术优劣，以应付各种穷追猛打的提问。但更重要的是，这是你为应用定制技术要求 的前提。 <br /><br />系统分析员思想： <br /><br />全局观念是系统分析员必须具备的观念。如果系统分析员设计时太注重细节，往往会陷入在某个问题上纠 缠不清的泥潭。（93年，我论文指导老师的一席话影响了我随后几年对软件开发的理解----今后计算机会 越来越快，多写几行代码少写代码无关紧要，最重要的是整体；一开始就错了，某个部份编得再好，也是 没有用的） <br /><br />任务难度的预测能力 <br /><br />系统分析员要具备快速的任务难度预测能力以及具备快速确定开发小组人员构成和任务划分的能力。（我 将这条归为思想，而不是能力）昆虫自然会长出翅膀，而思想却需要长期的浸润。要做到这点，需要大量 的思考、学习。设计远比编程重要。当今软件业的发展，各种开发工具的出现，编程已经不是什么问题， 程序员的工作某种程度上讲是将别人现成的东西拼凑堆砌起来。系统分析员要清楚的认识到，现在大多数 程序员没有学会怎么去整体的了解一个系统，有些甚至不了解编程（这不是说他们不会写代码）。可视化 的开发工具加五花八门的控件，程序员可以偷点懒了。（这可不是夸大，我好几年的管理工作，接触过大 量的程序员）基于技术，跳出框架。基于现有技术结合用户需求思考问题，设计时跳出框架。 <br /><br />系统分析员思想： <br /><br />系统分析员要有面向用户的思想。系统分析员应当有能力将自己扮演成用户，来了解要交付的项目看起来 想什么样式，感觉想什么，从而了解用户的想法并挑选出合理部份去开发。从这个意义上说，系统分析员 才能获得有意义的见解去引导他的开发组成员。系统分析员头脑中要对项目结局有一个清楚的认识，并保 证项目不偏离方向。系统分析员要有根植于技术，高于技术思考问题的思想。纯粹的程序员通常对最终结 果考虑的不是很多，当一种新的技术在市场上出现时，他们对能否按时交付的考虑就比较少，而强烈希望 他们的计划能够建立在新的技术之上。因此，系统分析员的想法和行动要象一个用户，又要能够站在技术 的高度，成为真正的用户、程序员之间的代言人。 <br /><br />系统分析员的关键 <br /><br />获得信任。系统分析员最重要的素质是获得信任，这是成为优秀系统分析员的关键。成熟最为关键。成熟 可以为整个项目组提供正确的支持,能够理解技术怎样才能解决用户的需求。 <br /><br />系统分析员的准备工作 <br /><br />统一的各种文档模式，这其中包括今后软件变量、字段命名规则。我推荐用pb制定的规则做基础，通过改 造成为适合自身实用的标准。统一的文档管理。统一的分析软件。比如说rose（uml太规范，国内的软件 管理水平根本用不上，只不过尽量应用，你自己对系统分析的理解有好处） 方法是思想的放映，在具体方法上就不多说了。我托人从u$a弄到几本书，用于面向对象系统开发的使 用》、《面向对象的分析》、《项目管理》等都是很不错的，推荐大家看看。 <br /><br />我在拙作"在中国没有人懂计算机"里发了点牢骚，听说挨了部份人（习惯性的）骂。其实，bbs本来就是 发泄的地方，在这里从来就罕有有内容的文章。 <br /><br />自从"维纳斯"登陆深圳后，大家更着眼于从宏观看中国的it业了。中国it这棵小树，说实在的，长到今天 实在是不容易。一些人提出了"反对微软霸权"的口号，不少人呼唤中国"硅谷"的出现。微软的成功不是技 术的成功，更多的是商业运作的成功。中国it这棵树能长多高，取决于他所植根于的土壤。而现在的事实是，这片土壤实在是太贫瘠了！如果按我们现在的思路和搞法，是长不成大树，更别指望能结出"微 软"，"硅谷"这样丰硕的果实。如果说，我们的软件技术落后美国十年，我们的硬件制造技术则落后美国 二十年，我们的管理水平落后美国至少三十年。而最终决定发展速率的恰恰是我们的死穴──低劣的管理 水平。低劣的管理水平的形成的原因有着深厚的背景和多方面的原因。 <br /><br />系统分析工作是解决一个问题的工作,目标是将一个对计算机应用系统的需求转化成实际的物理实现,其中 复杂就复杂在实际的面太多.在系统分析过程之中注意问以下的问题,可能会所进行的系统分析设计工作有 帮助. <br /><br />1)您所完成的系统目的是什么?注意不是功能要求,而是目的.也就是为什么要建设、为什么要现代建设。 <br /><br />2）您所完成的系统有哪些方面参与，各方面的初衷是什么？那些人可能在系统建设中起重要作用，他们 会采取什么样的态度？你对他们有多少影响力？ <br /><br />3）您的系统是否有一个明确的评价标准？最好从参与的各方面都进行考虑。 <br /><br />4）你的系统设计思想是什么？是否能够得到各方面的认可。 <br /><br />5）你对参与系统设计开发的人员了解吗？他们的特长在哪里，是否愿意与你合作，为什么？你对他们有 足够的影响力吗？ <br /><br />6）你的系统开发计划是否完善？你的计划表有明确的阶段吗？任何一阶段都应该怎样完成？如何对这一 阶段完成的情况进行评价？ <br /><br />7）你对所采用的系统开发方法以及工具是否熟悉？你的夥伴是否熟悉？ <br /><br />8）你所完成的系统是否有原型？计算机的或者物理的。 <br /><br />以上的几个问题都是在系统分析以及系统规划时涉及到的，供各位参考。 <br /><br />系统分析工作是解决一个问题的工作,目标是将一个对计算机应用系统的需求转化成实际的物理实现,其中 复杂就复杂在实际的面太多.在系统分析过程之中注意问以下的问题,可能会所进行的系统分析设计工作有帮助 <br /><br />1)您所完成的系统目的是什么?注意不是功能要求,而是目的.也就是为什么要建设、为什么要现代建设。在考虑系统目的时，我更多的侧重于系统的最终目标考虑，因为一个系统不可能一下子完美，为系统留些 余地。 <br /><br />2）您所完成的系统有哪些方面参与，各方面的初衷是什么？那些人可能在系统建设中起重要作用，他们 会采取什么样的态度？你对他们有多少影响力？中国it行业的失败之一就是人"太年轻"，一定要有领导的 支持，否则完蛋。不要认为自己对他们会有多少影响力，即便有，也要尽可能的认为是决策者再影响他 们。在中国，一个技术员，你算老几？说到这里我很悲哀。哪些人在系统中起重要作用并弄清楚他们的态 度，这点十分关键。 <br /><br />3）您的系统是否有一个明确的评价标准？最好从参与的各方面都进行考虑。不知道这样说对不对，在系 统建设之前，对你的程序员、对你的领导要有至少不同的两种评价。 <br /><br />4）你的系统设计思想是什么？是否能够得到各方面的认可。如果高明，对领导、对程序员都采用引导， 得到认可的最好办法，就是让他们认可他们自己的想法。（我力图这样做，但做得不好，系统分析员有一 点要学会韬光养晦，忍） <br /><br />5）你对参与系统设计开发的人员了解吗？他们的特长在哪里，是否愿意与你合作，为什么？你对他们有 足够的影响力吗？软件发展到一定的程度，不是编程，不是数学，而是管理。 <br /><br />6）你的系统开发计划是否完善？你的计划表有明确的阶段吗？任何一阶段都应该怎样完成？如何对这一 阶段完成的情况进行评价？ <br /><br />7）你对所采用的系统开发方法以及工具是否熟悉？你的夥伴是否熟悉？事实上，不是每种好的工具都要 使用，也并不一定都要他们熟练掌握。提醒诸位一句，当你将方案做得可以不依赖某个程序员，你在程序 员面前就无信任可言，因为从此程序员将受到更大的生存压力。我坚决不在公司使用rose。 <br /><br />8）你所完成的系统是否有原型？计算机的或者物理的。 <br /><br />以上的几个问题都是在系统分析以及系统规划时涉及到的，供各位参考。 <br /><br />这文章很好，我的话是："需求分析实际应该是问题分析"。含义是系统要解决的是问题。而不是用户提出 的需求。经常发现系统完成后，客户说"我的问题还没有解决"。可是，需求分析稿上的目标都搞定了。 <br /><br />既然是问题分析，所以，熟悉目标系统的知识就是必要的。甚至，可以说，一个好的系统分析员也应该是 好的业务专家。 <br /><br />我很高兴在这里遇到许多分析高手，可以交流分析中的问题。我赞同从来的观点。在中国作分析重要的是 人气，因为中国的企业级信息系统的建设在很大程度上可以说并非确有需求，而是迫于某种压力。用户在 很多时候考虑的不是系统的长远发展，而只是短期的成果，要求开发单位在很短的时间内完成一个很大的 系统的开发，没有时间对系统进行周密的分析，在这种情况下，很多开发商就会粗分析，粗设计，尽快进 入编码阶段，这样的系统的生命周期肯定不会很长。说了这么多，只是想说，系统分析员确实应是业务和 管理专家，并且需要有很好的语言组织能力，他需要根据问题域中存在的问题去尽力说服用户，引导用户 需求，毕竟，我们是专家，如果让用户牵着鼻子走，系统不会是成功的系统。（当然了，这要建立在用户 是可引导的前提下）本人拙见。 <br /><br />在理解和分析用户的需求时，应说服用户明白：建立计算机应用系统并不是简单地用计算机代替手工劳 <br />作，它更应该是管理思想的一次革命，是现用户模式的一次升华和提高。如果系统不能高于现实，开发的系统将长期陷入需求的反复修改，其软件的生命周期也短了。 <br /><br />针对我对您的问题的理解,试着作如下一般性/理论性的回复: <br /><br />需求分析(您可以采用usecasedriven的方法进行需求分析)在明确需求分析的基础上,确定需要采用的系统分析方法(结构化/面向对象/构件式)应用您的开发团队所确定采用的分析/设计方法,进行系统分析.根据您所采用的分析方法,依次或反复进行系统设计/建模. </font>
				<br />
		</span>
<img src ="http://www.blogjava.net/zlsunnan/aggbug/66158.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2006-08-28 13:41 <a href="http://www.blogjava.net/zlsunnan/archive/2006/08/28/66158.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>写出高质量软件</title><link>http://www.blogjava.net/zlsunnan/archive/2006/08/18/64259.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Fri, 18 Aug 2006 01:27:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2006/08/18/64259.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/64259.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2006/08/18/64259.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/64259.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/64259.html</trackback:ping><description><![CDATA[1. 你们的项目组使用源代码管理工具了么？<br /><br />　　MVM：应该用。VSS、CVS、PVCS、ClearCase、CCC/Harvest、FireFly都可以。我的选择是VSS。<br /><br />　　2. 你们的项目组使用缺陷管理系统了么？<br /><br />　　MVM：应该用。ClearQuest太复杂，我的推荐是BugZilla。<br /><br />　　3. 你们的测试组还在用Word写测试用例么？<br /><br />　　MVM：不要用Word写测试用例（Test Case）。应该用一个专门的系统，可以是Test Manager，也可以是自己开发一个ASP.NET的小网站。主要目的是Track和Browse。<br /><br />　　4. 你们的项目组有没有建立一个门户网站？<br /><br />　　MVM：要有一个门户网站，用来放Contact Info、Baselined Schedule、News等等。推荐Sharepoint Portal Server 2003来实现，15分钟就搞定。买不起SPS 2003可以用WSS (Windows Sharepoint Service)。<br /><br />　　5. 你们的项目组用了你能买到最好的工具么？<br /><br />　　MVM：应该用尽量好的工具来工作。比如，应该用VS.NET而不是Notepad来写C#。用Notepad写程序多半只是一种炫耀。但也要考虑到经费，所以说是“你能买到最好的”。<br /><br />　　6. 你们的程序员工作在安静的环境里么？<br /><br />　　MVM：需要安静环境。这点极端重要，而且要保证每个人的空间大于一定面积。<br /><br />　　7. 你们的员工每个人都有一部电话么？<br /><br />　　MVM：需要每人一部电话。而且电话最好是带留言功能的。当然，上这么一套带留言电话系统开销不小。不过至少每人一部电话要有，千万别搞得经常有人站起来喊：“某某某电话”。《人件》里面就强烈谴责这种做法。<br /><br />　　8. 你们每个人都知道出了问题应该找谁么？<br /><br />　　MVM：应该知道。任何一个Feature至少都应该有一个Owner，当然，Owner可以继续Dispatch给其他人。<br /><br />　　9. 你遇到过有人说“我以为…”么？<br /><br />　　MVM：要消灭“我以为”。Never assume anything。<br /><br />　　10. 你们的项目组中所有的人都坐在一起么？<br /><br />　　MVM：需要。我反对Virtual Team，也反对Dev在美国、Test在中国这种开发方式。能坐在一起就最好坐在一起，好处多得不得了。<br /><br />　　11. 你们的进度表是否反映最新开发进展情况？ <br /><br />　　MVM：应该反映。但是，应该用Baseline的方法来管理进度表：维护一份稳定的Schedule，再维护一份最新更改。Baseline的方法也应该用于其它的Spec。Baseline是变更管理里面的一个重要手段。<br /><br />　　12. 你们的工作量是先由每个人自己估算的么？<br /><br />　　MVM：应该让每个人自己估算。要从下而上估算工作量，而不是从上往下分派。除非有其他原因，比如政治任务工期固定等。<br /><br />　　13. 你们的开发人员从项目一开始就加班么？<br /><br />　　MVM：不要这样。不要一开始就搞疲劳战。从项目一开始就加班，只能说明项目进度不合理。当然，一些对日软件外包必须天天加班，那属于剥削的范畴。<br /><br />　　14. 你们的项目计划中Buffer Time是加在每个小任务后面的么？<br /><br />　　MVM：不要。Buffer Time加在每个小任务后面，很容易轻易的就被消耗掉。Buffer Time要整段的加在一个Milestone或者checkpoint前面。<br /><br />　　15. 值得再多花一些时间，从95%做到100%好<br /><br />　　MVM：值得，非常值得。尤其当项目后期人困马乏的时候，要坚持。这会给产品带来质的区别。<br /><br />16. 登记新缺陷时，是否写清了重现步骤？<br /><br />　　MVM：要。这属于Dev和Test之间的沟通手段。面对面沟通需要，详细填写Repro Steps也需要。<br /><br />　　17. 写新代码前会把已知缺陷解决么？<br /><br />　　MVM：要。每个人的缺陷不能超过10个或15个，否则必须先解决老的bug才能继续写新代码。<br /><br />　　18. 你们对缺陷的轻重缓急有事先的约定么？<br /><br />　　MVM：必须有定义。Severity要分1、2、3，约定好：蓝屏和Data Lost算Sev 1，Function Error算Sev 2，界面上的算Sev 3。但这种约定可以根据产品质量现状适当进行调整。<br /><br />　　19. 你们对意见不一的缺陷有三国会议么？<br /><br />　　MVM：必须要有。要有一个明确的决策过程。这类似于CCB (Change Control Board)的概念。<br /><br />　　20. 所有的缺陷都是由登记的人最后关闭的么？<br /><br />　　MVM：Bug应该由Opener关闭。Dev不能私自关闭Bug。<br /><br />　　21. 你们的程序员厌恶修改老的代码么？<br /><br />　　MVM：厌恶是正常的。解决方法是组织Code Review，单独留出时间来。XP也是一个方法。<br /><br />　　22. 你们项目组有Team Morale Activity么？<br /><br />　　MVM：每个月都要搞一次，吃饭、唱歌、Outing、打球、开卡丁车等等，一定要有。不要剩这些钱。<br /><br />　　23. 你们项目组有自己的Logo么？<br /><br />　　MVM：要有自己的Logo。至少应该有自己的Codename。<br /><br />　　24. 你们的员工有印有公司Logo的T-Shirt么？<br /><br />　　MVM：要有。能增强归属感。当然，T-Shirt要做的好看一些，最好用80支的棉来做。别没穿几次就破破烂烂的。<br /><br />　　25. 总经理至少每月参加次项目组会议<br /><br />　　MVM：要的。要让team member觉得高层关注这个项目。<br /><br />　　26. 你们是给每个Dev开一个分支么？<br /><br />　　MVM：反对。Branch的管理以及Merge的工作量太大，而且容易出错。<br /><br />　　27. 有人长期不Check-In代码么？<br /><br />　　MVM：不可以。对大部分项目来说，最多两三天就应该Check-In。<br /><br />　　28. 在Check-In代码时都填写注释了么？<br /><br />　　MVM：要写的，至少一两句话，比如“解决了Bug No.225”。如果往高处拔，这也算做“配置审计”的一部分。<br /><br />　　29. 有没有设定每天Check-In的最后期限？<br /><br />　　MVM：要的，要明确Check-In Deadline。否则会Build Break。<br /><br />　　30. 你们能把所有源码一下子编译成安装文件吗？ <br /><br />　　MVM：要的。这是每日编译（Daily Build）的基础。而且必须要能够做成自动的。<br /><br />31. 你们的项目组做每日编译么？<br /><br />　　MVM：当然要做。有三样东西是软件项目/产品开发必备的：1. bug management; 2. source control; 3. daily build。<br /><br />　　32. 你们公司有没有积累一个项目风险列表？<br /><br />　　MVM：要。Risk Inventory。否则，下个项目开始的时候，又只能拍脑袋分析Risk了。<br /><br />　　33. 设计越简单越好<br /><br />　　MVM：越简单越好。设计时候多一句话，将来可能就带来无穷无尽的烦恼。应该从一开始就勇敢的砍。这叫scope management。<br /><br />　　34. 尽量利用现有的产品、技术、代码<br /><br />　　MVM：千万别什么东西都自己Coding。BizTalk和Sharepoint就是最好的例子，有这两个作为基础，可以把起点提高很多。或者可以尽量多用现成的Control之类的。或者尽量用XML，而不是自己去Parse一个文本文件；尽量用RegExp，而不是自己从头操作字符串，等等等等。这就是“软件复用”的体现。<br /><br />　　35. 你们会隔一段时间就停下来夯实代码么？<br /><br />　　MVM：要。最好一个月左右一次。传言去年年初Windows组在Stevb的命令下停过一个月增强安全。Btw，“夯”这个字念“hang”，第一声。<br /><br />　　36. 你们的项目组每个人都写Daily Report么？<br /><br />　　MVM：要写。五分钟就够了，写10句话左右，告诉自己小组的人今天我干了什么。一则为了沟通，二则鞭策自己（要是游手好闲一天，自己都会不好意思写的）。<br /><br />　　37. 你们的项目经理会发出Weekly Report么？<br /><br />　　MVM：要。也是为了沟通。内容包括目前进度，可能的风险，质量状况，各种工作的进展等。<br /><br />　　38. 你们项目组是否至少每周全体开会一次？<br /><br />　　MVM：要。一定要开会。程序员讨厌开会，但每个礼拜开会时间加起来至少应该有4小时。包括team meeting, spec review meeting, bug triage meeting。千万别大家闷头写code。<br /><br />　　39. 你们项目组的会议、讨论都有记录么？<br /><br />　　MVM：会前发meeting request和agenda，会中有人负责主持和记录，会后有人负责发meeting minutes，这都是effective meeting的要点。而且，每个会议都要形成agreements和action items。<br /><br />　　40. 其他部门知道你们项目组在干什么么？<br /><br />　　MVM：要发一些Newsflash给整个大组织。Show your team’s value。否则，当你坐在电梯里面，其他部门的人问：“你们在干嘛”，你回答“ABC项目”的时候，别人全然不知，那种感觉不太好。<br /><br />　　41. 通过Email进行所有正式沟通<br /><br />　　MVM：Email的好处是免得抵赖。但也要避免矫枉过正，最好的方法是先用电话和当面说，然后Email来确认。<br /><br />　　42. 为项目组建立多个Mailing Group<br /><br />　　MVM：如果在AD+Exchange里面，就建Distribution List。比如，我会建ABC Project Core Team，ABC Project Dev Team，ABC Project All Testers，ABC Project Extended Team等等。这样发起Email来方便，而且能让该收到email的人都收到、不该收到不被骚扰。<br /><br />　　43. 每个人都知道哪里可以找到全部的文档么？<br /><br />　　MVM：应该每个人都知道。这叫做知识管理（Knowledge Management）。最方便的就是把文档放在一个集中的File Share，更好的方法是用Sharepoint。<br /><br />　　44. 你做决定、做变化时，告诉大家原因了么？<br /><br />　　MVM：要告诉大家原因。Empower team member的手段之一是提供足够的information，这是MSF一开篇的几个原则之一。的确如此，tell me why是人之常情，tell me why了才能有understanding。中国人做事喜欢搞限制，限制信息，似乎能够看到某一份文件的人就是有身份的人。大错特错。权威、权力，不在于是不是能access information/data，而在于是不是掌握资源。<br /><br />　　45. Stay agile and expect change<br /><br />　　MVM：要这样。需求一定会变的，已经写好的代码一定会被要求修改的。做好心理准备，对change不要抗拒，而是expect change。<br /><br />　46. 你们有没有专职的软件测试人员？<br /><br />　　MVM：要有专职测试。如果人手不够，可以peer test，交换了测试。千万别自己测试自己的。<br /><br />　　47. 你们的测试有一份总的计划来规定做什么和怎么做么？<br /><br />　　MVM：这就是Test Plan。要不要做性能测试？要不要做Usability测试？什么时候开始测试性能？测试通过的标准是什么？用什么手段，自动的还是手动的？这些问题需要用Test Plan来回答。<br /><br />　　48. 你是先写Test Case然后再测试的么？<br /><br />　　MVM：应该如此。应该先设计再编程、先test case再测试。当然，事情是灵活的。我有时候在做第一遍测试的同时补上test case。至于先test case再开发，我不喜欢，因为不习惯，太麻烦，至于别人推荐，那试试看也无妨。<br /><br />　　49. 你是否会为各种输入组合创建测试用例？<br /><br />　　MVM：不要，不要搞边界条件组合。当心组合爆炸。有很多test case工具能够自动生成各种边界条件的组合??但要想清楚，你是否有时间去运行那么多test case。<br /><br />　　50. 你们的程序员能看到测试用例么？<br /><br />　　MVM：要。让Dev看到Test Case吧。我们都是为了同一个目的走到一起来的：提高质量。<br /><br />　　51. 你们是否随便抓一些人来做易用性测试？<br /><br />　　MVM：要这么做。自己看自己写的程序界面，怎么看都是顺眼的。这叫做审美疲劳??臭的看久了也就不臭了，不方便的永久了也就习惯了。<br /><br />　　52. 你对自动测试的期望正确么？<br /><br />　　MVM：别期望太高。依我看，除了性能测试以外，还是暂时先忘掉“自动测试”吧，忘掉WinRunner和LoadRunner吧。对于国内的软件测试的现状来说，只能“矫枉必须过正”了。<br /><br />　　53. 你们的性能测试是等所有功能都开发完才做的么？<br /><br />　　MVM：不能这样。性能测试不能被归到所谓的“系统测试”阶段。早测早改正，早死早升天。<br /><br />　　54. 你注意到测试中的杀虫剂效应了么？<br /><br />　　MVM：虫子有抗药性，Bug也有。发现的新Bug越来越少是正常的。这时候，最好大家交换一下测试的area，或者用用看其他工具和手法，就又会发现一些新bug了。<br /><br />　　55. 你们项目组中有人能说出产品的当前整体质量情况么？<br /><br />　　MVM：要有。当老板问起这个产品目前质量如何，Test Lead/Manager应该负责回答。<br /><br />　　56. 你们有单元测试么？<br /><br />　　MVM：单元测试要有的。不过没有单元测试也不是不可以，我做过没有单元测试的项目，也做成功了??可能是侥幸，可能是大家都是熟手的关系。还是那句话，软件工程是非常实践、非常工程、非常灵活的一套方法，某些方法在某些情况下会比另一些方法好，反之亦然。<br /><br />　　57. 你们的程序员是写完代码就扔过墙的么？<br /><br />　　MVM：大忌。写好一块程序以后，即便不做单元测试，也应该自己先跑一跑。虽然有了专门的测试人员，做开发的人也不可以一点测试都不做。微软还有Test Release Document的说法，程序太烂的话，测试有权踢回去。<br /><br />　　58. 你们的程序中所有的函数都有输入检查么？<br /><br />　　MVM：不要。虽然说做输入检查是write secure code的要点，但不要做太多的输入检查，有些内部函数之间的参数传递就不必检查输入了，省点功夫。同样的道理，未必要给所有的函数都写注释。写一部分主要的就够了。<br /><br />　　59. 产品有统一的错误处理机制和报错界面么？<br /><br />　　MVM：要有。最好能有统一的error message，然后每个error message都带一个error number。这样，用户可以自己根据error number到user manual里面去看看错误的具体描述和可能原因，就像SQL Server的错误那样。同样，ASP.NET也要有统一的Exception处理。可以参考有关的Application Block。<br /><br />　　60. 你们有统一的代码书写规范么？<br /><br />　　MVM：要有。Code Convention很多，搞一份来发给大家就可以了。当然，要是有FxCop这种工具来检查代码就更好了。<br /><br />61. 你们的每个人都了解项目的商业意义么？<br /><br />　　MVM：要。这是Vision的意思。别把项目只当成工作。有时候要想着自己是在为中国某某行业的信息化作先驱者，或者时不时的告诉team member，这个项目能够为某某某国家部门每年节省多少多少百万的纳税人的钱，这样就有动力了。平凡的事情也是可以有个崇高的目标的。<br /><br />　　62. 产品各部分的界面和操作习惯一致么？<br /><br />　　MVM：要这样。要让用户觉得整个程序好像是一个人写出来的那样。<br /><br />　　63. 有可以作为宣传亮点的Cool Feature么？<br /><br />　　MVM：要。这是增强团队凝聚力、信心的。而且，“一俊遮百丑”，有亮点就可以掩盖一些问题。这样，对于客户来说，会感觉产品从质量角度来说还是acceptable的。或者说，cool feature或者说亮点可以作为质量问题的一个事后弥补措施。<br /><br />　　64. 尽可能缩短产品的启动时间<br /><br />　　MVM：要这样。软件启动时间（Start-Up time）是客户对性能好坏的第一印象。<br /><br />　　65. 不要过于注重内在品质而忽视了第一眼的外在印象<br /><br />　　MVM：程序员容易犯这个错误：太看重性能、稳定性、存储效率，但忽视了外在感受。而高层经理、客户正相反。这两方面要兼顾，协调这些是PM的工作。<br /><br />　　66. 你们根据详细产品功能说明书做开发么？<br /><br />　　MVM：要这样。要有设计才能开发，这是必须的。设计文档，应该说清楚这个产品会怎么运行，应该采取一些讲故事的方法。设计的时候千万别钻细节，别钻到数据库、代码等具体实现里面去，那些是后面的事情，一步步来不能着急。<br /><br />　　67. 开始开发和测试之前每个人都仔细审阅功能设计么？<br /><br />　　MVM：要做。Function Spec review是用来统一思想的。而且，review过以后形成了一致意见，将来再也没有人可以说“你看，当初我就是反对这么设计的，现在吃苦头了吧”<br /><br />　　68. 所有人都始终想着The Whole Image么？<br /><br />　　MVM：要这样。项目里面每个人虽然都只是在制造一片叶子，但每个人都应该知道自己在制造的那片叶子所在的树是怎么样子的。我反对软件蓝领，反对过分的把软件制造看成流水线、车间。参见第61条。<br /><br />　　69. Dev工作的划分是单纯纵向或横向的么？<br /><br />　　MVM：不能单纯的根据功能模块分，或者单纯根据表现层、中间层、数据库层分。我推荐这么做：首先根据功能模块分，然后每个“层”都有一个Owner来Review所有人的设计和代码，保证consistency。 <br /><br />　　70. 你们的程序员写程序设计说明文档么？<br /><br />　　MVM：要。不过我听说微软的程序员1999年以前也不写。所以说，写不写也不是绝对的，偷懒有时候也是可以的。参见第56条。<br /><br />　　71. 你在招人面试时让他写一段程序么？<br /><br />　　MVM：要的。我最喜欢让人做字符串和链表一类的题目。这种题目有很多循环、判断、指针、递归等，既不偏向过于考算法，也不偏向过于考特定的API。<br /><br />　　72. 你们有没有技术交流讲座？<br /><br />　　MVM：要的。每一两个礼拜搞一次内部的Tech Talk或者Chalk Talk吧。让组员之间分享技术心得，这笔花钱送到外面去培训划算。<br /><br />　　73. 你们的程序员都能专注于一件事情么？<br /><br />　　MVM：要让程序员专注一件事。例如说，一个部门有两个项目和10个人，一种方法是让10个人同时参加两个项目，每个项目上每个人都花50%时间；另一种方法是5个人去项目A，5个人去项目B，每个人都100%在某一个项目上。我一定选后面一种。这个道理很多人都懂，但很多领导实践起来就把属下当成可以任意拆分的资源了。<br /><br />　　74. 你们的程序员会夸大完成某项工作所需要的时间么？<br /><br />　　MVM：会的，这是常见的，尤其会在项目后期夸大做某个change所需要的时间，以次来抵制change。解决的方法是坐下来慢慢磨，磨掉程序员的逆反心理，一起分析，并把估算时间的颗粒度变小。<br /><br />　　75. 尽量不要用Virtual Heads<br /><br />　　MVM：最好不要用Virtual Heads。Virtual heads意味着resource is not secure，shared resource会降低resource的工作效率，容易增加出错的机会，会让一心二用的人没有太多时间去review spec、review design。一个dedicated的人，要强过两个只能投入50%时间和精力的人。我是吃过亏的：7个part time的tester，发现的Bug和干的活，加起来还不如两个full-time的。参见第73条。73条是针对程序员的，75条是针对Resource Manager的。<br /><img src ="http://www.blogjava.net/zlsunnan/aggbug/64259.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2006-08-18 09:27 <a href="http://www.blogjava.net/zlsunnan/archive/2006/08/18/64259.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>23种设计模式的代码版</title><link>http://www.blogjava.net/zlsunnan/archive/2006/08/17/64022.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Wed, 16 Aug 2006 16:45:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2006/08/17/64022.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/64022.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2006/08/17/64022.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/64022.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/64022.html</trackback:ping><description><![CDATA[package lq.test;<br /><br />import java.io.*;<br />import java.util.*;<br /><br />//*********创建型模式***************<br /><br />//factory method 1<br />//1具体的构造算法，和2构造出的具体产品由子类实现<br />interface Product {<br />}<br /><br />//或者我也提供一个工厂的接口，由这个抽象类来继承它<br /><br />abstract class Factory {<br />abstract public Product fmd();<br /><br />//我认为这个方方法的存在是，是对FactoryMethod方法的补充<br />//例如可以为生成的对象赋值，计算为生成对象应付何值，前后的日值<br />//且这些都是公用的，生成产品的最主要算法还是在FactoryMethod中，<br />//这个方法只是起辅助作用，这也是一种思维方法，将具体的算法实现在一个方法中<br />//而我不直接调用此方法，而使用另外的一个方法封装它，等到了更灵活的效果，而<br />//子类需实现的内容是FactoryMethod<br />//此方法是一个TemplateMethod<br />public Product creat() {<br />Product pd = null;<br /><br />System.out.println("before operation");<br /><br />pd = fmd();<br /><br />System.out.println("end operation");<br /><br />return pd;<br />}<br />}<br /><br />class Product1 implements Product {<br />}<br /><br />class Factory1 extends Factory {<br />public Product fmd() {<br />Product pd = new Product1();<br />return pd;<br />}<br />}<br /><br />//FactroyMethod 2<br />//这种方式简单实用<br />interface Producta {<br />}<br /><br />interface Factorya {<br />Producta create();<br />}<br /><br />class Producta1 implements Producta {}<br /><br />class Factorya1 implements Factorya {<br />public Producta create() {<br />Producta pda = null;<br />pda = new Producta1();<br />return pda;<br />} <br />}<br /><br />//AbstractFactory<br />//AbstractFactory与FactoryMethod的不同在于AbstractFactory创建多个产品<br />//感觉此模式没有什么大用<br /><br />//当然可以还有更多的接口<br />interface Apda {}<br /><br />interface Apdb {}<br /><br />interface Afactory {<br />Apda createA();<br />Apdb createB();<br />}<br /><br />class Apda1 implements Apda {}<br /><br />class Apdb1 implements Apdb {}<br /><br />//有几个接口就有几个对应的方法<br />class Afactory1 implements Afactory {<br />public Apda createA() {<br />Apda apda = null;<br />apda = new Apda1();<br />return apda;<br />}<br /><br />public Apdb createB() {<br />Apdb apdb = null;<br />apdb = new Apdb1();<br />return apdb;<br />}<br />}<br /><br />//Builder<br />//一个产品的生成分为生成部件和组装部件，不同的产品每个部件生成的方式不同<br />//而组装的方式相同，部件的生成抽象成接口方法，而组装的方法使用一个TemplateMethod方法<br /><br />interface Cpda {}<br /><br />class Cpda1 implements Cpda {}<br /><br />interface BuilderI {<br />void buildPart1();<br />void buildPart2();<br /><br />void initPd();<br />Cpda getPd();<br />}<br /><br />abstract class BuilderA implements BuilderI {<br />Cpda cpda;<br /><br />public Cpda getPd() {<br />initPd();<br /><br />//对对象的内容进行设置<br />buildPart1();<br />buildPart2();<br /><br />return cpda;<br />}<br />}<br /><br />class Builder extends BuilderA {<br />public void buildPart1() {<br />System.out.println(cpda);<br />}<br /><br />public void buildPart2() {<br />System.out.println(cpda);<br />}<br /><br />public void initPd() {<br />cpda = new Cpda1();<br />}<br />}<br /><br />//一个简单的生成产品的实现<br />//1<br />abstract class Fy {<br />public abstract void med1(); <br /><br />static class Fy1 extends Fy {<br />public void med1() {<br />}<br />}<br /><br />public static Fy getInstance() {<br />Fy fy = new Fy1();<br />return fy;<br /><br />//Fy fy = new Fy1() {//这种匿名内部类是静态的！！<br />//public void med1() {<br />//}<br />//};<br />//return fy;<br />}<br />}<br /><br />//2<br />interface Pdd {}<br /><br />class Pdd1 implements Pdd {}<br /><br />abstract class Fya {<br />public static Pdd getPd() {<br />Pdd pdd = new Pdd1();<br />return pdd;<br />}<br />}<br /><br />//Prototype 在java中就是clone，又包含深拷贝和浅拷贝<br />class CloneObja {<br />public CloneObja MyClone() {<br />return new CloneObja();<br />}<br />}<br /><br />class CloneObjb {<br />public CloneObjb MyClone() throws Throwable {<br />CloneObjb cobj = null;<br />cobj = (CloneObjb) pcl(this);<br />return cobj;<br />}<br /><br />//深度拷贝算法<br />private Object pcl(Object obj) throws Throwable {<br />ByteArrayOutputStream bao = new ByteArrayOutputStream(1000);<br />ObjectOutputStream objo = new ObjectOutputStream(bao);<br />objo.writeObject(obj);<br /><br />ByteArrayInputStream bai = new ByteArrayInputStream(bao.toByteArray());<br />ObjectInputStream obji = new ObjectInputStream(bai);<br /><br />Object objr = obji.readObject();<br />return objr;<br />} <br />}<br /><br />//Singleton<br />//一个类只有一个对象，例如一个线程池，一个cache<br />class Singleton1 {<br />public static Singleton1 instance = new Singleton1();<br /><br />private Singleton1() {<br />}<br /><br />public static Singleton1 getInstance() {<br />return instance;<br />}<br />}<br /><br />class Singleton2 {<br />public static Singleton2 instance;<br /><br />private Singleton2() {<br />}<br /><br />//public static Singleton2 getInstance() {<br />//if (instance == null) {<br />//instance = new Singleton2();<br />//}<br />//<br />//return instance;<br />//}<br /><br />public static Singleton2 getInstance() {<br />synchronized(Singleton2.class) {<br />if (instance == null) {<br />instance = new Singleton2();<br />}<br />}<br /><br />return instance;<br />}<br />}<br />//**********结构型模式**********<br /><br />//Adapter<br />//基本方法有两种，一种是使用引用一种使用继承<br />//将不符合标准的接口转成符合标准的接口，接口的修改主要是参数的增减，<br />//返回值类型,当然还有方法名<br />//感觉这就是封装的另一种表示形式，封装有用方法封装(在方法中调用功能方法)，<br />//用类封装(先传入功能方法所在的类的对象，通过调用此对象的功能方法)<br /><br />//使用引用的形式<br />class Adapteea {<br />public void kk() {}<br />}<br /><br />interface Targeta {<br />String vv(int i, int k);<br />}<br /><br />class Adaptera implements Targeta{<br />Adapteea ade;<br /><br />public Adaptera(Adapteea ade) {<br />this.ade = ade;<br />}<br /><br />public String vv(int i, int k) {<br />//具体的业务方法实现在Adaptee中，这个方法<br />//只起到了接口转换的作用<br />//调用此方法是通过引用<br />ade.kk();<br />return null;<br />}<br />}<br /><br />//使用继承形式的<br />class Adapteeb {<br />public void kk() {}<br />}<br /><br />interface Targetb {<br />String vv(int i, int k);<br />}<br /><br />class Adapterb extends Adapteeb implements Targetb {<br />public String vv(int i, int k) {<br />//调用此方法是通过继承<br />kk();<br />return null;<br />}<br />}<br /><br />//Proxy<br />interface Subject {<br />void request();<br />} <br /><br />class realSubject implements Subject {<br />public void request() {<br />//do the real business<br />}<br />}<br /><br />class Proxy implements Subject {<br />Subject subject;<br /><br />public Proxy(Subject subject) {<br />this.subject = subject;<br />}<br /><br />public void request() {<br />System.out.println("do something");<br /><br />subject.request();<br /><br />System.out.println("do something");<br />}<br />}<br /><br />//Bridge<br />//感觉就是多态的实现<br /><br />interface Imp {<br />void operation();<br />}<br /><br />class Cimp1 implements Imp {<br />public void operation() {<br />System.out.println("1");<br />}<br />}<br /><br />class Cimp2 implements Imp {<br />public void operation() {<br />System.out.println("2");<br />}<br />}<br /><br />class Invoker {<br />Imp imp = new Cimp1();<br /><br />public void invoke() {<br />imp.operation();<br />}<br />}<br /><br />//Composite<br /><br />interface Component {<br />void operation();<br /><br />void add(Component component);<br /><br />void remove(Component component);<br />}<br /><br />class Leaf implements Component {<br />public void operation() {<br />System.out.println("an operation");<br />}<br /><br />public void add(Component component) {<br />throw new UnsupportedOperationException();<br />}<br /><br />public void remove(Component component) {<br />throw new UnsupportedOperationException();<br />}<br />}<br /><br />class Composite implements Component {<br />List components = new ArrayList();<br /><br />public void operation() {<br />Component component = null;<br /><br />Iterator it = components.iterator();<br />while (it.hasNext()) {<br />//不知道此component对象是leaf还是composite，<br />//如果是leaf则直接实现操作，如果是composite则继续递归调用<br />component = (Component) it.next();<br />component.operation();<br />}<br />}<br /><br />public void add(Component component) {<br />components.add(component);<br />}<br /><br />public void remove(Component component) {<br />components.remove(component);<br />}<br />}<br /><br />//Decorator<br />//对一个类的功能进行扩展时，我可以使用继承，但是不够灵活，所以选用了<br />//另外的一种形式,引用与继承都可活得对对象的一定的使用能力，而使用引用将更灵活<br />//我们要保证是对原功能的追加而不是修改，否则只能重写方法，或使用新的方法<br />//注意concrete的可以直接new出来，<br />//而decorator的则需要用一个另外的decorator对象才能生成对象<br />//使用对象封装，和公用接口<br />//Decorator链上可以有多个元素<br /><br />interface Componenta {<br />void operation();<br />}<br /><br />class ConcreteComponent implements Componenta {<br />public void operation() {<br />System.out.println("do something");<br />}<br />}<br /><br />class Decorator implements Componenta {<br />private Componenta component;<br /><br />public Decorator(Componenta component) {<br />this.component = component;<br />}<br /><br />public void operation() {<br />//do something before<br /><br />component.operation();<br /><br />//do something after<br />}<br />}<br /><br />//Facade<br />//非常实用的一种设计模式，我可以为外部提供感兴趣的接口<br /><br />class Obj1 {<br />public void ope1() {}<br />public void ope2() {}<br />}<br /><br />class Obj2 {<br />public void ope1() {}<br />public void ope2() {}<br />}<br /><br />class Facade {<br />//我得到了一个简洁清晰的接口<br />public void fdMethod() {<br />Obj1 obj1 = new Obj1();<br />Obj2 obj2 = new Obj2();<br /><br />obj1.ope1();<br />obj2.ope2();<br /> }<br />}<br /><br />//Flyweight<br />//空<br />//**********行为型模式*************<br /><br />//Chain of Responsibility<br />//与Decorator的实现形式相类似，<br />//Decorator是在原来的方法之上进行添加功能，而<br />//Chain则是判断信号如果不是当前处理的则转交个下一个节点处理<br />//我可以使用if分支来实现相同的效果，但是不够灵活，链上的每个节点是可以替换增加的，相对<br />//比较灵活，我们可以设计接口实现对节点的增删操作，而实现更方便的效果<br />//这个是一个链状的结构，有没有想过使用环状结构<br /><br />interface Handler {<br />void handRequest(int signal);<br />}<br /><br />class CHandler1 implements Handler {<br />private Handler handler;<br /><br />public CHandler1(Handler handler) {<br />this.handler = handler;<br />}<br /><br />public void handRequest(int signal) {<br />if (signal == 1) {<br />System.out.println("handle signal 1");<br />}<br />else {<br />handler.handRequest(signal);<br />}<br />} <br />}<br /><br />class CHandler2 implements Handler {<br />private Handler handler;<br /><br />public CHandler2(Handler handler) {<br />this.handler = handler;<br />}<br /><br />public void handRequest(int signal) {<br />if (signal == 2) {<br />System.out.println("handle signal 2");<br />}<br />else {<br />handler.handRequest(signal);<br />}<br />} <br />}<br /><br />class CHandler3 implements Handler {<br />public void handRequest(int signal) {<br />if (signal == 3) {<br />System.out.println("handle signal 3");<br />}<br />else {<br />throw new Error("can't handle signal");<br />}<br />} <br />}<br /><br />class ChainClient {<br />public static void main(String[] args) {<br />Handler h3 = new CHandler3();<br />Handler h2 = new CHandler2(h3);<br />Handler h1 = new CHandler1(h2);<br /><br />h1.handRequest(2);<br />}<br />}<br /><br />//Interpreter<br />//感觉跟Composite很类似，只不过他分文终结符和非终结符<br /><br />//Template Method<br /><br />abstract class TemplateMethod {<br />abstract void amd1();<br /><br />abstract void amd2();<br /><br />//此方法为一个Template Method方法<br />public void tmd() {<br />amd1();<br />amd2();<br />}<br />}<br /><br />//State<br /><br />//标准型<br />//状态和操作不应该耦合在一起<br />class Contexta {<br />private State st;<br /><br />public Contexta(int nst) {<br />changeStfromNum(nst);<br />}<br /><br />public void changeStfromNum(int nst) {<br />if (nst == 1) {<br />st = new CStatea1();<br />}<br />else if (nst == 2) {<br />st = new CStatea2();<br />}<br /><br />throw new Error("bad state");<br />}<br /><br />void request() {<br />st.handle(this);<br />}<br />}<br /><br />interface State {<br />void handle(Contexta context);<br />}<br /><br />class CStatea1 implements State {<br />public void handle(Contexta context) {<br />System.out.println("state 1");<br />//也许在一个状态的处理过程中要改变状态，例如打开之后立即关闭这种效果<br />//context.changeStfromNum(2);<br />}<br />}<br /><br />class CStatea2 implements State {<br />public void handle(Contexta context) {<br />System.out.println("state 2");<br />}<br />}<br /><br />//工厂型<br />//根据状态不通生成不同的state<br /><br />//class StateFactory {<br />//public static State getStateInstance(int num) {<br />//State st = null;<br />//<br />//if (num == 1) {<br />//st = new CStatea1();<br />//}<br />//else if (num == 2) {<br />//st = new CStatea2();<br />//}<br />//<br />//return st;<br />//}<br />//}<br /><br />//Strategy<br />//跟Bridge相类似，就是一种多态的表示<br /><br />//Visitor<br />//双向引用，使用另外的一个类调用自己的方法,访问自己的数据结构<br />interface Visitor {<br />void visitElement(Elementd element);<br />}<br /><br />class CVisitor implements Visitor {<br />public void visitElement(Elementd element) {<br />element.operation();<br />}<br />}<br /><br />interface Elementd {<br />void accept(Visitor visitor);<br /><br />void operation();<br />}<br /><br />class CElementd implements Elementd {<br />public void accept(Visitor visitor) {<br />visitor.visitElement(this);<br />}<br /><br />public void operation() {<br />//实际的操作在这里<br />}<br />}<br /><br />class Clientd {<br />public static void main() {<br />Elementd elm = new CElementd();<br />Visitor vis = new CVisitor();<br /><br />vis.visitElement(elm);<br />}<br />}<br /><br />//Iteraotr<br />//使用迭代器对一个类的数据结构进行顺序迭代<br /><br />interface Structure {<br />interface Iteratora {<br />void first();<br /><br />boolean hasElement();<br /><br />Object next();<br /><br />}<br />}<br /><br />class Structure1 implements Structure {<br />Object[] objs = new Object[100];<br /><br />//使用内部类是为了对Struture1的数据结构有完全的访问权<br />class Iteratora1 implements Iteratora {<br />int index = 0;<br /><br />public void first() {<br />index = 0;<br />}<br /><br />public boolean hasElement() {<br />return index &lt; 100;<br />} <br /><br />public Object next() {<br />Object obj = null;<br /><br />if (hasElement()) {<br />obj = objs[index];<br />index++;<br />}<br /><br />return obj;<br />}<br />}<br />}<br /><br />//Meditor<br /><br />class A1 {<br />public void operation1() {}<br />public void operation2() {}<br />}<br /><br />class A2 {<br />public void operation1() {}<br />public void operation2() {}<br />}<br /><br />class Mediator {<br />A1 a1;<br />A2 a2;<br /><br />public Mediator(A1 a1, A2 a2) {<br />this.a1 = a1;<br />this.a2 = a2;<br /><br />}<br /><br />//如果我想实现这个功能我可能会把他放在A1中<br />//但是这样耦合大，我不想在A1中出现A2对象的引用，<br />//所以我使用了Mediator作为中介<br />public void mmed1() {<br />a1.operation1();<br />a2.operation2();<br />}<br /><br />public void mmed2() {<br />a2.operation1();<br />a1.operation2();<br />}<br />}<br /><br />//Command<br />//我认为就是将方法转换成了类<br /><br />class Receiver {<br />public void action1() {}<br /><br />public void action2() {}<br />}<br /><br />interface Command {<br />void Execute();<br />}<br /><br />class CCommand1 implements Command {<br />private Receiver receiver;<br /><br />public CCommand1(Receiver receiver) {<br />this.receiver = receiver;<br />}<br /><br />public void Execute() {<br />receiver.action1();<br />}<br />}<br /><br />class CCommand2 implements Command {<br />private Receiver receiver;<br /><br />public CCommand2(Receiver receiver) {<br />this.receiver = receiver;<br />}<br /><br />public void Execute() {<br />receiver.action2();<br />}<br />}<br /><br />//Observer<br />//在这里看似乎这个模式没有什么用<br />//但是如果我有一个线程监控Subject，如果Subject的状态<br />//发生了变化，则更改Observer的状态，并出发一些操作，这样就有实际的意义了<br />//Observer与Visitor有相似的地方，都存在双向引用<br />//Subject可以注册很多Observer<br /><br />interface Subjectb {<br />void attach(Observer observer);<br /><br />void detach(Observer observer);<br /><br />void mynotify();<br /><br />int getState();<br /><br />void setState(int state);<br />}<br /><br />class Subjectb1 implements Subjectb {<br />List observers = new ArrayList();<br />int state;<br /><br />public void attach(Observer observer) {<br />observers.add(observer);<br />}<br /><br />public void detach(Observer observer) {<br />observers.remove(observer);<br />}<br /><br />public void mynotify() {<br />Observer observer = null;<br />Iterator it = observers.iterator();<br /><br />while (it.hasNext()) {<br />observer = (Observer) it.next();<br />observer.Update();<br />}<br />}<br /><br />public int getState() {<br />return state;<br />}<br /><br />public void setState(int state) {<br />this.state = state;<br />}<br />}<br /><br />interface Observer {<br />void Update();<br />}<br /><br />class Observer1 implements Observer {<br />Subjectb subject;<br />int state;<br /><br />public Observer1(Subjectb subject) {<br />this.subject = subject;<br />}<br /><br />public void Update() {<br />this.state = subject.getState();<br />}<br /><br />public void operation() {<br />//一些基于state的操作<br />}<br />}<br /><br />//Memento<br />//感觉此模式没有什么大用<br /><br />class Memento {<br />int state;<br /><br />public int getState() {<br />return state;<br />}<br /><br />public void setState(int state) {<br />this.state = state;<br />}<br />}<br /><br />class Originator {<br />int state;<br /><br />public void setMemento(Memento memento) {<br />state = memento.getState();<br />}<br /><br />public Memento createMemento() {<br />Memento memento = new Memento();<br />memento.setState(1);<br />return memento;<br />}<br /><br />public int getState() {<br />return state;<br />}<br /><br />public void setState(int state) {<br />this.state = state;<br />}<br />}<br /><br />class careTaker {<br />Memento memento;<br /><br />public void saverMemento(Memento memento) {<br />this.memento = memento;<br />}<br /><br />public Memento retrieveMemento() {<br />return memento;<br />}<br />}<br /><br />//程序最终还是顺序执行的，是由不通部分的操作拼接起来的<br />//将不同类的代码拼接起来是通过引用实现的，有了引用我就<br />//相当于有了一定访问数据结构和方法的能力，这与写在类内部<br />//差不多，例如我想将一个类中的一个方法抽离出去，因为这个方法依赖与此类的数据和其他方法<br />//直接将代码移走是不行的，但如果我们拥有了此类对象的引用，则与写在此类<br />//内部无异，所以我们拥有了引用就可以将此方法移出<br />public class tt1 {<br />public static void main(String[] args) {<br />}<br />}<br /><img src ="http://www.blogjava.net/zlsunnan/aggbug/64022.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2006-08-17 00:45 <a href="http://www.blogjava.net/zlsunnan/archive/2006/08/17/64022.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts-Logic 标签库 </title><link>http://www.blogjava.net/zlsunnan/archive/2006/01/11/27648.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Wed, 11 Jan 2006 15:27:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2006/01/11/27648.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/27648.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2006/01/11/27648.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/27648.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/27648.html</trackback:ping><description><![CDATA[<P>此标签库可以分为三种类型：条件、循环、转发/重定向。<BR><BR>一.) 条件类型 </P>
<OL>
<LI>logic:empty和logic:notEmpty<BR>logic:empty标签判断脚本变量是否为null，是否是一个空的字符串(长度为0)，是否是一个空的collection或map(调用isEmpty()方法来判断)。logic:notEmpty标签类似。<BR>
<TABLE style="WIDTH: 357px; HEIGHT: 152px" borderColor=#000000 cellSpacing=1 cellPadding=1 bgColor=#ffffcc border=1>
<TBODY>
<TR>
<TD>&nbsp;&lt;logic:empty name="myBean"&gt;<BR>&nbsp;&nbsp; The bean is missing<BR>&nbsp;&lt;/logic:empty&gt;<BR>&nbsp;&lt;logic:notEmpty name="myBean"&gt;<BR>&nbsp;&nbsp; The bean is not missing<BR>&nbsp;&lt;/logic:notEmpty&gt;</TD></TR></TBODY></TABLE>上段代码表示当一个名为myBean的bean在所有的scope中都不存在时，输出The bean is missing；存在的话输出The bean is not missing。<BR>此标签有三个属性：name、property和scope。<BR>
<LI>logic:equal, logic:notEqual, logic:lessThan, logic:greaterThan,logic:lessEqual, and logic:greaterEqual<BR>这堆logic:*equal*标签从名字上就能知道意思，它们使用起来有些笨拙。<BR>有如下属性：name、property、scope、value、cookie、header、parameter。<BR>
<TABLE style="WIDTH: 550px; HEIGHT: 200px" borderColor=#000000 cellSpacing=1 cellPadding=1 bgColor=#ffffcc border=1>
<TBODY>
<TR>
<TD>&nbsp;&lt;logic:equal name="bean" property="doubleProperty" value="&lt;%= doub1 %&gt;"&gt;<BR>&nbsp;&nbsp;&nbsp; equal<BR>&nbsp;&lt;/logic:equal&gt;<BR>&nbsp;&lt;logic:greaterEqual name="bean" property="doubleProperty" value="&lt;%= doub1 %&gt;"&gt;<BR>&nbsp;&nbsp;&nbsp; greaterEqual<BR>&nbsp;&lt;/logic:greaterEqual&gt;&nbsp;</TD></TR></TBODY></TABLE><BR>
<LI>logic:present和logic:notPresent<BR>检查header、request parameter、cookie、JavaBean或JavaBean propertie是否存在且不等于null。另外还可以检查当前用户的身份，使用属性user和role。<BR>其它的属性有：name、property、scope、cookie、header、parameter。<BR>
<TABLE style="WIDTH: 553px; HEIGHT: 80px" borderColor=#000000 cellSpacing=1 cellPadding=1 bgColor=#ffffcc border=1>
<TBODY>
<TR>
<TD>&nbsp;&lt;logic:notPresent name="myBean" &nbsp;property="prop"&nbsp; scope="page"&gt;<BR>&nbsp;The bean property bean.prop is present<BR>&nbsp;&lt;/logic:notPresent&gt;</TD></TR></TBODY></TABLE>上段代码检查在page作用域内名为myBean的bean是否有一个prop属性。<BR>
<LI>logic:match和logic:notMatch<BR>比较两字符串是否相等，可以比较字符串的开始的、结尾的或其中的某个部分。<BR>location属性：指定从某个位置开始进行比较。<BR>其它的属性有：name、property、scope、value、cookie、header、parameter。<BR>下面的例子是检查浏览器类型：<BR>
<TABLE style="WIDTH: 436px; HEIGHT: 152px" borderColor=#000000 cellSpacing=1 cellPadding=1 bgColor=#ffffcc border=1>
<TBODY>
<TR>
<TD>&nbsp;&lt;logic:match header="User-Agent" value="Mozilla"&gt;<BR>&nbsp; Mozilla!<BR>&nbsp;&lt;/logic:match&gt;<BR>&nbsp;&lt;logic:notMatch header="User-Agent" value="Mozilla"&gt;<BR>&nbsp; Not Mozilla :(<BR>&nbsp;&lt;/logic:notMatch&gt;</TD></TR></TBODY></TABLE><BR>检查bean属性是否匹配"hello world"字符串：<BR>
<TABLE style="WIDTH: 550px; HEIGHT: 200px" borderColor=#000000 cellSpacing=1 cellPadding=1 bgColor=#ffffcc border=1>
<TBODY>
<TR>
<TD>&nbsp;&lt;logic:match name="bean" property="stringProperty" value="hello world"&gt;<BR>&nbsp; Hello World!<BR>&nbsp;&lt;/logic:match&gt;<BR>&nbsp;&lt;logic:notMatch name="bean" property="stringProperty" value="hello world"&gt;<BR>&nbsp; I'm so sad and lonely.<BR>&nbsp;&lt;/logic:notMatch&gt;&nbsp;</TD></TR></TBODY></TABLE><BR>检查名为"name"的请求参数是否是"xyz"的子字符串，但是子字符串必须从"xyz"的索引位置1开始(也就是说子字符串必须是"y"或"yz")：<BR>
<TABLE style="WIDTH: 442px; HEIGHT: 128px" borderColor=#000000 cellSpacing=1 cellPadding=1 bgColor=#ffffcc border=1>
<TBODY>
<TR>
<TD>&nbsp;&lt;logic:match parameter="name" &nbsp;value="xyz"&nbsp; location="1"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The parameter name is a sub-string of the string xyz from index 1<BR>&nbsp;&lt;/logic:match&gt;</TD></TR></TBODY></TABLE></LI></OL>
<P><FONT color=#000000>二.) 循环类型</FONT></P>
<BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px">
<P><FONT color=#000000>logic:iterate<BR>重复嵌套在此标签体内的指定集合的元素，集合必须为如下类型：array&nbsp;、Collection(包括ArrayList和Vector）、Enumeration、Iterator、Map(包括HashMap、Hashtable和TreeMap)。例如：<BR></FONT>
<TABLE style="WIDTH: 547px; HEIGHT: 248px" borderColor=#000000 cellSpacing=1 cellPadding=1 bgColor=#ffffcc border=1>
<TBODY>
<TR>
<TD>&nbsp;&lt;logic:iterate id="employee" name="department" property="employees" scope= "request"&gt;<BR>…<BR>&nbsp;&lt;bean:write name="employee" property="username" /&gt;<BR>…<BR>&nbsp;&lt;bean:write name="employee" property="name" /&gt;<BR>…<BR>&nbsp;&lt;bean:write name="employee" property="phone" /&gt;<BR>…<BR>&nbsp;&lt;/logic:iterate&gt;&nbsp;</TD></TR></TBODY></TABLE><FONT color=#000000>上面的代码迭代了一个employee的集合，是department属性employees的一个集合，作用域为request。<BR><BR>为了输出第五个到第十个employee，需要使用length和offset属性：<BR></FONT>
<TABLE style="WIDTH: 549px; HEIGHT: 248px" borderColor=#000000 cellSpacing=1 cellPadding=1 bgColor=#ffffcc border=1>
<TBODY>
<TR>
<TD>&nbsp;&lt;logic:iterate id="employee" name="department" property="employees" scope= "request" <STRONG>length</STRONG>="10" <STRONG>offset</STRONG>="5"&gt;<BR>…<BR>&nbsp;&lt;bean:write name="employee" property="username" /&gt;<BR>…<BR>&nbsp;&lt;bean:write name="employee" property="name" /&gt;<BR>…<BR>&nbsp;&lt;bean:write name="employee" property="phone" /&gt;<BR>…<BR>&nbsp;&lt;/logic:iterate&gt;</TD></TR></TBODY></TABLE><BR>&nbsp;<FONT color=#000000>你可以定义一个变量保存当前的索引值：&nbsp;&nbsp;&nbsp;&nbsp;</FONT>&nbsp;&nbsp; <BR>
<TABLE style="WIDTH: 548px; HEIGHT: 248px" borderColor=#000000 cellSpacing=1 cellPadding=1 bgColor=#ffffcc border=1>
<TBODY>
<TR>
<TD>&nbsp;&lt;ol&gt;<BR>&nbsp;&lt;logic:iterate id="element" name="bean" property="stringArray" <STRONG>indexId</STRONG>="index"&gt;<BR>&nbsp;&lt;li&gt;<BR>&nbsp;&lt;em&gt;<BR>&nbsp;&lt;bean:write name="element"/&gt;<BR>&nbsp;&lt;/em&gt;&amp;nbsp;<BR>&nbsp;[&lt;bean:write name="index"/&gt;]&lt;/li&gt;<BR>&nbsp;&lt;/logic:iterate&gt;<BR>&nbsp;&lt;/ol&gt;&nbsp;</TD></TR></TBODY></TABLE></P></BLOCKQUOTE>
<P><FONT color=#000000>三.) 转发/重定向类型</FONT></P>
<BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px">
<P><FONT color=#000000>logic:forward和logic:redirect<BR>logic:forward标签和jsp:forward标签和相似，但它可以使用global forward中的一个ActionForward：</FONT>&nbsp; 
<TABLE style="WIDTH: 548px; HEIGHT: 177px" borderColor=#000000 cellSpacing=1 cellPadding=1 bgColor=#ffffcc border=1>
<TBODY>
<TR>
<TD>&nbsp;&lt;logic:forward name="login" /&gt;<BR>----------------------------------------------------------------------------------------------------------------------------------<BR>&nbsp;与上面相关的global forward中的代码：<BR>&nbsp;&lt;global-forwards&gt;<BR>&nbsp;&lt;forward name="login" path="/loginForm.jsp"/&gt;<BR>&nbsp;&lt;/global-forwards&gt;</TD></TR></TBODY></TABLE><BR><FONT color=#000000>logic:redirect标签和上面的标签类似，但它默认调用的方法是response.sendRedirect()，取代了上面的requestDispatcher.forward()。最大的不同是它支持所有html:link标签的属性，所以你能够指</FONT><FONT color=#000000>定request参数：</FONT><BR>
<TABLE style="WIDTH: 471px; HEIGHT: 56px" borderColor=#000000 cellSpacing=1 cellPadding=1 bgColor=#ffffcc border=1>
<TBODY>
<TR>
<TD>&nbsp;&lt;logic:redirect name="login" paramId="employeeId" paramName="employee" property="id" /&gt;</TD></TR></TBODY></TABLE></P></BLOCKQUOTE><img src ="http://www.blogjava.net/zlsunnan/aggbug/27648.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2006-01-11 23:27 <a href="http://www.blogjava.net/zlsunnan/archive/2006/01/11/27648.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>检查客户端的Cookie设置</title><link>http://www.blogjava.net/zlsunnan/archive/2005/12/06/22672.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Tue, 06 Dec 2005 02:15:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2005/12/06/22672.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/22672.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2005/12/06/22672.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/22672.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/22672.html</trackback:ping><description><![CDATA[<P><FONT face=Arial color=#000000 size=4>&lt;</FONT><A href="mailto:%@page"><FONT face=Arial color=#000000 size=4>%@page</FONT></A><FONT face=Arial color=#000000 size=4> contentType="text/html; charset=GBK"%&gt;&lt;%<BR>//判断客户端是否打开Cook<BR>&nbsp; if (request.getParameter("flag") == null)<BR>&nbsp; {<BR>&nbsp;&nbsp;&nbsp; // the first request<BR>&nbsp;&nbsp;&nbsp; Cookie cookie = new Cookie("cookieSetting","on");<BR>&nbsp;&nbsp;&nbsp; response.addCookie(cookie);<BR>&nbsp;&nbsp;&nbsp; String nextUrl = request.getRequestURI() +"?flag=1";<BR>&nbsp;&nbsp;&nbsp; // force the browser to refresh<BR>&nbsp;&nbsp;&nbsp; out.println(nextUrl);<BR>&nbsp;&nbsp;&nbsp; //out.println("&lt;META HTTP-EQUIV=Refresh CONTENT = 0; URL = " + nextUrl +" &gt; ");<BR>&nbsp; }<BR>&nbsp; else<BR>&nbsp; {<BR>&nbsp;&nbsp;&nbsp; // the second request<BR>&nbsp;&nbsp;&nbsp; Cookie[] cookies = request.getCookies();<BR>&nbsp;&nbsp;&nbsp; boolean cookieFound = false;<BR>&nbsp;&nbsp;&nbsp; if (cookies != null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int length = cookies.length;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; length; i++) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Cookie cookie = cookies[i];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (cookie.getName().equals("cookieSetting") &amp;&amp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cookie.getValue().equals("on")) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cookieFound = true;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; if (cookieFound) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("Cookie is on.");<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("Cookie is off.");<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp; }<BR>%&gt;<BR></FONT></P><img src ="http://www.blogjava.net/zlsunnan/aggbug/22672.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2005-12-06 10:15 <a href="http://www.blogjava.net/zlsunnan/archive/2005/12/06/22672.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>限制对Web资源的访问 </title><link>http://www.blogjava.net/zlsunnan/archive/2005/12/06/22665.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Tue, 06 Dec 2005 02:00:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2005/12/06/22665.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/22665.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2005/12/06/22665.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/22665.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/22665.html</trackback:ping><description><![CDATA[<P xmlns:fo="http://www.w3.org/1999/XSL/Format">开发人员遇到的一个老问题是：如何使资源不完全暴露在大庭广众下，而只让那些适当的人和程序有完全的权限来访问他们需要的资源？至少有三个好方法可以解决这个问题。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">作为一个Java Web开发人员，你可能已经对Web应用程序的目录结构很熟悉了（见<A href="javascript:openWindowRes('Java/2002_09/xml2html.asp?xmlfile=RestrictAccessWebR/Figure1.xml&amp;xslfile=../../include/xsl/Figure.xsl');">图1</A>）。在WEB-INF/classes目录下放置了servlet类，在WEB-INF/lib下是Java档案文件，如HTML和图片文件的静态资源直接放在应用程序目录下（或者在应用程序目录下的任何子目录中）。例如，所有图片文件都放在图片目录中（见<A href="javascript:openWindowRes('Java/2002_09/xml2html.asp?xmlfile=RestrictAccessWebR/Figure1.xml&amp;xslfile=../../include/xsl/Figure.xsl');">图1</A>）。JSP页面也放在应用程序目录中。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">
<TABLE align=right>
<TBODY>
<TR>
<TD><IMG alt="&#13;&#10;&#9;&#9;&#9;&#9;图1. " src="http://www.fawcette.com/China/Java/2002_09/RestrictAccessWebR/Image/fig1sm.gif" align=right></IMG></TD></TR>
<TR>
<TD>
<CENTER>图1. <A href="javascript:openWindowRes('Java/2002_09/xml2html.asp?xmlfile=RestrictAccessWebR/Figure1.xml&amp;xslfile=../../include/xsl/Figure.xsl');">一个 Servlet/JSP </A><BR><A href="javascript:openWindowRes('Java/2002_09/xml2html.asp?xmlfile=RestrictAccessWebR/Figure1.xml&amp;xslfile=../../include/xsl/Figure.xsl');">应用程序的目录结构</A></CENTER></TD></TR></TBODY></TABLE>幸运的是，放在WEB-INF目录下的资源是安全的。用户不可以简单地在浏览器的Location或Address中输入一个URL来调用它。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">这就是为什么许多程序员都把他们的资源文件（如果它们是和应用程序放在一起的话）放在WEB-INF目录中的原因。WEB-INF之外的任何资源都可以通过输入URL来查看。HTML文件和JSP文件一般都会被调用，所以它们通常存储在WEB-INF之外。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">然而，你可能想限制对WEB-INF目录之外的文件的访问。你可以用三种方法：通过运用referer HTTP request header（是“referer”，不是“referrer”）；通过检查用户的session对象中的一个属性；或者通过将那些资源放在WEB-INF中，并在适当的时候参照它们。下面是运用这三种方法的指南。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><B><FONT color=#333399>运用Referer HTTP Request Header</FONT></B><BR>Referer HTTP request header指定了一个URI (Uniform Resource Identifier)，该URI包含链接到被请求资源的页面的地址。例如，下面是对一个叫做myPage1.jsp的JSP页面的请求，该页面来自一个叫做Login.jsp的文件： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>http://domainName/appName/Login.jsp</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><STRONG><U><FONT size=5>如果直接在Web浏览器的Address或Location中输入URL来调用myPage1.jsp，就不会有一个referer header。</FONT></U></STRONG></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">下面的例子运用了一个叫做DisplayRequestHeaders.jsp的JSP页面： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>&lt;%@ page import="java.util.Enumeration" %&gt;
&lt;%
  Enumeration headers = request.getHeaderNames();
  while (headers.hasMoreElements()) {
    String header = (String) 
headers.nextElement();
    out.println(header + ":" + 
request.getHeader(header) + "&lt;br&gt;");
  }
%&gt;
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">如果你直接将URL输入到浏览器的Address或Location来调用这个页面，结果如下： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>accept:*/*
accept-language:en-us
accept-encoding:gzip, deflate
user-agent:Mozilla/4.0 (compatible; MSIE 6.0; 
Windows NT 5.0; .NET CLR 1.0.3705)
host:localhost:8080?connection:Keep-Alive
cookie:JSESSIONID=65D28447DFE4F58D1D806EAA933E9DD7
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">然而，如果通过点击另一个页面（如Login.jsp）中的一个链接来请求DisplayRequestHeaders.jsp，你可以看到如下的结果： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>accept:image/gif, image/x-xbitmap, image/jpeg, 
image/pjpeg, application/vnd.ms-excel, 
application/msword, application/vnd.ms-powerpoint, 
*/*<BR><B>referer:http://localhost:8080/myJSPApp/Login.jsp</B><BR>accept-language:en-us
accept-encoding:gzip, deflate
user-agent:Mozilla/4.0 (compatible; MSIE 6.0; 
Windows NT 5.0; .NET CLR 1.0.3705)
host:localhost:8080
connection:Keep-Alive
cookie:JSESSIONID=65D28447DFE4F58D1D806EAA933E9DD7
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">注意两者的主要区别是在第二个结果中出现了referer header。因此，如果你的应用程序规定，当你直接将URL输入到浏览器的Location或Address中来调用一个资源时，你不能看到这个资源（在这个例子中，就是myPage1.jsp），那么你可以把这个代码添加到myPage1.jsp的顶部： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>if (request.getHeader("referer")==null)
    response.sendRedirect(somewhereElse);
 </PRE></FONT></TD></TR></TBODY></TABLE>其中somewhereElse是你想让用户进入的页面的URL。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">或者，如果你想确信资源来自一个特定的URL，可以在前面的代码后面添加下面的代码： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>if (request.getHeader("referer")==null)
    response.sendRedirect(somewhereElse);
  if (request.getHeader("referer")!=aURL)
    response.sendRedirect(somewhereElse);
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">然而，你需要注意，运用referer header只适合于简单的控制流管理。因为它依赖于来自用户的request headers，所以它并不是100%的安全。了解socket programming的聪明的用户常常可以确信有一个包含期望值的referer header。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><B><FONT color=#333399>检查一个Session对象的属性</FONT></B><BR>另一种限制对一个特定页面的访问的方法就是通过检查用户的session对象中出现的一个特定的属性。例如，如果你想让ABC.jsp页面只能被登录后的用户看到，你可以在用户成功登录后，在用户session对象中设置一个叫做loggedIn的属性。然后在ABC.jsp页面的顶部，你可以查看loggedIn属性是否出现在用户的session对象中： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>&lt;%
  if (session.getAttribute("loggedIn")==null) {
%&gt;
    &lt;jsp:forward page="Login.jsp"/&gt;
&lt;%
  }
  else {
%&gt; 
display the page content here.
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">这种方法的缺点是你在每一页都需要额外的代码，而且还有另外的维护工作。更重要的是，受限制的页面需要参预session管理，而这正是你在一些应用程序中可能想避免的事情。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><B><FONT color=#333399>在WEB-INF目录下存储资源</FONT></B><BR>在你不想让用户调用你的JSP页面时，第三种方法——将资源放在WEB-INF下——会很有用。实现Model 2结构的应用程序和Struts应用程序运用JSP页面作为Model-View-Controller中的View。这些JSP页面并不打算让用户从浏览器直接调用。作为替代，它们从控制器servlet内部来调用。对这些应用程序来说，将JSP页面放在WEB-INF目录外——就像许多程序员所做的那样——需要你添加额外的代码来限制对这些资源的直接访问。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">另一方面，把它们放在WEB-INF中可以保证它们只能从那个应用程序的一个servlet来访问。但如果你决定把JSP页面存在WEB-INF中，你就需要知道如何参照这些页面。幸运的是，这并不难。做法如下。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">通过运用一个RequestDispatcher对象，一个servlet可以包含（include）或转送（forward）一个JSP页面。下面的例子forward并include一个叫做Included.jsp的JSP页面： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>  RequestDispatcher rd = 
    request.getRequestDispatcher("/WEB-
    INF/Included.jsp");
  rd.forward(request, response);
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">但有时侯，你需要显示的是HTML文件，而不是JSP页面。在一些Web容器（containers）中，前面代码中的request dispatchers并不能用于静态的资源（如果静态资源不在WEB-INF下，它可以用）。对这些资源来说，你可以运用javax.servlet.ServletContext接口的getResourceAsStream方法。该方法的定义如下： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>public java.io.InputStream 
getResourceAsStream(java.lang.String path)
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">要运用这个方法，你需要把一个路径传送到你想包含的静态资源。该方法返回一个InputStream。然后，你可以用InputStream的read方法来得到静态资源的内容。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">例如，下面的这个JSP页面包含一个叫做getResourceContent的函数，你可以用它以字符串形式返回一个静态资源的内容： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>&lt;%@ page import="java.io.InputStream"%&gt;
&lt;%!
  public String getResourceContent(InputStream in) {
    if (in==null)
      return null;

    StringBuffer sb = new StringBuffer(2048);
    try {
      int i = in.read();
      while (i != -1) {
        sb.append((char)i);
        i = in.read();
      }
      in.close();
    }
    catch (Exception e) {
    }
    return sb.toString();
  }
%&gt;

&lt;html&gt;
&lt;head&gt;
&lt;title&gt;
&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;br&gt;
&lt;%
  String path = "/WEB-INF/header.html";
  InputStream in = 
application.getResourceAsStream(path);
  out.println(getResourceContent(in));
%&gt;
&lt;/body&gt;
&lt;/html&gt;
 </PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">注意，在JSP页面中，应用程序暗示的对象代表ServletContext对象。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">该JSP页面显示了如何包含位于WEB-INF目录中的header.html文件的内容。如果你把header.html文件放在这儿，它就不能直接从浏览器调用。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">通过运用一些缓冲策略，你可以优化JSP页面中的getResourceContent方法。在这里，我只是解释了得到放在WEB-INF目录下的一个静态资源的内容的方法。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">你可以从一个servlet内部，也可以从一个JSP页面运用getResourceContent方法。从一个JSP页面，你可以运用指示符include，如下面的代码所示： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>&lt;%@ include file="/WEB-INF/header.html" %&gt;
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">现在你应该了解了：你有三种不同的方法来保护你的资源，以及如何实现这些方法。</P><BR xmlns:fo="http://www.w3.org/1999/XSL/Format"><img src ="http://www.blogjava.net/zlsunnan/aggbug/22665.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2005-12-06 10:00 <a href="http://www.blogjava.net/zlsunnan/archive/2005/12/06/22665.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>客户端重定向</title><link>http://www.blogjava.net/zlsunnan/archive/2005/12/06/22658.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Tue, 06 Dec 2005 01:41:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2005/12/06/22658.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/22658.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2005/12/06/22658.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/22658.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/22658.html</trackback:ping><description><![CDATA[你不能在所有情况下都运用服务器端重定向。至少在两种情况下你需要运用客户端重定向：
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">
<TABLE cellSpacing=5>
<TBODY>
<TR>
<TD>
<TD>· 当你想延迟重定向时，你必须运用客户端重定向。服务器端重定向是立即出现的，因此，举例来说，你就不能用服务器端重定向技术来创建一个闪屏（splash screen）。</TD></TR></TBODY></TABLE>
<TABLE cellSpacing=5>
<TBODY>
<TR>
<TD>
<TD>· 如果你没有运用诸如servlets或JSP的服务器端处理技术，那么你只能用客户端重定向来引导你的网站访问者。</TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">关于客户端重定向，你可以用Refresh metatag或JavaScript。下面我分别讲述这两种方法。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><B><FONT color=#333399>运用Refresh Metatag</FONT></B><BR>Metatags比JavaScript容易。在一个页面已经显示了一段时间后，你可以用它们来把用户从这个页面引导开： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>&lt;META HTTP-EQUIV="Refresh" 
CONTENT="t;URL=differentURL"&gt; 
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">t是在重定向前，浏览器将等待的秒数。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">例如，下面的脚本在页面显示了三秒后，将浏览器重定向到http://www.brainysoftware.com。 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;Client side redirection using the 
meta tag &lt;/TITLE&gt;
&lt;META HTTP-EQUIV="Refresh" 
CONTENT="3;URL=http://www.brainysoftware.com"&gt; 
&lt;/HEAD&gt;
&lt;BODY&gt;
You will be redirected in 3 seconds. For now, 
relax and enjoy.
&lt;/BODY&gt;
&lt;/HTML&gt;
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">当t=0时，不会出现延迟。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">该技术可以用于Netscape Navigator 3及更高版本，Internet Explorer 4及更高版本。<BR></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><B><FONT color=#333399>运用JavaScript</FONT></B><BR>在运用metatag时，你的局限性很大，因为一些老的浏览器读不懂它。如果你担心这个问题，那么你可以用JavaScript。然而，如果用户浏览器不支持JavaScript，你还是会有问题。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">要用JavaScript进行客户端重定向，运用定位对象（location object）： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>&lt;SCRIPT LANGUAGE="JavaScript"&gt;
location=differentURL
&lt;/SCRIPT&gt;
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">differentURL是浏览器被重新定向到的新的URL。下面的例子显示如何运用定位对象来重定向： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;Client-side redirection using 
JavaScript&lt;/TITLE&gt;
&lt;SCRIPT LANGUAGE="JavaScript"&gt;
location='http://www.brainysoftware.com';
&lt;/SCRIPT&gt;
&lt;/HEAD&gt;
&lt;BODY&gt;
You will be redirected to another page. For now, 
enjoy and relax.
&lt;/BODY&gt;
&lt;/HTML&gt;
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">同运用metatags一样，你可以用JavaScript来延迟重定向。运用setTimeout方法来实现这一点： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>setTimeout("location='differentURL'", t);
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">t是用户被重定向到differentURL前的毫秒数。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">下面的代码在5秒后将用户重定向到www.brainysoftware.com： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;Client-side redirection using 
JavaScript&lt;/TITLE&gt;
&lt;SCRIPT LANGUAGE="JavaScript"&gt;
  setTimeout("location=
  'http://www.brainysoftware.com'", 5000);
&lt;/SCRIPT&gt;
&lt;/HEAD&gt;
&lt;BODY&gt;
Hello, you will be redirected in 5 seconds. Start 
counting now...
&lt;/BODY&gt;
&lt;/HTML&gt;
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">该技术可以用于Netscape Navigator 2及更高版本，Internet Explorer 3及更高版本。<BR></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><B><FONT color=#333399>预见不成功的重定向</FONT></B><BR>每种技术都有缺点。metatags不能用于老的浏览器，如果浏览器不支持JavaScript，那么JavaScript就不能用。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">所以在重定向用户时，你常常要预料到失败的情况。当自动的重定向失败时，用户必须点击一个链接，使他或她可以进入期望的地址。所以需要常发送这个链接，如下例所示： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;When redirection fails . . .
&lt;/TITLE&gt;
&lt;SCRIPT LANGUAGE="JavaScript"&gt;
  setTimeout("location=
  'http://www.brainysoftware.com'", 5000);
&lt;/SCRIPT&gt;
&lt;/HEAD&gt;
&lt;BODY&gt;
Click &lt;A HREF="RedirectionURL"&gt;here&lt;/A&gt; if you do 
not get redirected in 5 seconds.
&lt;/BODY&gt;
&lt;/HTML&gt;
</PRE></FONT></TD></TR></TBODY></TABLE></P><img src ="http://www.blogjava.net/zlsunnan/aggbug/22658.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2005-12-06 09:41 <a href="http://www.blogjava.net/zlsunnan/archive/2005/12/06/22658.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>服务器端重定向</title><link>http://www.blogjava.net/zlsunnan/archive/2005/12/06/22657.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Tue, 06 Dec 2005 01:39:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2005/12/06/22657.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/22657.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2005/12/06/22657.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/22657.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/22657.html</trackback:ping><description><![CDATA[<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><BR>&nbsp;&nbsp;&nbsp;&nbsp; 重定向（redirection）是Web编程中的一个重要的技术。运用重定向，你可以把控件发送到另一个servlet/JSP页面，或将Web浏览器（用户）重定向到一个新的URL。然而，将用户重定向到同一个页面也是很常见的。例如，如果要查看浏览器的cookie support是否激活，你可以把一个cookie发送到浏览器，然后将浏览器重定向到同一个页面。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">重定向可以在服务器端进行，也可以在客户端（浏览器）进行。因为在servlet/JSP页面上有服务器端的代码，所以可以在服务器端进行重定向。客户端的重定向可以通过发送JavaScript代码或者传送到浏览器的HTML页面的元数据来完成。现在，让我们来看看两个服务器端重定向的技巧。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">在servlet/JSP编程中，服务器端重定向可以通过下面两个方法来实现：运用javax.servlet.RequestDispatcher接口的forward方法，或者运用javax.servlet.http.HttpServletResponse接口的sendRedirect方法。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><B><FONT color=#333399>运用RequestDispatcher接口的Forward方法</FONT></B><BR>要运用RequestDispatcher接口的forward方法，首先要得到一个RequestDispatcher对象。servlet技术提供了三种方式来得到它： 
<TABLE cellSpacing=5>
<TBODY>
<TR>
<TD>
<TD>1. 通过运用javax.servlet.ServletContext接口的getRequestDispatcher方法，将一个包含路径的String传递给其它资源。该路径是相对于ServletContext的根路径的。</TD></TR></TBODY></TABLE>
<TABLE cellSpacing=5>
<TBODY>
<TR>
<TD>
<TD>2. 通过运用javax.servlet.ServletRequest接口的getRequestDispatcher方法，将一个包含路径的String传递到其它资源。该路径是相对于当前的HTTP请求的。</TD></TR></TBODY></TABLE>
<TABLE cellSpacing=5>
<TBODY>
<TR>
<TD>
<TD>3. 通过运用javax.servlet.ServletContext接口的getNamedDispatcher方法，传递一个包含其它资源名字的String。</TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">在得到RequestDispatcher对象后，运用forward方法就很容易了。forward方法的定义如下： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>public void forward(javax.servlet.ServletRequest request, 
javax.servlet.ServletResponse response)
throws javax.servlet.ServletException, java.io.IOException</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">但要注意，你只有在客户端没有输出时才可以调用forward方法。如果当前页面的缓冲区（buffer）不是空的，那么你在调用forward方法前必须先清空缓冲区。否则，会抛出一个IllegalStateException。forward方法也可以用来将请求发送到一个静态的页面。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">servlet/JSP的初学者在尝试得到一个RequestDispatcher对象时，常感到困惑，这是因为ServletContext接口的getRequestDispatcher方法和ServletRequest接口的getRequestDispatcher方法有很大的不同。接着阅读本文你就可以得到一些技巧来避免将这两者混淆在一起了。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">在运用RequestDispatcher对象的forward方法来将一个控件从一个叫做ABCServlet的servlet传递到另一个叫做XYZServlet的servlet时，最简单的方法就是把ABCServlet和XYZServlet的类文件放在同一个目录中。通过这种方法，你就可以从URL http://domain/VirtualDir/servlet/ABCServlet来调用ABCServlet，从URL http://domain/VirtualDir/servlet/XYZServlet来调用XYZServlet了。然后运用forward方法就很简单了。你可以从ServletRequest接口运用getRequestDispatcher ,传递第二个servlet的名字。在ABCServlet中，你可以写下面的代码： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>  RequestDispatcher rd = 
request.getRequestDispatcher("SecondServlet");
  rd.forward(request, response);
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">你不需要在XYZServlet前放 / 符号。这种方法是最简单的，因为你根本不需要担心两个servlets的路径。 </P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">稍复杂的方法就是把下面这个String传递到ServletRequest的getRequestDispatcher： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>"/servlet/XYZServlet"</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">如果你必须调用一个从ServletContext的getRequestDispatcher得到的RequestDispatcher对象的forward方法，你需要将“/VirtualDir/servlet/XYZServlet”作为路径参数来传递，如下： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>  RequestDispatcher rd =
    getServletContext().getRequestDispatcher(
	   "/servlet/XYZServlet");
  rd.forward(request, response);
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">要运用getNamedDispatcher方法，你的代码就会变成： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>  RequestDispatcher rd =
    getServletContext().getNamedDispatcher(
	   "XYZServlet");
  rd.forward(request, response);</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">在运用getNamedDispatcher方法时，你必须在部署描述符中注册第二个servlet。下面是个例子： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>  &lt;web-app&gt;
  &lt;servlet&gt;
    &lt;servlet-name&gt;ABCServlet&lt;/servlet-name&gt;
    &lt;servlet-class&gt;ABCServlet&lt;/servlet-class&gt;
  &lt;/servlet&gt;
  &lt;servlet&gt;
    &lt;servlet-name&gt;XYZServlet&lt;/servlet-name&gt;
    &lt;servlet-class&gt;XYZServlet&lt;/servlet-class&gt;
  &lt;/servlet&gt;
&lt;/web-app&gt;
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">如果你改变了所含的servlet，你需要重新启动Web container以使改变生效。这是因为所包含的servlet从来都不是直接调用的。一旦加载了所包含的servlet，它的时间戳（time stamp）就不会改变了。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">如果你从一个JSP页面发送控件，你也可以用&lt;jsp:forward&gt;执行元素，它会终止当前JSP页面的执行，并将控件传递到另一个资源。它的语法如下： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>&lt;jsp:forward page="relativeURL"/&gt;</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">例如，在JSP页面被解析后，&lt;jsp:forward page=”OtherPage.jsp”/&gt;在结果servlet中就转换成了下面的代码： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>pageContext.forward("OtherPage.jsp");</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><BR xmlns:fo="http://www.w3.org/1999/XSL/Format"></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><B><FONT color=#333399>运用HttpServletResponse接口的sendRedirect方法</FONT></B><BR>sendRedirect方法比forward方法要容易。其定义如下： 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>public void sendRedirect(java.lang.String location)
   throws java.iio.IOException
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">该方法把一个命令发送到浏览器，让浏览器对在location中指定的URL提出请求。该方法可以接受绝对的或相对的URLs。如果传递到该方法的参数是一个相对的URL，那么Web container在将它发送到客户端前会把它转换成一个绝对的URL。如果地址是相对的，没有一个’/’，那么Web container就认为它是相对于当前的请求URI的。</P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format">例如，你可以用下面的代码将用户重定向到www.brainysoftware.com: 
<TABLE bgColor=#ffffaa>
<TBODY>
<TR>
<TD><FONT color=#0000cc><PRE>response.sendRedirect(http://www.brainysoftware.com);
</PRE></FONT></TD></TR></TBODY></TABLE></P>
<P xmlns:fo="http://www.w3.org/1999/XSL/Format"><STRONG><FONT face=Arial color=#000000 size=5><U>你应该运用哪种技巧？<BR>为了编写最有效的代码，你应该了解这两种重定向技巧的不同。forward方法是在Web container内部工作的。sendRedirect方法需要到客户端的一个往返。所以forward方法比sendRedirect要快。但是，运用forward方法有局限性，你只能重定向到同一个Web应用程序中的一个资源。而sendRedirect方法可以让你重定向到任何URL。结论：如果可以解决你的问题，那么就用forward方法。只有当你不能用forward方法时才运用sendRedirect方法。</U></FONT></STRONG></P><img src ="http://www.blogjava.net/zlsunnan/aggbug/22657.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2005-12-06 09:39 <a href="http://www.blogjava.net/zlsunnan/archive/2005/12/06/22657.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts 例子</title><link>http://www.blogjava.net/zlsunnan/archive/2005/12/06/22654.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Tue, 06 Dec 2005 01:21:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2005/12/06/22654.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/22654.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2005/12/06/22654.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/22654.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/22654.html</trackback:ping><description><![CDATA[<SPAN id=ArticleContent1_ArticleContent1_lblContent>&nbsp; 
<LI>查看:<A class=postlink href="http://www.exadel.com/downloads/struts/examples/WebFlowPic/blank.htm" target=_blank><FONT color=#558ab6>[设计图]</FONT></A> 下载: <A class=postlink href="http://www.exadel.com/downloads/struts/examples/struts-blank.war" target=_blank><FONT color=#005c9d>struts-blank.war</FONT></A> <BR>
<LI>查看:<A class=postlink href="http://www.exadel.com/downloads/struts/examples/WebFlowPic/example.htm" target=_blank><FONT color=#005c9d>[设计图]</FONT></A> 下载:<A class=postlink href="http://www.exadel.com/downloads/struts/examples/struts-example.war" target=_blank><FONT color=#005c9d>struts-example.war</FONT></A> <BR>
<LI>查看:<A class=postlink href="http://www.exadel.com/downloads/struts/examples/WebFlowPic/exercise_taglib.htm" target=_blank><FONT color=#005c9d>[设计图]</FONT></A> 下载:<A class=postlink href="http://www.exadel.com/downloads/struts/examples/struts-exercise-taglib.war" target=_blank><FONT color=#005c9d>struts-exercise-taglib.war</FONT></A> <BR>
<LI>查看:<A class=postlink href="http://www.exadel.com/downloads/struts/examples/WebFlowPic/validator.htm" target=_blank><FONT color=#005c9d>[设计图]</FONT></A> 下载:<A class=postlink href="http://www.exadel.com/downloads/struts/examples/struts-validator.war" target=_blank><FONT color=#005c9d>struts-validator.war</FONT></A> <BR>
<LI>查看:<A class=postlink href="http://www.exadel.com/downloads/struts/examples/WebFlowPic/upload.htm" target=_blank><FONT color=#005c9d>[设计图]</FONT></A> 下载:<A class=postlink href="http://www.exadel.com/downloads/struts/examples/struts-upload.war" target=_blank><FONT color=#005c9d>struts-upload.war</FONT></A> <BR>
<LI>查看:<A class=postlink href="http://www.exadel.com/downloads/struts/examples/WebFlowPic/cookbook.htm" target=_blank><FONT color=#005c9d>[设计图]</FONT></A> 下载:<A class=postlink href="http://www.exadel.com/downloads/struts/examples/struts-cookbook.war" target=_blank><FONT color=#005c9d>struts-cookbook.war</FONT></A> <BR>
<LI>查看:<A class=postlink href="http://www.exadel.com/downloads/struts/examples/WebFlowPic/module_switching.htm" target=_blank><FONT color=#558ab6>[设计图]</FONT></A> 下载:<A class=postlink href="http://www.exadel.com/downloads/struts/examples/simple-module-switching.war" target=_blank><FONT color=#005c9d>simple-module-switching.war</FONT></A>
<SCRIPT type=text/javascript><!--
google_ad_client = "pub-4944583547581781";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_channel ="";
google_ad_type = "text";
google_color_border = "6699CC";
google_color_bg = "003366";
google_color_link = "FFFFFF";
google_color_url = "AECCEB";
google_color_text = "AECCEB";
//--></SCRIPT>

<SCRIPT src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type=text/javascript></SCRIPT>
 <BR>
<LI><A href="http://cbasten.udbox.com/">本地下载</A>&nbsp; </LI>
<P></SPAN>&nbsp;</P><img src ="http://www.blogjava.net/zlsunnan/aggbug/22654.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2005-12-06 09:21 <a href="http://www.blogjava.net/zlsunnan/archive/2005/12/06/22654.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>