﻿<?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-软件艺术思考者-随笔分类-software project</title><link>http://www.blogjava.net/zhanglijun33/category/17948.html</link><description>混沌，彷徨，立志，蓄势...</description><language>zh-cn</language><lastBuildDate>Fri, 10 Jul 2015 03:46:47 GMT</lastBuildDate><pubDate>Fri, 10 Jul 2015 03:46:47 GMT</pubDate><ttl>60</ttl><item><title>apache+jk+tomcat集群+session同步 </title><link>http://www.blogjava.net/zhanglijun33/archive/2015/07/08/426099.html</link><dc:creator>智者无疆</dc:creator><author>智者无疆</author><pubDate>Wed, 08 Jul 2015 10:26:00 GMT</pubDate><guid>http://www.blogjava.net/zhanglijun33/archive/2015/07/08/426099.html</guid><wfw:comment>http://www.blogjava.net/zhanglijun33/comments/426099.html</wfw:comment><comments>http://www.blogjava.net/zhanglijun33/archive/2015/07/08/426099.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhanglijun33/comments/commentRss/426099.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhanglijun33/services/trackbacks/426099.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: tomcat 集群&nbsp;&nbsp;<a href='http://www.blogjava.net/zhanglijun33/archive/2015/07/08/426099.html'>阅读全文</a><img src ="http://www.blogjava.net/zhanglijun33/aggbug/426099.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhanglijun33/" target="_blank">智者无疆</a> 2015-07-08 18:26 <a href="http://www.blogjava.net/zhanglijun33/archive/2015/07/08/426099.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于Java23种设计模式的有趣见解</title><link>http://www.blogjava.net/zhanglijun33/archive/2008/01/29/modes.html</link><dc:creator>智者无疆</dc:creator><author>智者无疆</author><pubDate>Tue, 29 Jan 2008 06:35:00 GMT</pubDate><guid>http://www.blogjava.net/zhanglijun33/archive/2008/01/29/modes.html</guid><wfw:comment>http://www.blogjava.net/zhanglijun33/comments/178360.html</wfw:comment><comments>http://www.blogjava.net/zhanglijun33/archive/2008/01/29/modes.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/zhanglijun33/comments/commentRss/178360.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhanglijun33/services/trackbacks/178360.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 快乐地看待枯燥的东西&nbsp;&nbsp;<a href='http://www.blogjava.net/zhanglijun33/archive/2008/01/29/modes.html'>阅读全文</a><img src ="http://www.blogjava.net/zhanglijun33/aggbug/178360.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhanglijun33/" target="_blank">智者无疆</a> 2008-01-29 14:35 <a href="http://www.blogjava.net/zhanglijun33/archive/2008/01/29/modes.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>软件产品开发，为什么失败</title><link>http://www.blogjava.net/zhanglijun33/archive/2006/09/03/acticle.html</link><dc:creator>智者无疆</dc:creator><author>智者无疆</author><pubDate>Sun, 03 Sep 2006 09:38:00 GMT</pubDate><guid>http://www.blogjava.net/zhanglijun33/archive/2006/09/03/acticle.html</guid><wfw:comment>http://www.blogjava.net/zhanglijun33/comments/67429.html</wfw:comment><comments>http://www.blogjava.net/zhanglijun33/archive/2006/09/03/acticle.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/zhanglijun33/comments/commentRss/67429.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhanglijun33/services/trackbacks/67429.html</trackback:ping><description><![CDATA[
		<p>软件产品开发，为什么失败</p>
		<p>转自Liu Hang</p>
		<p>软件产品开发，为什么失败？在做了四年的软件开发，亲身经历了几个失败案例之后，我不得不对这个问题进行反思。我所接触到的朋友多半是做软件开发的，他们和我一样，经历失败的例子比成功的要多得多。从网上的各种文章、论坛得来的信息也一样充满着悲观。为什么这么多的失败？对于这个问题，有着各种各样的答案。诸如需求不明，不断改变；项目管理混乱，时间一拖再拖；技术方案出错，技术难题解决不了；人员流动频繁；产品出来后没有市场、没有竞争力等等问题，不一而足。正是失败的原因各种各样，在产品开发的过程中要面临一个又一个的险滩与暗礁，而每一个都有可能是致命的威胁。如何面对这些危险，绕过这些险滩？以下一些是我个人的思考。把软件开发看作一个整体的流程，本文试图从产品开发的整个流程来阐述我们会遇到的种种问题已及提出一些自己的见解。</p>
		<p> </p>
		<p>一、软件产品的立项</p>
		<p>一个软件产品的开发和项目有着许多不同，一般来说，软件项目(project)都是因为有了明确的客户，或者已经有了合同或意向而开始启动的。软件产品(product)则完全不一样了。在一个产品没有开发出来之前，基本上没有客户。当然也有人仅仅凭着一套大脑中的想法或概念就能找到客户，对这些人我只有佩服。当然大多数公司只能先拿出一套自己的产品去推销，才有可能找到定单。所以就有了做产品的想法。</p>
		<p>在这些软件公司中又可以分两种情况。一种是在某个行业做了多个项目，也积累了一些行业、技术等经验。每一个新的项目都要重复很多同样的工作，效率自然不高了。这时公司很自然的想到要有自己的产品。于是开始产品的立项了。</p>
		<p>另外一种公司则完全不是这样。他们不是在某个行业做过多少项目，甚至根本没有做过一个项目，就要雄心勃勃的去做产品。这种情况每天都在发生。他们以前可能做系统集成的，可能卖硬件的，或许根本就不是IT行业的，或者恰好做了一个项目，现在他们要进军软件行业了，所以急切的要做出自己的产品去打市场。于是他们在一番调查论证后，开始了产品的立项。</p>
		<p>在这两种情况下，可以明显的看得出前一种公司的基础要好得多，成功的几率也要大多多。但这并不表明后一种就会失败。在目前阶段关键是看他有没有全面的调查论证，进入这个软件行业，做这个产品是否可行。很多产品的失败，从一开始就注定的了。公司没做过多少认真的论证就匆匆开始了。软件行业，产品开发都有着自身的很多规律，如果公司的决策层、领导层没有经验，也没有去学习，拿着别的行业的经验去套用它，那失败也就不远了。举个简单的例子，软件开发中人力资源是最重要的。软件开发人员的薪水在各个行业中是算非常高的了，特别对于有丰富经验的人才更是与此。从传统行业过来的领导层如果不明白这点，也就找不到优秀的人才了。</p>
		<p>无论哪种公司，他们在做产品立项时都要做好以下的心里准备：</p>
		<p>?       软件产品开发投资是很大的，特别是对哪些想做大型的、优秀产品的公司。</p>
		<p>?       软件产品开发周期也是比较长的。两、三年做一个产品是很常见的。不要认为半年就可以做一个很好的产品</p>
		<p>?       软件产品是很容易失败的。既有可能产品开发不出来，也有可能没有市场。</p>
		<p> </p>
		<p>如果一个公司没有这些心里准备，那结果就很可能失败。为什么这么说，以我的亲身经历来说吧。我曾经做过的一家公司，主要业务是做系统集成的。后来开始了一个物流软件的产品开发。在2000年左右，物流行业软件刚刚兴起，也算是一个比较好的方向。公司组建了一个开发团队，开始了长达两年的曲折的研发过程。由于在研发过程中遇到的种种问题，无法给公司领导层一个明确的结果，在研发开始初见曙光的时候，公司高层终止了这个产品。公司的高层忍受不了大量的投资和过长的时间。任何想做产品的公司都一定要有这些心里准备，要充分估计投资额和研发周期。</p>
		<p>假设公司经过了充分的论证，也有了一定的投资和时间预算，产品立项了。但这只是一个良好的开端，因为下面的任何一个过程的失败，都有可能导致全盘皆输。</p>
		<p> </p>
		<p>二、团队构建</p>
		<p>产品立项后，就要开始组建研发团队了。软件开发是一个既要高度协作、又有独立创造的智力活动。所以人的因素是关系到产品开发能否成功的一个重要方面。应该说产品能否研发成功，研发团队的合理构建是关键性的。一个公司的领导层或许没有多少软件研发的经验，但必须要保证能构建一个合理的研发团队。就点很容易理解，就是让合适的人去做合适的事。不过反过来说，如果领导层没有软件研发的经验，那也很难构建一个合理的团队。那怎样构建一个好的团队呢？这个问题没有一个非常普遍的答案。各个产品的规模、技术难度都不相同，答案也不一样。</p>
		<p>我们时常看到的情况是，公司会任命一个研发经理或叫PM,负责整个研发过程。于是问题就出来了。这个PM是负责管理工作还是技术工作，或者两个都一起负责？怎样规定PM的职责权限？如果没有对这个问题的明确答案，那危险就随之而来的。就来我的经历过的来说吧。</p>
		<p>在离开上一家公司后，我来到另一家软件公司做网上教育平台的产品研发。前期阶段可以说没有一个专职的项目经理，管理工作基本上由一个做技术的Team Leader（架构师）负责，虽说也有不少问题，但大家基本上还是团结在一起安步就班的工作。后来公司为我们团队招来一个PM，此君是海龟派，在国外、学习工作好几年，也有技术背景。我们对他也充满期待。没想到过了不久，他居然把我们的Team Leader给开了，找了一些大家都不认可的理由。并自任技术负责人，对我们的大部分的技术方案都持否定态度，并严厉要求我们服从他的技术领导。如果他精通技术也就罢了，关键是他的技术一点也不怎样，还自我感觉良好。那最终的结果可想而知了。产品基本上失败，技术人员纷纷跳槽，最后，这个 PM也只得走人了。</p>
		<p>这样的故事每天都在发生，大量的技术人员在抱怨领导不懂技术，瞎指挥。在我的案例中，可以明显的感觉到，公司对这个PM没有明确的职责划分，或者这个PM没有摆正自己的位置。这个PM应该做做管理工作，而不是负责技术。</p>
		<p>做软件开发的都知道，一个团队中一般都有一个技术领导，或者叫架构师（Architect）。 那他和PM怎样划分职责？ </p>
		<p>如果要开发的产品规模比较大，比如人员数达到10人以上。这时面临的管理、组织工作比较多，公司应该考虑起用一位专职的PM来负责这方面的工作。他的主要工作包括人员招聘，提供开发团队所需资源，制定计划，监督实施等，同时，他应该是一个能够鼓动士气，懂得调动员工积极性的亲善的领导人，对作为一个PM的职位来说，个人的素质和性格应该具有更决定性的意义，他主要工作是为研发团队提供一切必要的服务，监督的作用应该包含其中。在这样的团队里，必须存在一位架构师来全面负责技术工作，他有权独立决定技术方案，他与PM的关系是团结与合作，而不是领导和被领导，架构师应该得到PM应有的尊重，而这点在中国很难做到。很多PM干涉架构师的技术工作，甚至起而代之，造成团队之间的混乱。上面的案例正是说明了这一点。架构师一般都是有一点完美主义，他总是希望看到产品做得更加出色，但这是否让产品开发走向不可控制的地步？事实上，架构师要对现实情况作出妥协（如时间、资金的限制），也就是对PM作出妥协。如果说PM代表时间、资源的现实主义，那么架构师就代表着完美的理想主义，他们就是在不断的合作、妥协中共同推动产品的发展。</p>
		<p>对于开发的产品规模较小，也可以完全不设置专职的PM。设置一位Team Leader，他既负责整个技术、也同时负责团队的管理工作。如果认为负担较重的话，可以为Team Leader配置一位秘书（项目管理人员），他的主要功能是辅助Team Leader做一些管理方便的工作。如人员招聘的准备工作、开发计划的监督等等。</p>
		<p>当团队合理的构建之后，下面的就进入了产品研发的核心流程了。</p>
		<p> </p>
		<p>三、业务建模&amp;需求分析 </p>
		<p>前面的过程更多的是由企业领导层决定的，当我们技术人员进入这个团队时，只能祈讨已经有了一个好的开始：公司下定决心要研发这个产品，有一个优秀的、明白自己职责的PM。现在已及下面的所有流程都和技术人员（包括需求人员）密切相关，是技术人员决定着产品成败与否的时候了。</p>
		<p>无论做产品还是项目，第一步做需求分析，这一点无须置疑。几乎每一个做软件的都知道，需求的重要性。可是为什么那么多的产品或项目最后失败的主要原因是需求问题呢？</p>
		<p>在软件研发中，产品的业务需求比项目的业务需求更难以确定。产品一般面对的是某个行业的通用需求，涉及的客户面更广，合理的提取这些需求以形成更通用的产品本身就是一件很困难的工作。对于一些以前没有这些行业经验的软件公司来说，这就更困难了。很多公司真因为没有很好去做需求分析的工作而导致产品的失败。在我以前做物流的那家公司里，也是犯了这样严重的错误。我们当时对物流行业并不熟悉，也不能很好的把握业务需求，产品研发在来回反复的过程中消耗了大量的时间与精力。</p>
		<p>对于这个问题的解决，几乎所有的软件工程方法都提出了好的方案：引入领域业务专家（Domain Expert）。我们的研发团队中，一定要有这样的角色，即使我们没有这样一个专职人员，但一定要有人扮演类似的角色（例如架构师）。业务专家可以和架构师一起进行业务建模的工作，而架构师则偏重于技术方面，把业务模型转化成系统需求，按照RUP的流程来说，就是最终变成一个个Use Case。而一个好的业务专家是非常难得的，这就是为什么很多公司有这个意识而没有做好需求工作的重要原因：由于资金、时间等各方面的限制，他们最终放弃了这一步，而把希望寄托在架构师或其他开发人员身上，而这其中的风险就可想而知了。</p>
		<p>也有很多公司没有业务专家，而把这个角色附加给架构师了。他们要求架构师既要精通业务，也要精通技术。而现实中，这样的人凤毛麟角，属于可遇而不可求的那一类。所以在这类角色没有很明确分开的产品研发中，得到的东西要么是需求方面做得不够好，要么在软件架构方面不令人满意。</p>
		<p>怎样最大程度保证需求的合理？我个人认为做一个产品的界面原型是一个好的方法。这一点在做一个基于browser的应用系统时更可行：根据业务需求做出整个的页面原型，这样的页面也许很粗糙，后台也不需要任何的程序运行，但可以根据这些页面元素及之间的流程来验证业务需求是否合理、正确。这种方法应用到项目开发（相对于产品）中，可以和客户一起验证需求，经过几次反复，可以比较准确的理解、把握客户的真实需求。这样的工作耗费时间不多，但却能起到很大的作用。</p>
		<p> </p>
		<p>四、架构设计</p>
		<p>如果需求做好的话，可以说这个产品基本上能够得以出笼，但是否称得上品质优秀，则看架构设计工作做的怎样了。一个好的产品除了满足客户的业务功能外，还要满足一些非功能性的需求：系统性能、可用性、可管理性、可靠性、可扩展性、安全性等等。</p>
		<p>正是因为面对这些众多问题，在这一过程中，架构师很容易走向极端。最常见的两种极端情况：（1）过分追求完美。（2）做出来就行，不考虑软件品质。</p>
		<p>作为一个系统架构师，很多人具有完美主义的倾向。他们不断的考虑系统的性能、可扩展性、安全性，技术的先进性等等。他们最喜欢说的的词：组件性、通用性、扩展性等等。所以他们不断的修改架构，不断的冒出新的思想，采用新的技术。而这一切走向极端就会让研发陷入不可控制的地步。在我做物流和教育平台产品的时候，都遇到了这样的问题。架构师希望产品做得更大，满足更多的业务需求，同时又希望几乎每个业务模块组件化，具有更多的通用性，采用尽可能新的技术。最终造成计划的一拖再拖，让公司领导层丧失信心。</p>
		<p>第二种情况在一些规模较小的产品研发中也很常见。技术领导人在时间、资金或PM的压力下基本上放弃了软件品质的追求。他们只希望在规定的时间内尽快做出一个东西就行。他们基本上不太考虑组件性、扩展性等等。这样做出来的产品和项目也就差不多了，因为他的通用性、扩充性太差。</p>
		<p>可以看出，这两种情况都可能导致产品的失败。第一种总是想一口吃成一个胖子，他们忽略了软件开发的跌代性，软件总是在不断的跌代、更新中，螺旋式上升发展的。而第二种则是技术人员的悲哀，他们没有条件去追求软件的品质，只能寄希望于产品的下一个版本。事实上，没有前一个版本良好的框架，新的版本要想做好，几乎是重新开发。</p>
		<p>所以一个优秀的架构师，既要不放弃心中的完美主义的理想，又要对现实做一定程度的妥协，在这种平衡中，领导团队进行技术开发。事实上，在中国软件产业的现阶段，急功近利的公司太多了，他们不会提供更多的条件、更多的空间让架构师去实现一个优秀的产品。这是我们所有做软件技术人员的悲哀，这是所有想走向、或正在架构师岗位上的人员的悲哀。</p>
		<p>在软件产品开发的这一阶段，除了以上的情况，还有许多问题同样会导致产品的失败。</p>
		<p>例如公司对软件工程的理解和掌握。软件工程强调使用过程来保证项目的成功，一般都会提出一整套的理论，如一些核心流程、步骤、方法、规则等等，例如RUP,XP。有很多项目经理、架构师和软件公司的高层都希望去使用这些方法，以保证项目、产品的成功。特别是公司的上层领导，他们只能通过这种方法来保证对项目的控制，所以特别热衷于实施这些方法。然而事实上呢？。大家都常有这样的经历：为了文档而写文档，写出来的文档基本上可以扔进垃圾箱，没有任何作用了。我这样说，并不认为软件工程有什么不好，关键在于你怎么去使用它。软件工程是一门实践性很强的学科，需要我们根据不同的现实情况不断的调整、实施，而不是照本宣科的一些教条。最怕遇到这种情况：一些领导或项目经理读了几本软件工程的书，自以为找到了灵丹妙药，就开始在项目中强力实施。比如制订一些步骤、计划，在什么什么时间，达到什么什么成果，要写什么什么样的文档等等。在我以前做教育软件产品中就充满了这种倾向。了在某为一时间交出架构设计文档，我们大部分时间不是去考虑、验证架构，而是为了写出这份文档。结果是，到我们要去实现时，发现根本行不通，整个架构存在严重的问题，到这个时候再回头重做设计，代价太大了。个人感觉是，要合理的使用一些软件工程理论，需要项目经理、架构师有丰富的实践经验，能够根据不同的产品研发情况，制订自己的一些确实可行的方法。软件工程是一些通用的理论，从来而且应该是可以灵活裁减的。</p>
		<p>四、其他步骤</p>
		<p>  如果说上面的步骤都很好的做到了，那产品应该说基本上成功了。有了一个合理的的架构设计，那么详细设计和编码应该不是一个问题，这只需要我们的软件工程师的努力就可以了。当然测试是很重要的，但基本上不会导致产品的研发失败。</p>
		<p>作者后记：在做过几个项目，经历过一些失败之后，我把自己的一些想法写下来。这篇文章文字比较乱，基本上想到哪写到哪。我所碰到的问题，很多做软件的朋友也时常碰到，在抱怨了太多之后，我决定把它写下来。在国内做软件太困难了，如果你对软件还有一些理想主义的话，那你就太痛苦了。<br /></p>
<img src ="http://www.blogjava.net/zhanglijun33/aggbug/67429.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhanglijun33/" target="_blank">智者无疆</a> 2006-09-03 17:38 <a href="http://www.blogjava.net/zhanglijun33/archive/2006/09/03/acticle.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java模式学习(3)</title><link>http://www.blogjava.net/zhanglijun33/archive/2006/07/25/adapter.html</link><dc:creator>智者无疆</dc:creator><author>智者无疆</author><pubDate>Tue, 25 Jul 2006 10:21:00 GMT</pubDate><guid>http://www.blogjava.net/zhanglijun33/archive/2006/07/25/adapter.html</guid><wfw:comment>http://www.blogjava.net/zhanglijun33/comments/60037.html</wfw:comment><comments>http://www.blogjava.net/zhanglijun33/archive/2006/07/25/adapter.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/zhanglijun33/comments/commentRss/60037.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhanglijun33/services/trackbacks/60037.html</trackback:ping><description><![CDATA[适配器模式<br />适配器模式可以把两个不相关的类组装在一起使用。实际上是继承和组合的综合运用。<br />让我们来看一个例子：<br />public interface Itable{<br />public void say(String str);<br />}<br />public interface Ichair{<br />public void say(String str);<br />}<br />public class Table implements  Itable{<br />　　public void say(String str){<br />　　　　System.out.println(" i am a table :"+str);<br />　　}<br /><span lang="EN-US">}<br />public class Chair implements Ichair{<br />　　public void say(String str){<br />　　　　System.out.println(" i am a chair :"+str);<br />　　}<br /><span lang="EN-US">}<br /><br />public class AdapterFitment  extends Table{<br />private Ichair c;<br />  public AdapterFitment(Ichair c){<br />  this.c=c;<br />}<br />public void insert(String str){c.say(str);}<br />}<br /><br />然后呢，这个组合家具就即能当桌子用又能当椅子用了。</span></span><img src ="http://www.blogjava.net/zhanglijun33/aggbug/60037.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhanglijun33/" target="_blank">智者无疆</a> 2006-07-25 18:21 <a href="http://www.blogjava.net/zhanglijun33/archive/2006/07/25/adapter.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于软件分层的讨论</title><link>http://www.blogjava.net/zhanglijun33/archive/2006/07/24/softlay.html</link><dc:creator>智者无疆</dc:creator><author>智者无疆</author><pubDate>Mon, 24 Jul 2006 08:02:00 GMT</pubDate><guid>http://www.blogjava.net/zhanglijun33/archive/2006/07/24/softlay.html</guid><wfw:comment>http://www.blogjava.net/zhanglijun33/comments/59834.html</wfw:comment><comments>http://www.blogjava.net/zhanglijun33/archive/2006/07/24/softlay.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/zhanglijun33/comments/commentRss/59834.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhanglijun33/services/trackbacks/59834.html</trackback:ping><description><![CDATA[现在后台分成如下几个层：<br />Domain：提供getter/setter<br />Dao：接口，定义了持久化方法，CRUD<br />DaoImpl：Dao的实现<br />Service：业务逻辑<br /><br />但是在实际的过程中发现，service里面有很多涉及到持久化的查询、更新操作，那么，这些方法是在dao中定义呢？还是在service中定义和实现？<br />比如，对于一个 客户资料UserOrder 实体<br />dao中定义了getUserOrderByID() getAllUserOrder() insertUserOrder() updateUserOrder() delUserOrder()五个方法<br />现在需要一个 根据地区查询客户getUserByArea()，或者 根据产品线和地区查询客户 getUserByAreaSrv()<br />这样的方法，定义在service还是dao中？<br />第二个问题。service层肯定是面向客户端调用的，那么dao层对于客户端是否暴露？<br />就是说，service层中是否提供dao中的方法，比如getAllUserOrder() insertUserOrder()这些方法？<br />如果不提供，那么客户端需要知道哪些是dao中提供的，哪些是service提供的<br />如果提供这些方法，意味着所有的方法需要在dao中定义，daoimpl中实现，service中包装，是否太重复和烦琐？<br />1.<br /><font face="Arial" size="2">DAO只负责数据库操作，不涉及任何逻辑，Service就需要业务逻辑了，比如getUserOrder()在DAO，你就查数据库就行，在service中，你可能需要首先验证当前用户是否有这个操作权限，然后调用DAO，最后记录日志<br />2.需要在dao中定义，daoimpl中实现，service中包装，是否太重复和烦琐？<br />其实这不会发生，因为你认为getUserByAreaSrv()方法肯定一定要完全从数据库完成，其实不然，其实我可能只从数据库查询一个ID集合，然后，在服务层再通过缓存访问封装成完整的Model。这些都需要在Service完成（只是一个比喻）。<br /><br />预留Service是为防止你的业务系统复杂化之后有一个插脚的地方。<br /><br />现在很多系统就没有这种插脚的地方，在struts的Action中直接调用Hibernate数据库操作，如那个日本人做的开发框架RoR就是这样，这些以丧失可维护性做代价的快速开发都是伪框架。<br /><br />所以，为你的系统将来留着Service<br />3.我认为Dao层还是不要暴露在最外层为妙，即Service以外的层次都无法调用。否则实体化与应用逻辑以及界面之间的耦合度将无法控制，导致产品的生命周期缩短，可扩展性遭到破坏。<br />Service层当然也可以有getUserOrderByID() 这样的操作，但最好通过调用DAO来实现，或者像banq说的以dao-cache的方式来实现，而不是直接去做实体操作。这样做可能会感掉有些重复繁琐，但会给产品今后的升级补充带来好处。可能会多出这30％的代码量，但带来的好处是起码能延长代码30％的生命。<br />Service的方法定义和方法内的参数定义更倾于业务逻辑，而不是存储方式。而Dao的定义则跟存储方式直接挂钩，而不需要过度考虑业务逻辑。所以代码上的“繁琐”恰恰代表了逻辑上的“简单”。<br />更重要的是Service是必须的。<br />22一般service层是怎么实现的？是不是就是一个拥有很多静态方法的类，每个静态方法处理一些业务逻辑，还是有什么其它的方法，望指教 <br />1我还有疑问，比如CoreFeeService好像是操作CoreFee这个domain bean的，如果我有一段业务逻辑，是调用CoreFeeService改变CoreFee的一些属性，然后将CoreFee持久化到数据库，接着可能要调用另一个domain bean的service方法。<br /><br />在Struts框架中，这段代码是写在Action中，还是别的地方，Action又好像应该只控制转发，不应该有业务的代码在里面。但像上面那样，可能还有事务处理，这时我该怎么做好呢？<br />2你描述的这段设计本来就是包含了业务逻辑。当然应该在Service里。service本身包含了粒度不同的操作，粗粒度的操作（方法）里可以包含很多细粒度的操作。这些细粒度的操作可以是private，也可是是public（如果别的action需要它）。<br />Action的概念当然跟业务本身是有一定关系的。你可以在action的代码中表示出一些业务上的逻辑关系，但要节约。能在service中实现的最好在service中。这对程序结构是有好处的。<br />你说：“Action又好像应该只控制转发，不应该有业务的代码在里面”——这句话体现出你可能已经成为“框架”的“受害者”了。要用“框架”来提炼你的设计，而不要用来约束你的思维。不要指望从“框架”中找到理清软件开发一切思路的“银弹”——因为它根本不存在。<br /></font><img src ="http://www.blogjava.net/zhanglijun33/aggbug/59834.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhanglijun33/" target="_blank">智者无疆</a> 2006-07-24 16:02 <a href="http://www.blogjava.net/zhanglijun33/archive/2006/07/24/softlay.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>学习设计模式(2)</title><link>http://www.blogjava.net/zhanglijun33/archive/2006/07/24/partten2.html</link><dc:creator>智者无疆</dc:creator><author>智者无疆</author><pubDate>Mon, 24 Jul 2006 06:29:00 GMT</pubDate><guid>http://www.blogjava.net/zhanglijun33/archive/2006/07/24/partten2.html</guid><wfw:comment>http://www.blogjava.net/zhanglijun33/comments/59801.html</wfw:comment><comments>http://www.blogjava.net/zhanglijun33/archive/2006/07/24/partten2.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/zhanglijun33/comments/commentRss/59801.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhanglijun33/services/trackbacks/59801.html</trackback:ping><description><![CDATA[
		<p>设计模式并不难，之所以把它列到j2ee学习的最后一项，我想是因为它需要有很多写程序的体验。记得在一本书上看到程序员有三个特点（或者毛病）：傲慢，懒惰，浮躁。其中最宝贵的品质是：懒惰，因为他们/我们很懒，所以对经常写一些重复的代码感觉烦躁，从而不断（注意是不断地）找方法，发明工具来减轻他们/我们的负担（当然有时也是为了技术创新），这样就出现了现在的局面，每天都有很多新技术、新框架涌现。好了，扯远了，下面来说一下另一个我们经常用到的模式--工厂模式。<br /></p>
		<h3 style="TEXT-ALIGN: center" align="center">设计模式之<span lang="EN-US">Factory</span></h3>
		<p>
				<b>
						<i>定义<span lang="EN-US">:提供创建对象的接口.</span></i>
				</b>
				<span lang="EN-US">
						<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /?>
						<o:p>
						</o:p>
				</span>
		</p>
		<p>
				<b>
						<i>为何使用<span lang="EN-US">?</span></i>
				</b>
				<span lang="EN-US">
						<br />工厂模式是我们最常用的模式了,著名的Jive论坛 ,就大量使用了工厂模式，工厂模式在Java程序系统可以说是随处可见。<o:p></o:p></span>
		</p>
		<p>为什么工厂模式是如此常用？因为工厂模式就相当于创建实例对象的<span lang="EN-US">new，我们经常要根据类Class生成实例对象，如A a=new A() 工厂模式也是用来创建实例对象的，所以以后new时就要多个心眼，是否可以考虑实用工厂模式，虽然这样做，可能多做一些工作，但会给你系统带来更大的可扩展性和尽量少的修改量。<o:p></o:p></span></p>
		<p>我们以类<span lang="EN-US">Sample为例， 如果我们要创建Sample的实例对象:<o:p></o:p></span></p>
		<p>
				<span lang="EN-US">Sample sample=new Sample();<o:p></o:p></span>
		</p>
		<p>可是，实际情况是，通常我们都要在创建<span lang="EN-US">sample实例时做点初始化的工作,比如赋值 查询数据库等。<o:p></o:p></span></p>
		<p>首先，我们想到的是，可以使用<span lang="EN-US">Sample的构造函数，这样生成实例就写成:<o:p></o:p></span></p>
		<p>
				<span lang="EN-US">Sample sample=new Sample(参数);<o:p></o:p></span>
		</p>
		<p>但是，如果创建<span lang="EN-US">sample实例时所做的初始化工作不是象赋值这样简单的事，可能是很长一段代码，如果也写入构造函数中，那你的代码很难看了（就需要Refactor重整）。<o:p></o:p></span></p>
		<p>为什么说代码很难看，初学者可能没有这种感觉，我们分析如下，初始化工作如果是很长一段代码，说明要做的工作很多，将很多工作装入一个方法中，相当于将很多鸡蛋放在一个篮子里，是很危险的，这也是有背于<span lang="EN-US">Java面向对象的原则，面向对象的封装(Encapsulation)和分派(Delegation)告诉我们，尽量将长的代码分派“切割”成每段，将每段再“封装”起来(减少段和段之间偶合联系性)，这样，就会将风险分散，以后如果需要修改，只要更改每段，不会再发生牵一动百的事情。<o:p></o:p></span></p>
		<p>在本例中，首先，我们需要将创建实例的工作与使用实例的工作分开<span lang="EN-US">, 也就是说，让创建实例所需要的大量初始化工作从Sample的构造函数中分离出去。<o:p></o:p></span></p>
		<p>这时我们就需要<span lang="EN-US">Factory工厂模式来生成对象了，不能再用上面简单new Sample(参数)。还有,如果Sample有个继承如MySample, 按照面向接口编程,我们需要将Sample抽象成一个接口.现在Sample是接口,有两个子类MySample 和HisSample .我们要实例化他们时,如下:<o:p></o:p></span></p>
		<p>
				<span lang="EN-US">Sample mysample=new MySample();<br />Sample hissample=new HisSample();<o:p></o:p></span>
		</p>
		<p>随着项目的深入<span lang="EN-US">,Sample可能还会"生出很多儿子出来", 那么我们要对这些儿子一个个实例化,更糟糕的是,可能还要对以前的代码进行修改:加入后来生出儿子的实例.这在传统程序中是无法避免的.<o:p></o:p></span></p>
		<p>但如果你一开始就有意识使用了工厂模式<span lang="EN-US">,这些麻烦就没有了.<o:p></o:p></span></p>
		<p>
				<strong>工厂方法</strong>
				<b>
						<span lang="EN-US">
								<br />
						</span>
				</b>你会建立一个专门生产<span lang="EN-US">Sample实例的工厂:<o:p></o:p></span></p>
		<p>
		</p>
		<table style="WIDTH: 80%; mso-cellspacing: 2.2pt; mso-padding-alt: 2.25pt 2.25pt 2.25pt 2.25pt" cellspacing="3" cellpadding="0" width="80%" border="0">
				<tbody>
						<tr>
								<td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: #cccccc; PADDING-BOTTOM: 2.25pt; PADDING-TOP: 2.25pt">
										<p>
												<span lang="EN-US">public class Factory{<o:p></o:p></span>
										</p>
										<p>　　<span lang="EN-US">public static Sample creator(int which){<o:p></o:p></span></p>
										<p>　　<span lang="EN-US">//getClass 产生Sample 一般可使用动态类装载装入类。<br />　　if (which==1)<br />　　　　return new SampleA();<br />　　else if (which==2)<br />　　　　return new SampleB();<o:p></o:p></span></p>
										<p>　　<span lang="EN-US">}<o:p></o:p></span></p>
										<p>
												<span lang="EN-US">}<o:p></o:p></span>
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>那么在你的程序中<span lang="EN-US">,如果要实例化Sample时.就使用<o:p></o:p></span></p>
		<p>
				<span lang="EN-US">Sample sampleA=Factory.creator(1);<o:p></o:p></span>
		</p>
		<p>这样<span lang="EN-US">,在整个就不涉及到Sample的具体子类,达到封装效果,也就减少错误修改的机会,这个原理可以用很通俗的话来比喻:就是具体事情做得越多,越容易范错误.这每个做过具体工作的人都深有体会,相反,官做得越高,说出的话越抽象越笼统,范错误可能性就越少.好象我们从编程序中也能悟出人生道理?呵呵.<o:p></o:p></span></p>
		<p>使用工厂方法 要注意几个角色，首先你要定义产品接口，如上面的<span lang="EN-US">Sample,产品接口下有Sample接口的实现类,如SampleA,其次要有一个factory类，用来生成产品Sample，如下图，最右边是生产的对象Sample：<o:p></o:p></span></p>
		<p>
				<span lang="EN-US">
						<img id="_x0000_i1089" height="178" src="mk:@MSITStore:C:\Documents%20and%20Settings\Administrator.MICROSOF-7789E7\桌面\java设计模式.chm::/设计模式之Factory.files/factory.jpg" width="526" />
						<o:p>
						</o:p>
				</span>
		</p>
		<p>进一步稍微复杂一点，就是在工厂类上进行拓展，工厂类也有继承它的实现类<span lang="EN-US">concreteFactory了<b><i>。</i></b><o:p></o:p></span></p>
		<p>
				<strong>抽象工厂</strong>
				<span lang="EN-US">
						<br />工厂模式中有: 工厂方法(Factory Method) 抽象工厂(Abstract Factory).<o:p></o:p></span>
		</p>
		<p style="MARGIN-BOTTOM: 12pt">这两个模式区别在于需要创建对象的复杂程度上。如果我们创建对象的方法变得复杂了<span lang="EN-US">,如上面工厂方法中是创建一个对象Sample,如果我们还有新的产品接口Sample2.<o:p></o:p></span></p>
		<p style="MARGIN-BOTTOM: 12pt">这里假设：<span lang="EN-US">Sample有两个concrete类SampleA和SamleB，而Sample2也有两个concrete类Sample2A和SampleB2<o:p></o:p></span></p>
		<p style="MARGIN-BOTTOM: 12pt">那么，我们就将上例中<span lang="EN-US">Factory变成抽象类,将共同部分封装在抽象类中,不同部分使用子类实现，下面就是将上例中的Factory拓展成抽象工厂:<o:p></o:p></span></p>
		<p>
		</p>
		<table style="WIDTH: 80%; mso-cellspacing: 2.2pt; mso-padding-alt: 2.25pt 2.25pt 2.25pt 2.25pt" cellspacing="3" cellpadding="0" width="80%" border="0">
				<tbody>
						<tr>
								<td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: #cccccc; PADDING-BOTTOM: 2.25pt; PADDING-TOP: 2.25pt">
										<p>
												<span lang="EN-US">public abstract class Factory{<o:p></o:p></span>
										</p>
										<p>　　<span lang="EN-US">public abstract Sample creator();<o:p></o:p></span></p>
										<p>　　<span lang="EN-US">public abstract Sample2 creator(String name); <o:p></o:p></span></p>
										<p>
												<span lang="EN-US">}<o:p></o:p></span>
										</p>
										<p>
												<span lang="EN-US">public class SimpleFactory extends Factory{<o:p></o:p></span>
										</p>
										<p>　　<span lang="EN-US">public Sample creator(){<br />　　　　.........<br />　　　　return new SampleA<br />　　}<o:p></o:p></span></p>
										<p>　　<span lang="EN-US">public Sample2 creator(String name){<br />　　　　.........<br />　　　　return new Sample2A<br />　　}<o:p></o:p></span></p>
										<p>
												<span lang="EN-US">}<o:p></o:p></span>
										</p>
										<p>
												<span lang="EN-US">public class BombFactory extends Factory{<o:p></o:p></span>
										</p>
										<p>　　<span lang="EN-US">public Sample creator(){<br />　　　　......<br />　　　　return new SampleB <br />　　}<o:p></o:p></span></p>
										<p>　　<span lang="EN-US">public Sample2 creator(String name){<br />　　　　......<br />　　　　return new Sample2B<br />　　}<o:p></o:p></span></p>
										<p>
												<span lang="EN-US">}<o:p></o:p></span>
										</p>
										<p>
												<span lang="EN-US"> <o:p></o:p></span>
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>从上面看到两个工厂各自生产出一套<span lang="EN-US">Sample和Sample2,也许你会疑问，为什么我不可以使用两个工厂方法来分别生产Sample和Sample2? <o:p></o:p></span></p>
		<p>抽象工厂还有另外一个关键要点，是因为<span lang="EN-US"> SimpleFactory内，生产Sample和生产Sample2的方法之间有一定联系，所以才要将这两个方法捆绑在一个类中，这个工厂类有其本身特征，也许制造过程是统一的，比如：制造工艺比较简单，所以名称叫SimpleFactory。<o:p></o:p></span></p>
		<p>在实际应用中，工厂方法用得比较多一些，而且是和动态类装入器组合在一起应用，<span lang="EN-US"><o:p></o:p></span></p>
		<p>
				<strong>举例</strong>
				<span lang="EN-US">
						<o:p>
						</o:p>
						<o:p>
						</o:p>
				</span>
		</p>
		<p>我们以<span lang="EN-US">Jive的ForumFactory为例，这个例子在前面的Singleton模式中我们讨论过，现在再讨论其工厂模式:<o:p></o:p></span></p>
		<p>
		</p>
		<table style="WIDTH: 97%; mso-cellspacing: 2.2pt; mso-padding-alt: 2.25pt 2.25pt 2.25pt 2.25pt" cellspacing="3" cellpadding="0" width="97%" border="0">
				<tbody>
						<tr>
								<td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: #cccccc; PADDING-BOTTOM: 2.25pt; PADDING-TOP: 2.25pt">
										<p>
												<span lang="EN-US">public abstract class ForumFactory {<o:p></o:p></span>
										</p>
										<p>　　<span lang="EN-US">private static Object initLock = new Object();<br />　　private static String className = "com.jivesoftware.forum.database.DbForumFactory";<br />　　private static ForumFactory factory = null; <o:p></o:p></span></p>
										<p>　　<span lang="EN-US">public static ForumFactory getInstance(Authorization authorization) {<br />　　　　//If no valid authorization passed in, return null.<br />　　　　if (authorization == null) {<br />　　　　　　return null;<br />　　　　}<br />　　　　//以下使用了Singleton 单态模式<br />　　　　if (factory == null) {<br />　　　　　　synchronized(initLock) {<br />　　　　　　　　if (factory == null) {<br />　　　　　　　　　　　　...... <o:p></o:p></span></p>
										<p>　　　　　　　　　　<span lang="EN-US">try {<br />　　　　　　　　　　　　　　//动态转载类<br />　　　　　　　　　　　　　　Class c = Class.forName(className);<br />　　　　　　　　　　　　　　factory = (ForumFactory)c.newInstance();<br />　　　　　　　　　　}<br />　　　　　　　　　　catch (Exception e) {<br />　　　　　　　　　　　　　　return null;<br />　　　　　　　　　　}<br />　　　　　　　　}<br />　　　　　　}<br />　　　　}<o:p></o:p></span></p>
										<p>　　　　<span lang="EN-US">//Now, 返回 proxy.用来限制授权对forum的访问<br />　　　　return new ForumFactoryProxy(authorization, factory,<br />　　　　　　　　　　　　　　　　　　　　factory.getPermissions(authorization));<br />　　}<o:p></o:p></span></p>
										<p>　　<span lang="EN-US">//真正创建forum的方法由继承forumfactory的子类去完成.<br />　　public abstract Forum createForum(String name, String description)<br />　　throws UnauthorizedException, ForumAlreadyExistsException;<o:p></o:p></span></p>
										<p>　　<span lang="EN-US">....<o:p></o:p></span></p>
										<p>
												<span lang="EN-US">}<o:p></o:p></span>
										</p>
										<p>
												<span lang="EN-US"> <o:p></o:p></span>
										</p>
										<p>
												<span lang="EN-US"> <o:p></o:p></span>
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>因为现在的<span lang="EN-US">Jive是通过数据库系统存放论坛帖子等内容数据,如果希望更改为通过文件系统实现,这个工厂方法ForumFactory就提供了提供动态接口:<o:p></o:p></span></p>
		<p>
				<span lang="EN-US">private static String className = "com.jivesoftware.forum.database.DbForumFactory";<o:p></o:p></span>
		</p>
		<p>你可以使用自己开发的创建<span lang="EN-US">forum的方法代替com.jivesoftware.forum.database.DbForumFactory就可以.<o:p></o:p></span></p>
		<p>在上面的一段代码中一共用了三种模式<span lang="EN-US">,除了工厂模式外,还有Singleton单态模式,以及proxy模式,proxy模式主要用来授权用户对forum的访问,因为访问forum有两种人:一个是注册用户 一个是游客guest,那么那么相应的权限就不一样,而且这个权限是贯穿整个系统的,因此建立一个proxy,类似网关的概念,可以很好的达到这个效果.  <o:p></o:p></span></p>
		<p>看看<span lang="EN-US">Java宠物店中的CatalogDAOFactory:<o:p></o:p></span></p>
		<p>
		</p>
		<table style="BACKGROUND: #cccccc; WIDTH: 100%; mso-cellspacing: 0cm; mso-padding-alt: 0cm 0cm 0cm 0cm" cellspacing="0" cellpadding="0" width="100%" bgcolor="#cccccc" border="0">
				<tbody>
						<tr>
								<td style="PADDING-RIGHT: 0cm; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; PADDING-TOP: 0cm">
										<p class="MsoNormal">
												<span lang="EN-US" style="FONT-SIZE: 10.5pt">public class CatalogDAOFactory { <o:p></o:p></span>
										</p>
										<p>　　<span lang="EN-US">/**<o:p></o:p></span></p>
										<p>　　<span lang="EN-US">* 本方法制定一个特别的子类来实现DAO模式。<br />　　* 具体子类定义是在J2EE的部署描述器中。<br />　　*/<o:p></o:p></span></p>
										<p>　　<span lang="EN-US">public static CatalogDAO getDAO() throws CatalogDAOSysException {<o:p></o:p></span></p>
										<p>　　　　<span lang="EN-US">CatalogDAO catDao = null;<o:p></o:p></span></p>
										<p>　　　　<span lang="EN-US">try {<o:p></o:p></span></p>
										<p>　　　　　　<span lang="EN-US">InitialContext ic = new InitialContext();<br />　　　　　　//动态装入CATALOG_DAO_CLASS<br />　　　　　　//可以定义自己的CATALOG_DAO_CLASS，从而在无需变更太多代码<br />　　　　　　//的前提下，完成系统的巨大变更。<o:p></o:p></span></p>
										<p>　　　　　　<span lang="EN-US">String className =(String) ic.lookup(JNDINames.CATALOG_DAO_CLASS);<o:p></o:p></span></p>
										<p>　　　　　　<span lang="EN-US">catDao = (CatalogDAO) Class.forName(className).newInstance();<o:p></o:p></span></p>
										<p>　　　　<span lang="EN-US">} catch (NamingException ne) {<o:p></o:p></span></p>
										<p>　　　　　　<span lang="EN-US">throw new CatalogDAOSysException("<br />　　　　　　　　CatalogDAOFactory.getDAO: NamingException while <br />　　　　　　　　　　getting DAO type : \n" + ne.getMessage());<o:p></o:p></span></p>
										<p>　　　　<span lang="EN-US">} catch (Exception se) {<o:p></o:p></span></p>
										<p>　　　　　　<span lang="EN-US">throw new CatalogDAOSysException("<br />　　　　　　　　CatalogDAOFactory.getDAO: Exception while getting <br />　　　　　　　　　　DAO type : \n" + se.getMessage());<o:p></o:p></span></p>
										<p>　　　　<span lang="EN-US">}<o:p></o:p></span></p>
										<p>　　　　<span lang="EN-US">return catDao;<o:p></o:p></span></p>
										<p>　　<span lang="EN-US">}<o:p></o:p></span></p>
										<p>
												<span lang="EN-US">}<o:p></o:p></span>
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<span lang="EN-US">CatalogDAOFactory是典型的工厂方法，catDao是通过动态类装入器className获得CatalogDAOFactory具体实现子类，这个实现子类在Java宠物店是用来操作catalog数据库，用户可以根据数据库的类型不同，定制自己的具体实现子类，将自己的子类名给与CATALOG_DAO_CLASS变量就可以。<o:p></o:p></span>
		</p>
		<p>由此可见，工厂方法确实为系统结构提供了非常灵活强大的动态扩展机制，只要我们更换一下具体的工厂方法，系统其他地方无需一点变换，就有可能将系统功能进行改头换面的变化。<span lang="EN-US"><o:p></o:p></span></p>
<img src ="http://www.blogjava.net/zhanglijun33/aggbug/59801.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhanglijun33/" target="_blank">智者无疆</a> 2006-07-24 14:29 <a href="http://www.blogjava.net/zhanglijun33/archive/2006/07/24/partten2.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>进入设计模式的学习（1）</title><link>http://www.blogjava.net/zhanglijun33/archive/2006/07/24/parrten1.html</link><dc:creator>智者无疆</dc:creator><author>智者无疆</author><pubDate>Mon, 24 Jul 2006 06:07:00 GMT</pubDate><guid>http://www.blogjava.net/zhanglijun33/archive/2006/07/24/parrten1.html</guid><wfw:comment>http://www.blogjava.net/zhanglijun33/comments/59794.html</wfw:comment><comments>http://www.blogjava.net/zhanglijun33/archive/2006/07/24/parrten1.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhanglijun33/comments/commentRss/59794.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhanglijun33/services/trackbacks/59794.html</trackback:ping><description><![CDATA[学习设计模式应该是j2ee学习的最后阶段了。我知道自己的j2ee学的还不很扎实，但是工作中用到了很多模式，如果不把它们弄明白，心里总没个底，学习可能总是反复的吧。上个周末在国家图书馆里终于发现了一本不错的模式设计书。现在记录下学习过程，以备后用。<br />       设计模式的灵感来源于建筑学。一个年青的欧洲建筑师发现不同的建筑拥有不同的风格，如果有意识的把这些风格的特征以不同的模式记录下来，在以后的建筑设计之中就会很好的吸取，继承，发扬前人的设计经验。从而使工作变得高效，高质。<br /><h3 style="TEXT-ALIGN: center" align="center">设计模式之<span lang="EN-US">Singleton(单态)<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /?><o:p></o:p></span></h3><p><strong>定义</strong><span lang="EN-US">:<br />Singleton模式主要作用是保证在Java应用程序中，一个类Class只有一个实例存在。 <o:p></o:p></span></p><p>在很多操作中，比如建立目录 数据库连接都需要这样的单线程操作。<span lang="EN-US"><o:p></o:p></span></p><p>还有<span lang="EN-US">, singleton能够被状态化; 这样，多个单态类在一起就可以作为一个状态仓库一样向外提供服务，比如，你要论坛中的帖子计数器，每次浏览一次需要计数，单态类能否保持住这个计数，并且能synchronize的安全自动加1，如果你要把这个数字永久保存到数据库，你可以在不修改单态接口的情况下方便的做到。<o:p></o:p></span></p><p>另外方面，<span lang="EN-US">Singleton也能够被无状态化。提供工具性质的功能，<br /><br />Singleton模式就为我们提供了这样实现的可能。使用Singleton的好处还在于可以节省内存，因为它限制了实例的个数，有利于Java垃圾回收（garbage collection）。<br /><br />我们常常看到工厂模式中类装入器(class loader)中也用Singleton模式实现的,因为被装入的类实际也属于资源。<o:p></o:p></span></p><p><strong>如何使用<span lang="EN-US">?</span></strong><span lang="EN-US"><br />一般Singleton模式通常有几种形式:<o:p></o:p></span></p><table style="WIDTH: 100%; mso-cellspacing: 2.2pt; mso-padding-alt: 2.25pt 2.25pt 2.25pt 2.25pt" cellspacing="3" cellpadding="0" width="100%" border="0"><tbody><tr><td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: #cccccc; PADDING-BOTTOM: 2.25pt; PADDING-TOP: 2.25pt"><p><span lang="EN-US">public class Singleton {<o:p></o:p></span></p><p>　　<span lang="EN-US">private Singleton(){}<o:p></o:p></span></p><p>　　<span lang="EN-US">//在自己内部定义自己一个实例，是不是很奇怪？<br />　　//注意这是private 只供内部调用<o:p></o:p></span></p><p>　　<span lang="EN-US">private static Singleton instance = new Singleton();<o:p></o:p></span></p><p>　　<span lang="EN-US">//这里提供了一个供外部访问本class的静态方法，可以直接访问　　<br />　　public static Singleton getInstance() {<br />　　　　return instance; 　　<br />　　 } <br />} <o:p></o:p></span></p><p><span lang="EN-US"> <o:p></o:p></span></p></td></tr></tbody></table><p>第二种形式<span lang="EN-US">:<o:p></o:p></span></p><table style="WIDTH: 100%; mso-cellspacing: 2.2pt; mso-padding-alt: 2.25pt 2.25pt 2.25pt 2.25pt" cellspacing="3" cellpadding="0" width="100%" border="0"><tbody><tr><td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: #cccccc; PADDING-BOTTOM: 2.25pt; PADDING-TOP: 2.25pt"><p class="MsoNormal"><span lang="EN-US" style="FONT-SIZE: 10.5pt">public class Singleton { <o:p></o:p></span></p><p>　　<span lang="EN-US">private static Singleton instance = null;<br /><br />　　public static synchronized Singleton getInstance() {<br /><br />　　//这个方法比上面有所改进，不用每次都进行生成对象，只是第一次　　　 　<br />　　//使用时生成实例，提高了效率！<br />　　if (instance==null)<br />　　　　instance＝new Singleton();<br />　　return instance; 　　} <o:p></o:p></span></p><p><span lang="EN-US">} <o:p></o:p></span></p><p><span lang="EN-US"> <o:p></o:p></span></p></td></tr></tbody></table><p>使用<span lang="EN-US">Singleton.getInstance()可以访问单态类。<o:p></o:p></span></p><p>上面第二中形式是<span lang="EN-US">lazy initialization，也就是说第一次调用时初始Singleton，以后就不用再生成了。<o:p></o:p></span></p><p>注意到<span lang="EN-US">lazy initialization形式中的synchronized，这个synchronized很重要，如果没有synchronized，那么使用getInstance()是有可能得到多个Singleton实例。关于lazy initialization的Singleton有很多涉及double-checked locking (DCL)的讨论，有兴趣者进一步研究。<o:p></o:p></span></p><p>一般认为第一种形式要更加安全些。<span lang="EN-US"><o:p></o:p></span></p><p><strong>使用<span lang="EN-US">Singleton注意事项</span></strong>：<span lang="EN-US"><br />有时在某些情况下，使用Singleton并不能达到Singleton的目的，如有多个Singleton对象同时被不同的类装入器装载；在EJB这样的分布式系统中使用也要注意这种情况，因为EJB是跨服务器，跨JVM的。<o:p></o:p></span></p><p>我们以<span lang="EN-US">SUN公司的宠物店源码(Pet Store 1.3.1)的ServiceLocator为例稍微分析一下：<br /><br />在Pet Store中ServiceLocator有两种，一个是EJB目录下；一个是WEB目录下，我们检查这两个ServiceLocator会发现内容差不多，都是提供EJB的查询定位服务，可是为什么要分开呢？仔细研究对这两种ServiceLocator才发现区别：在WEB中的ServiceLocator的采取Singleton模式，ServiceLocator属于资源定位，理所当然应该使用Singleton模式。但是在EJB中，Singleton模式已经失去作用，所以ServiceLocator才分成两种，一种面向WEB服务的，一种是面向EJB服务的。<o:p></o:p></span></p><p><span lang="EN-US">Singleton模式看起来简单，使用方法也很方便，但是真正用好，是非常不容易，需要对Java的类 线程 内存等概念有相当的了解。<o:p></o:p></span></p><p>进一步深入可参考：<span lang="EN-US"><o:p></o:p></span></p><p><span lang="EN-US"><a href="http://www-106.ibm.com/developerworks/java/library/j-dcl.html?dwzone=java" target="_blank">Double-checked locking and the Singleton pattern</a><o:p></o:p></span></p><p><span lang="EN-US"><a href="http://www.javaworld.com/javaworld/jw-01-2001/jw-0112-singleton-p3.html#resources" target="_blank">When is a singleton not a singleton?</a><o:p></o:p></span></p><img src ="http://www.blogjava.net/zhanglijun33/aggbug/59794.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhanglijun33/" target="_blank">智者无疆</a> 2006-07-24 14:07 <a href="http://www.blogjava.net/zhanglijun33/archive/2006/07/24/parrten1.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>架构web程序</title><link>http://www.blogjava.net/zhanglijun33/archive/2006/07/05/zhanglijun33.html</link><dc:creator>智者无疆</dc:creator><author>智者无疆</author><pubDate>Wed, 05 Jul 2006 01:42:00 GMT</pubDate><guid>http://www.blogjava.net/zhanglijun33/archive/2006/07/05/zhanglijun33.html</guid><wfw:comment>http://www.blogjava.net/zhanglijun33/comments/56648.html</wfw:comment><comments>http://www.blogjava.net/zhanglijun33/archive/2006/07/05/zhanglijun33.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.blogjava.net/zhanglijun33/comments/commentRss/56648.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhanglijun33/services/trackbacks/56648.html</trackback:ping><description><![CDATA[
		<span>
				<div class="fontclear">
				</div>
				<div class="left fontsize3">这篇文章将讨论怎样组合几个著名的框架去做到松耦合的目的，怎样建立你的构架，怎样让你的各个应用层保持一致。<br />　　<strong>摘要:</strong><br /><br />　　这篇文章将讨论怎样组合几个著名的框架去做到松耦合的目的，怎样建立你的构架，怎样让你的各个应用层保持一致。富于挑战的是：组合这些框架使得每一层都以一种松耦合的方式彼此沟通，而与底层的技术无关。这篇文章将使用３种流行的开源框架来讨论组合框架的策略 <br /><br />　　其实，就算用Java建造一个不是很烦琐的web应用程序，也不是件轻松的事情。当为一个应用程序建造一个构架时有许多事情需要考虑。从高层来说，开发者需要考虑：怎样建立用户接口？在哪里处理业务逻辑？和怎样持久化应用数据。这三层每一层都有它们各自的问题需要回答。 各个层次应该使用什么技术？怎样才能把应用程序设计得松耦合和能灵活改变？构架允许层的替换不会影响到其它层吗？应用程序怎样处理容器级的服务，比如事务处理？ <br /><br />　　当为你的web应用程序创建一个构架时，需要涉及到相当多的问题。幸运的是，已经有不少开发者已经遇到过这类重复发生的问题，并且建立了处理这类问题的框架。一个好框架具备以下几点： 减轻开发者处理复杂的问题的负担（“不重复发明轮子”）；内部定义为可扩展的；有一个强大的用户群支持。框架通常能够很好的解决一方面的问题。然而，你的应用程序有几个层可能都需要它们各自的框架。就如解决你的用户接口（UI）问题时你就不应该把事务逻辑和持久化逻辑掺杂进来。例如，你不应该在控制器里面写jdbc代码，使它包含有业务逻辑，这不是控制器应该提供的功能。它应该是轻量级的，代理来自用户接口（UI）外的调用请求给其它服务于这些请求的应用层。好的框架自然的形成代码如何分布的指导。更重要的是，框架减轻开发者从头开始写像持久层这样的代码的痛苦，使他们专注于对客户来说很重要的应用逻辑。<br /><br />　　这篇文章将讨论怎样组合几个著名的框架去做到松耦合的目的，怎样建立你的构架，怎样让你的各个应用层保持一致。富于挑战的是：组合这些框架使得每一层都以一种松耦合的方式彼此沟通，而与底层的技术无关。这篇文章将使用３种流行的开源框架来讨论组合框架的策略。表现层我们将使用Struts;业务层我们将使用Spring;持久层使用Hibrenate.你也可以在你的应用程序中替换这些框架中的任何一种而得到同样的效果。图１展示了当这些框架组合在一起时从高层看是什么样子。 <br /><br /><img height="193" alt="" src="http://dev.yesky.com/imagelist/06/24/7583amu34t39.gif" width="450" /><br /><br />图1 用Struts, Spring, 和 Hibernate框架构建的概览 <br /><br />　　<strong>应用程序的分层</strong><br /><br />　　大多数不复杂的web应用都能被分成至少４个各负其责的层次。这些层次是：表现层、持久层、业务层、领域模型层。每层在应用程序中都有明确的责任，不应该和其它层混淆功能。每一应用层应该彼此独立但要给他们之间放一个通讯接口。让我们从审视各个层开始，讨论这些层应该提供什么和不应该提供什么</div>
				<div class="left fontsize3">　　<strong>表现层</strong><br /><br />　　在一个典型的web应用的一端是表现层。很多Java开发者也理解Struts所提供的。然而，太常见的是，他们把像业务逻辑之类的耦合的代码放进了一个org.apache.struts.Action。所以，让我们在像Struts这样一个框架应该提供什么上取得一致意见。这儿是Struts负责的： <br /><br />　　·为用户管理请求和响应； <br />　　·提供一个控制器代理调用业务逻辑和其它上层处理； <br />　　·处理从其它层掷出给一个Struts Action的异常； <br />　　·为显示提供一个模型； <br />　　·执行用户接口验证。<br /><br />　　这儿是一些经常用Struts编写的但是却不应该和Struts表现层相伴的项目： <br /><br />　　·直接和数据库通讯，比如JDBC调用； <br />　　·业务逻辑和与你的应用程序相关的验证； <br />　　·事务管理；<br />　　·在表现层中引入这种代码将导致典型耦合和讨厌的维护。<br /><br />　　<strong>持久层</strong><br /><br />　　在典型web应用的另一端是持久层。这通常是使事情迅速失控的地方。开发者低估了构建他们自己的持久层框架的挑战性。一般来说，机构内部自己写的持久层不仅需要大量的开发时间，而且还经常缺少功能和变得难以控制。有几个开源的“对象－关系映射”框架非常解决问题。尤其是，Hibernate框架为java提供了＂对象－关系持久化＂机制和查询服务。Hibernate对那些已经熟悉了SQL和JDBC API的Java开发者有一个适中的学习曲线。Hibernate持久对象是基于简单旧式Java对象和Java集合。此外，使用Hibernate并不妨碍你正在使用的IDE。下面的列表包含了你该写在一个持久层框架里的代码类型：<br /><br />　　查询相关的信息成为对象。Hibernate通过一种叫作HQL的面向对象的查询语言或者使用条件表达式API来做这个事情。 HQL非常类似于SQL-- 只是把SQL里的table和columns用Object和它的fields代替。有一些新的专用的HQL语言成分要学；不过，它们容易理解而且文档做得好。HQL是一种使用来查询对象的自然语言，花很小的代价就能学习它。<br /><br />　　保存、更新、删除储存在数据库中的信息。 <br /><br />　　像Hibernate这样的高级“对象－关系”映射框架提供对大多数主流SQL数据库的支持，它们支持“父/子”关系、事务处理、继承和多态。<br /><br />　　这儿是一些应该在持久层里被避免的项目：<br /><br />　　业务逻辑应该在你的应用的一个高一些的层次里。持久层里仅仅允许数据存取操作。 <br /><br />　　你不应该把持久层逻辑和你的表现层逻辑搅在一起。避免像JSPs或基于servlet的类这些表现层组件里的逻辑和数据存取直接通讯。通过把持久层逻辑隔离进它自己的层，应用程序变得易于修改而不会影响在其它层的代码。例如：Hebernate能够被其它持久层框架或者API代替而不会修改在其它任何层的代码。 <br /><br />　　<strong>业务层</strong><br /><br />　　在一个典型的web应用程序的中间的组件是业务层或服务层。从编码的视角来看，这个服务层是最容易被忽视的一层。不难在用户接口层或者持久层里找到散布在其中的这种类型的代码。这不是正确的地方，因为这导致了应用程序的紧耦合，这样一来，随着时间推移代码将很难维护。幸好，针对这一问题有好几种Frameworks存在。在这个领域两个最流行的框架是Spring和PicoContainer，它们叫作微容器，你可以不费力不费神的把你的对象连在一起。所有这些框架都工作在一个简单的叫作“依赖注入”（也通称“控制反转”）的概念上。这篇文章将着眼于Spring的为指定的配置参数通过bean属性的setter注入的使用。Spring也提供了一个构建器注入的复杂形式作为setter注入的一个替代。对象们被一个简单的XML文件连在一起，这个XML文件含有到像事务管理器、对象工厂、包含业务逻辑的服务对象、和数据存取对象这些对象的引用。 <br /><br />　　这篇文章的后面将用例子来把Spring使用这些概念的方法说得更清楚一些。业务层应该负责下面这些事情：<br /><br />　　·处理应用程序的业务逻辑和业务验证； <br />　　·管理事务； <br />　　·预留和其它层交互的接口； <br />　　·管理业务层对象之间的依赖； <br />　　·增加在表现层和持久层之间的灵活性，使它们互不直接通讯； <br />　　·从表现层中提供一个上下文给业务层获得业务服务； <br />　　·管理从业务逻辑到持久层的实现。 <br /><br />　　<strong>领域模型层</strong><br /><br />　　最后，因为我们讨论的是一个不是很复杂的、基于web的应用程序，我们需要一组能在不同的层之间移动的对象。领域对象层由那些代表现实世界中的业务对象的对象们组成，比如：一份订单、订单项、产品等等。这个层让开发者停止建立和维护不必要的数据传输对象（或者叫作DTOs）,来匹配他们的领域对象。例如，Hibernate允许你把数据库信息读进领域对象的一个对象图，这样你可以在连接断开的情况下把这些数据显示到UI层。那些对象也能被更新和送回到持久层并在数据库里更新。而且，你不必把对象转化成DTOs，因为DTOs在不同的应用层间移动，可能在转换中丢失。这个模型使得Java开发者自然地以一种面向对象的风格和对象打交道，没有附加的编码。<br /><br />　　<strong>结合一个简单的例子</strong><br /><br />　　既然我们已经从一个高的层次上理解了这些组件， 现在就让我们开始实践吧。在这个例子中，我们还是将合并Struts、Spring、Hibernate框架。每一个这些框架在一篇文章中都有太多的细节覆盖到。这篇文章将用一个简单的例子代码展示怎样把它们结合在一起，而不是进入每个框架的许多细节。示例应用程序将示范一个请求怎样跨越每一层被服务的。这个示例应用程序的一个用户能保存一个订单到数据库中和查看一个在数据库中存在的订单。进一步的增强可以使用户更新或删除一个存在的订单。<br /><br />　　因为领域对象将和每一层交互，我们将首先创建它们。这些对象将使我们定义什么应该被持久化，什么业务逻辑应该被提供，和哪种表现接口应该被设计。然后，我们将配置持久层和用Hibernate为我们的领域对象定义“对象-关系”映射。然后，我们将定义和配置我们的业务对象。在有了这些组件后，我们就能讨论用Spring把这些层连在一起。最后，我们将提供一个表现层，它知道怎样和业务服务层交流和知道怎样处理从其它层产生的异常。<br /><br />　　<strong>领域对象层</strong><br /><br />　　因为这些对象将和所有层交互，这也许是一个开始编码的好地方。这个简单的领域模型将包括一个代表一份订单的对象和一个代表一个订单项的对象。订单对象将和一组订单项对象有一对多的关系。例子代码在领域层有两个简单的对象：<br /><br />　　·com.meagle.bo.Order.java: 包括一份订单的概要信息；<br />　　·com.meagle.bo.OrderLineItem.java: 包括一份订单的详细信息；<br /><br />　　考虑一下为你的对象选择包名，它将反映你的应用程序是怎样分层的。例如：简单应用的领域对象可以放进com.meagle.bo包。更多专门的领域对象将放入在com.meagle.bo下面的子包里。业务逻辑在com.meagle.service包里开始打包，DAO对象放进com.meagle.service.dao.hibernate包。对于forms和actions的表现类分别放入com.meagle.action 和 com.meagle.forms包。准确的包命名为你的类提供的功能提供一个清楚的区分，使当故障维护时更易于维护，和当给应用程序增加新的类或包时提供一致性。</div>
				<div class="left fontsize3">　　<strong>持久层配置</strong><br /><br />　　用Hibernate设置持久层涉及到几个步骤。第一步是进行配置持久化我们的领域业务对象。因为我们用于领域对象持久化的Hibernate和POJOs一起工作，因此，订单和订单项对象包括的所有的字段的都需要提供getter和setter方法。订单对象将包括像ID、用户名、合计、和订单项这样一些字段的标准的JavaBean格式的setter和getter方法。订单项对象将同样的用JavaBean的格式为它的字段设置setter和getter方法。<br /><br />　　Hibernate在XML文件里映射领域对象到关系数据库。订单和订单项对象将有两个映射文件来表达这种映射。有像XDoclet这样的工具来帮助这种映射。Hibernate将映射领域对象到这些文件：<br /><br />　　Order.hbm.xml <br />　　OrderLineItem.hbm.xml<br /><br />　　你可以在WebContent/WEB-INF/classes/com/meagle/bo目录里找到这些生成的文件。配置Hibernate SessionFactory使它知道是在和哪个数据库通信，使用哪个数据源或连接池，加载哪些持久对象。SessionFactory提供的Session对象是Java对象和像选取、保存、更新、删除对象这样一些持久化功能间的翻译接口。我们将在后面的部分讨论Hibernate操作Session对象需要的SessionFactory配置。<br /><br />　　<strong>业务层配置</strong><br /><br />　　既然我们已经有了领域对象，我们需要有业务服务对象来执行应用逻辑、执行向持久层的调用、获得从用户接口层的请求、处理事务、处理异常。为了将所有这些连接起来并且易于管理，我们将使用Spring框架的bean管理方面。Spring使用“控制反转”,或者“setter依赖注入”来把这些对象连好，这些对象在一个外部的XML文件中被引用。“控制反转”是一个简单的概念，它允许对象接受其它的在一个高一些的层次被创建的对象。使用这种方法，你的对象从必须创建其它对象中解放出来并降低对象耦合。<br /><br />　　这儿是个不使用IoC的对象创建它的从属对象的例子，这导致紧的对象耦合：<br /><br /><img height="212" alt="" src="http://dev.yesky.com/imagelist/06/24/k3q6gejl431m.gif" width="250" /><br /><br />　　图2：没有使用IoC的对象组织。对象Ａ创建对象Ｂ和Ｃ。<br /><br />　　这儿是一个使用IoC的例子，它允许对象在一个高一些层次被创建和传进另外的对象，所以另外的对象能直接使用现成的对象·［译者注：另外的对象不必再亲自创建这些要使用的对象］：<br /><br /><img height="210" alt="" src="http://dev.yesky.com/imagelist/06/24/gf5j4beg3c05.gif" width="250" /><br />图3：对象使用IoC组织。对象Ａ包含setter方法，它们接受到对象Ｂ和Ｃ的接口。这也可以用对象Ａ里的接受对象Ｂ和Ｃ的构建器完成。</div>
				<div class="left fontsize3">　　<strong>建立我们的业务服务对象</strong><br /><br />　　我们将在我们的业务对象中使用的setter方法接受的是接口，这些接口允许对象的松散定义的实现，这些对象将被设置或者注入。在我们这个例子里我们将使我们的业务服务对象接受一个DAO去控制我们的领域对象的持久化。当我们在这篇文章的例子中使用Hibernate，我们可以容易的转换到一个不同的持久框架的实现，通知Spring使用新的实现的DAO对象。你能明白编程到接口和使用“依赖注入”模式是怎样宽松耦合你的业务逻辑和你的持久化机制的。<br /><br />　　这儿是业务服务对象的接口，它是一个DAO对象依赖的桩。<br /><br /><table width="90%" align="center" border="1"><tbody><tr><td class="main" bgcolor="#cccccc">public interface IOrderService {<br />　public abstract Order saveNewOrder(Order order)<br />　　throws OrderException,<br />　　　　　 OrderMinimumAmountException;<br />　public abstract List findOrderByUser(String user)<br />　　　　　　　　　　　　　 throws OrderException;<br />　public abstract Order findOrderById(int id)<br />　　　　　　　　　　　　　 throws OrderException;<br />　public abstract void setOrderDAO(IOrderDAO orderDAO);<br />} </td></tr></tbody></table><br />　　注意上面的代码有一个为DAO对象准备的setter方法。这儿没有一个getOrderDAO方法因为它不是必要的，因为不太有从外面访问连着的OrderDAO对象的需要。DAO对象将被用来和我们的持久层沟通。我们将用Spring把业务服务对象和DAO对象连在一起。因为我们编码到接口，我们不会紧耦合实现。 <br /><br />　　下一步是写我们的DAO实现对象。因为Spring有内建的对Hibernate的支持，这个例子DAO将继承HibernateDaoSupport类，这使得我们容易取得一个到HibernateTemplate类的引用，HibernateTemplate是一个帮助类，它能简化Hibernate Session的编码和处理HibernateExceptions。这儿是DAO的接口： <br /><br /><table width="90%" align="center" border="1"><tbody><tr><td class="main" bgcolor="#cccccc">public interface IOrderDAO { <br />　public abstract Order findOrderById(final int id);<br />　public abstract List findOrdersPlaceByUser(final String placedBy);<br />　public abstract Order saveOrder(final Order order);<br />} </td></tr></tbody></table><br />　　我们还有两个对象要和我们的业务层连在一起。这包括HibernateSessionFactory和一个TransactionManager对象。这在Spring配置文件里直接完成。Spring提供一个HibernateTransactionManager，它将从工厂绑定一个Hibernate Session到一个线程来支持事务。这儿是HibernateSessionFactory和HibernateTransactionManager的Spring配置。 <br /><br /><table width="90%" align="center" border="1"><tbody><tr><td class="main" bgcolor="#cccccc">＜bean id="mySessionFactory"<br />　　　 class="org.springframework.orm.hibernate.<br />　　　　　　　LocalSessionFactoryBean"＞<br />　＜property name="mappingResources"＞ <br />　　＜list＞ <br />　　　＜value＞<br />　　　　com/meagle/bo/Order.hbm.xml<br />　　　＜/value＞<br />　　　＜value＞<br />　　　　com/meagle/bo/OrderLineItem.hbm.xml<br />　　　＜/value＞<br />　　＜/list＞<br />　＜/property＞<br />　＜property name="hibernateProperties"＞<br />　　＜props＞<br />　　　＜prop key="hibernate.dialect"＞<br />　　　　net.sf.hibernate.dialect.MySQLDialect<br />　　　＜/prop＞<br />　　　＜prop key="hibernate.show_sql"＞<br />　　　　false<br />　　　＜/prop＞<br />　　　＜prop key="hibernate.proxool.xml"＞<br />　　　　C:/MyWebApps/.../WEB-INF/proxool.xml<br />　　　＜/prop＞<br />　　　＜prop key="hibernate.proxool.pool_alias"＞<br />　　　　　spring<br />　　　＜/prop＞<br />　　＜/props＞<br />　＜/property＞<br />＜/bean＞<br />＜!-- Transaction manager for a single Hibernate<br />SessionFactory (alternative to JTA) --＞<br />＜bean id="myTransactionManager"<br />　　　　 class="org.<br />　 　　　　　　 springframework.<br />　　　　　　　　orm.<br />　　　　　　　　hibernate.<br />　　　　　　　　HibernateTransactionManager"＞<br />　＜property name="sessionFactory"＞<br />　　＜ref local="mySessionFactory"/＞<br />　＜/property＞<br />　＜/bean＞ </td></tr></tbody></table><br />　　每一个对象能被Spring配置里的一个＜bean＞标记引用。在这个例子里，bean “mySessionFactory”代表一个HibernateSessionFactory，bean “myTransactionManager”代表一个Hibernate transaction manager。注意transactionManger bean有一个叫作sessionFactory的属性元素。HibernateTransactionManager有一个为sessionFactory准备的setter和getter方法，它们是用来当Spring容器启动时的依赖注入。sessionFactory属性引用mySessionFactory bean。这两个对象现在当Spring容器初始化时将被连在一起。这种连接把你从为引用和创建这些对象而创建singleton对象和工厂中解放出来，这减少了你应用程序中的代码维护。mySessionFactory bean有两个属性元素,它们翻译成为mappingResources 和 hibernatePropertes准备的setter方法。通常，如果你在Spring之外使用Hibernate,这个配置将被保存在hibernate.cfg.xml文件中。不管怎样,Spring提供了一个便捷的方式--在Spring配置文件中合并Hibernate的配置。 <br /><br />　　既然我们已经配置了我们的容器服务beans和把它们连在了一起，我们需要把我们的业务服务对象和我们的DAO对象连在一起。然后，我们需要把这些对象连接到事务管理器。<br /><br />　　这是在Spring配置文件里的样子：<br /><br /><table width="90%" align="center" border="1"><tbody><tr><td class="main" bgcolor="#cccccc">＜!-- ORDER SERVICE --＞ <br />＜bean id="orderService"<br />　class="org.<br />　　　　 springframework.<br />　　　　 transaction.<br />　　　　 interceptor.<br />　　　　 TransactionProxyFactoryBean"＞<br />　＜property name="transactionManager"＞<br />　　＜ref local="myTransactionManager"/＞<br />　＜/property＞<br />　＜property name="target"＞<br />　　＜ref local="orderTarget"/＞<br />　＜/property＞<br />　＜property name="transactionAttributes"＞<br />　　＜props＞<br />　　　＜prop key="find*"＞<br />　　PROPAGATION_REQUIRED,readOnly,-OrderException<br />　　　＜/prop＞<br />　　　＜prop key="save*"＞<br />　　PROPAGATION_REQUIRED,-OrderException<br />　　 ＜/prop＞<br />　　＜/props＞<br />　＜/property＞<br />＜/bean＞<br />＜!-- ORDER TARGET PRIMARY BUSINESS OBJECT:<br />Hibernate implementation --＞<br />＜bean id="orderTarget"<br />　　　　 class="com.<br />　　　　　　　　meagle.<br />　　　　　　　　service.<br />　　　　　　　　spring.<br />　　　　　　　　OrderServiceSpringImpl"＞<br />　＜property name="orderDAO"＞<br />　　＜ref local="orderDAO"/＞<br />　＜/property＞<br />＜/bean＞<br />＜!-- ORDER DAO OBJECT --＞<br />＜bean id="orderDAO"<br />　　　　 class="com.<br />　　　　　　　　meagle.<br />　　　　　　　　service.<br />　　　　　　　　dao.<br />　　　　　　　　hibernate.<br />　　　　　　　　OrderHibernateDAO"＞<br />　＜property name="sessionFactory"＞<br />　　＜ref local="mySessionFactory"/＞<br />　＜/property＞<br />＜/bean＞ </td></tr></tbody></table><br />　　图4是我们已经连在一起的东西的一个概览。它展示了每个对象是怎样相关联的和怎样被Spring设置进其它对象中。把这幅图和示例应用中的Spring配置文件对比查看它们之间的关系。 <br /><br /><img height="352" alt="" src="http://dev.yesky.com/imagelist/06/24/zj7n69115h6l.gif" width="436" /><br />图4：这是Spring怎样将在这个配置的基础上装配beans。<br /><br />　　这个例子使用一个TransactionProxyFactoryBean，它有一个为我们已经定义了的事务管理者准备的setter方法。这是一个有用的对象，它知道怎样处理声明的事务操作和你的服务对象。你可以通过transactionAttributes属性定义事务怎样被处理，transactionAttributes属性为方法名定义模式和它们怎样参与进一个事务。<br /><br />　　TransactionProxyFactoryBean类也有一个为一个target准备的setter,target将是一个到我们的叫作orderTarget的业务服务对象的引用。 orderTarget bean定义使用哪个业务服务对象并有一个指向setOrderDAO()的属性。orderDAO bean将居于这个属性中，orderDAO bean是我们的和持久层交流的DAO对象。<br /><br />　　还有一个关于Spring和bean要注意的是bean能以两种模式工作。这两种模式被定义为singleton和prototype。一个bean默认的模式是singleton，意味着一个共享的bean的实例将被管理。这是用于无状态操作--像一个无状态会话bean将提供的那样。当bean由Spring提供时，prototype模式允许创建bean的新实例。你应当只有在每一个用户都需要他们自己的bean的拷贝时才使用prototype模式。<br /><strong>提供一个服务定位器</strong><br /><br />　　既然我们已经把我们的服务和我们的DAO连起来了，我们需要把我们的服务暴露给其它层。通常是一个像使用Struts或Swing这样的用户接口层里的代码来使用这个服务。一个简单的处理方法是使用一个服务定位器模式的类从一个Spring上下文中返回资源。这也可以靠引用bean ID通过Spring来直接完成。<br /><br />　　这儿是一个在Struts Action中怎样配置一个服务定位器的例子：<br /><div><center></center><span><table width="90%" align="center" border="1"><tbody><tr><td class="main" bgcolor="#cccccc">public abstract class BaseAction extends Action {<br />　private IOrderService orderService; <br />　public void setServlet(ActionServlet<br />　　　　　　　　　　　　　　　　 actionServlet) {<br />　　super.setServlet(actionServlet);<br />　　ServletContext servletContext =<br />　　　　　　　 actionServlet.getServletContext();<br />　　WebApplicationContext wac =<br />　　　WebApplicationContextUtils.<br />　　　　 getRequiredWebApplicationContext(<br />　　　　　　　　　　　　　　　　 servletContext);<br />　　　this.orderService = (IOrderService)<br />　　　　　　　　　　 wac.getBean("orderService");<br />　}<br />　protected IOrderService getOrderService() {<br />　　return orderService;<br />　}<br />} </td></tr></tbody></table><br />　　<strong>用户接口层配置</strong><br /><br />　　示例应用的用户接口层使用Struts框架。这儿我们将讨论当为一个应用分层时和Struts相关的部分。让我们从在struts-config.xml文件里检查一个Action配置开始。<br /><br /><table width="90%" align="center" border="1"><tbody><tr><td class="main" bgcolor="#cccccc">＜action path="/SaveNewOrder" <br />　　type="com.meagle.action.SaveOrderAction"<br />　　name="OrderForm"<br />　　scope="request"<br />　　validate="true"<br />　　input="/NewOrder.jsp"＞<br />　＜display-name＞Save New Order＜/display-name＞<br />　＜exception key="error.order.save"<br />　　path="/NewOrder.jsp"<br />　　scope="request"<br />　　type="com.meagle.exception.OrderException"/＞<br />　＜exception key="error.order.not.enough.money"<br />　　path="/NewOrder.jsp"<br />　　scope="request"<br />　　type="com.<br />　　meagle.<br />　　exception.<br />　　OrderMinimumAmountException"/＞<br />　＜forward name="success" path="/ViewOrder.jsp"/＞<br />　＜forward name="failure" path="/NewOrder.jsp"/＞<br />＜/action＞<br /></td></tr></tbody></table><br />　　SaveNewOrder Action被用来持久化一个用户从用户接口层提交的订单。这是一个典型的Struts Action；然而，注意这个action的异常配置。这些Exceptions为我们的业务服务对象也在Spring 配置文件中配置了。当这些异常被从业务层掷出我们能在我们的用户接口里恰当的处理它们。第一个异常，OrderException，当在持久层里保存订单对象失败时将被这个action使用。这将引起事务回滚和通过业务对象传递把异常传回给Struts层。OrderMinimumAmountException，在业务对象逻辑里的一个事务因为提交的订单达不到最小订单数量而失败也将被处理。然后，事务将回滚和这个异常能被用户接口层恰当的处理。 <br /><br />　　最后一个连接步骤是使我们的表现层和我们的业务层交互。这已经通过使用前面讨论的服务定位器来完成了。服务层充当一个到我们的业务逻辑和持久层的接口。这儿是 Struts中的SaveNewOrder Action可能怎样使用一个服务定位器调用一个业务方法： <br /><br /><table width="90%" align="center" border="1"><tbody><tr><td class="main" bgcolor="#cccccc">public ActionForward execute( <br />　ActionMapping mapping,<br />　ActionForm form,<br />　javax.servlet.http.HttpServletRequest request,<br />　javax.servlet.http.HttpServletResponse response)<br />　throws java.lang.Exception {<br />　OrderForm oForm = (OrderForm)form;<br />　// Use the form to build an Order object that<br />　// can be saved in the persistence layer.<br />　// See the full source code in the sample app.<br />　// Obtain the wired business service object<br />　// from the service locator configuration <br />　// in BaseAction.<br />　// Delegate the save to the service layer and <br />　// further upstream to save the Order object.<br />　getOrderService().saveNewOrder(order);<br />　oForm.setOrder(order);<br />　ActionMessages messages = new ActionMessages();<br />　messages.add( <br />　　　ActionMessages.GLOBAL_MESSAGE,<br />new ActionMessage( <br />　　　"message.order.saved.successfully")); <br />　saveMessages(request, messages);<br />　return mapping.findForward("success");<br />} </td></tr></tbody></table><br />　　<strong>结论</strong><br /><br />　　这篇文章按照技术和架构覆盖了许多话题。从中而取出的主要思想是怎样更好的给你的应用程序分层：用户接口层、持久逻辑层、和其它任何你需要的应用层。这样可以解耦你的代码，允许添加新的代码组件，使你的应用在将来更易维护。这里覆盖的技术能很好的解决这类的问题。不管怎样，使用这样的构架可以让你用其他技术代替现在的层。例如，你也许不想使用Hibernate持久化。因为你在你的DAO对象中编码到接口，你能怎样使用其它的技术或框架，比如 iBATIS，作为一个替代是显而易见的。或者你可能用不同于Struts的框架替代你的UI层。改变UI层的实现不会直接影响你的业务逻辑层或者你的持久层。替换你的持久层不会影响你的UI逻辑或业务服务层。集成一个web应用其实也不是一件烦琐的工作，靠解耦你的各应用层和用适当的框架组成它，它能变得更容易处理</span></div></div>
		</span>
<img src ="http://www.blogjava.net/zhanglijun33/aggbug/56648.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhanglijun33/" target="_blank">智者无疆</a> 2006-07-05 09:42 <a href="http://www.blogjava.net/zhanglijun33/archive/2006/07/05/zhanglijun33.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>