﻿<?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-时间-文章分类-软件设计与架构</title><link>http://www.blogjava.net/lvq810/category/31966.html</link><description>世间最可贵的就是“今”，最易丧失得也是“今”。因为它最容易丧失，所以更觉得它宝贵。</description><language>zh-cn</language><lastBuildDate>Tue, 30 Dec 2008 03:29:41 GMT</lastBuildDate><pubDate>Tue, 30 Dec 2008 03:29:41 GMT</pubDate><ttl>60</ttl><item><title>关于用户角色权限的一点想法</title><link>http://www.blogjava.net/lvq810/articles/225985.html</link><dc:creator>lvq810</dc:creator><author>lvq810</author><pubDate>Sun, 31 Aug 2008 17:21:00 GMT</pubDate><guid>http://www.blogjava.net/lvq810/articles/225985.html</guid><wfw:comment>http://www.blogjava.net/lvq810/comments/225985.html</wfw:comment><comments>http://www.blogjava.net/lvq810/articles/225985.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lvq810/comments/commentRss/225985.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lvq810/services/trackbacks/225985.html</trackback:ping><description><![CDATA[前言：<br />
权限往往是一个极其复杂的问题，但也可简单表述为这样的逻辑表达式：判断&#8220;Who对What(Which)进行How的操作&#8221;的逻辑表达式是否为真。针对不同的应用，需要根据项目的实际情况和具体架构，在维护性、灵活性、完整性等N多个方案之间比较权衡，选择符合的方案。<br />
目标：<br />
直观，因为系统最终会由最终用户来维护，权限分配的直观和容易理解，显得比较重要，系统不辞劳苦的实现了组的继承，除了功能的必须，更主要的就是因为它足够直观。<br />
简单，包括概念数量上的简单和意义上的简单还有功能上的简单。想用一个权限系统解决所有的权限问题是不现实的。设计中将常常变化的&#8220;定制&#8221;特点比较强的部分判断为业务逻辑，而将常常相同的&#8220;通用&#8221;特点比较强的部分判断为权限逻辑就是基于这样的思路。<br />
扩展，采用可继承在扩展上的困难。的Group概念在支持权限以组方式定义的同时有效避免了重定义时<br />
现状：<br />
对于在企业环境中的访问控制方法，一般有三种：<br />
1.自主型访问控制方法。目前在我国的大多数的信息系统中的访问控制模块中基本是借助于自主型访问控制方法中的访问控制列表(ACLs)。<br />
2.强制型访问控制方法。用于多层次安全级别的军事应用。<br />
3.基于角色的访问控制方法（RBAC）。是目前公认的解决大型企业的统一资源访问控制的有效方法。其显著的两大特征是：1.减小授权管理的复杂性，降低管理开销。2.灵活地支持企业的安全策略，并对企业的变化有很大的伸缩性。<br />
名词：<br />
粗粒度：表示类别级，即仅考虑对象的类别(the&nbsp;type&nbsp;of&nbsp;object)，不考虑对象的某个特定实例。比如，用户管理中，创建、删除，对所有的用户都一视同仁，并不区分操作的具体对象实例。<br />
细粒度：表示实例级，即需要考虑具体对象的实例(the&nbsp;instance&nbsp;of&nbsp;object)，当然，细粒度是在考虑粗粒度的对象类别之后才再考虑特定实例。比如，合同管理中，列表、删除，需要区分该合同实例是否为当前用户所创建。<br />
原则：<br />
权
限逻辑配合业务逻辑。即权限系统以为业务逻辑提供服务为目标。相当多细粒度的权限问题因其极其独特而不具通用意义，它们也能被理解为是&#8220;业务逻辑&#8221;的一部
分。比如，要求：&#8220;合同资源只能被它的创建者删除，与创建者同组的用户可以修改，所有的用户能够浏览&#8221;。这既可以认为是一个细粒度的权限问题，也可以认为
是一个业务逻辑问题。在这里它是业务逻辑问题，在整个权限系统的架构设计之中不予过多考虑。当然，权限系统的架构也必须要能支持这样的控制判断。或者
说，&nbsp;系统提供足够多但不是完全的控制能力。即，设计原则归结为：&#8220;系统只提供粗粒度的权限，细粒度的权限被认为是业务逻辑的职责&#8221;。<br />
需要再次强
调的是，这里表述的权限系统仅是一个&#8220;不完全&#8221;的权限系统，即，它不提供所有关于权限的问题的解决方法。它提供一个基础，并解决那些具有&#8220;共性&#8221;的(或者
说粗粒度的)部分。在这个基础之上，根据&#8220;业务逻辑&#8221;的独特权限需求，编码实现剩余部分(或者说细粒度的)部分，才算完整。回到权限的问题公式，通用的设
计仅解决了Who+What+How&nbsp;的问题，其他的权限问题留给业务逻辑解决。<br />
概念：<br />
Who：权限的拥用者或主体（Principal、User、Group、Role、Actor等等）<br />
What：权限针对的对象或资源（Resource、Class）。<br />
How：具体的权限（Privilege,&nbsp;正向授权与负向授权）。<br />
Role：是角色，拥有一定数量的权限。<br />
Operator：操作。表明对What的How&nbsp;操作。<br />
说明：<br />
User：与&nbsp;Role&nbsp;相关，用户仅仅是纯粹的用户，权限是被分离出去了的。User是不能与&nbsp;Privilege&nbsp;直接相关的，User&nbsp;要拥有对某种资源的权限，必须通过Role去关联。解决&nbsp;Who&nbsp;的问题。<br />
Resource：就是系统的资源，比如部门新闻，文档等各种可以被提供给用户访问的对象。资源可以反向包含自身，即树状结构，每一个资源节点可以与若干指定权限类别相关，可定义是否将其权限应用于子节点。<br />
Privilege：
是Resource&nbsp;Related的权限。就是指，这个权限是绑定在特定的资源实例上的。比如说部门新闻的发布权限，叫做"部门新闻发布权限"。这就表
明，该Privilege是一个发布权限，而且是针对部门新闻这种资源的一种发布权限。Privilege是由Creator在做开发时就确定的。权限，
包括系统定义权限和用户自定义权限用户自定义权限之间可以指定排斥和包含关系(如：读取，修改，管理三个权限，管理&nbsp;权限&nbsp;包含&nbsp;前两种权限)。
Privilege&nbsp;如"删除"&nbsp;是一个抽象的名词，当它不与任何具体的&nbsp;Object&nbsp;或&nbsp;Resource&nbsp;绑定在一起时是没有任何意义的。拿新闻发
布来说，发布是一种权限，但是只说发布它是毫无意义的。因为不知道发布可以操作的对象是什么。只有当发布与新闻结合在一起时，才会产生真正
的&nbsp;Privilege。这就是&nbsp;Privilege&nbsp;Instance。权限系统根据需求的不同可以延伸生很多不同的版本。<br />
Role：是粗粒度和细粒度(业务逻辑)的接口，一个基于粗粒度控制的权限框架软件，对外的接口应该是Role，具体业务实现可以直接继承或拓展丰富Role的内容，Role不是如同User或Group的具体实体，它是接口概念，抽象的通称。<br />
Group：
用户组，权限分配的单位与载体。权限不考虑分配给特定的用户。组可以包括组(以实现权限的继承)。组可以包含用户，组内用户继承组的权限。Group要实
现继承。即在创建时必须要指定该Group的Parent是什么Group。在粗粒度控制上，可以认为，只要某用户直接或者间接的属于某个Group那么
它就具备这个Group的所有操作许可。细粒度控制上，在业务逻辑的判断中，User仅应关注其直接属于的Group，用来判断是否&#8220;同组&#8221;&nbsp;。
Group是可继承的，对于一个分级的权限实现，某个Group通过&#8220;继承&#8221;就已经直接获得了其父Group所拥有的所有&#8220;权限集合&#8221;，对这个Group
而言，需要与权限建立直接关联的，仅是它比起其父Group需要&#8220;扩展&#8221;的那部分权限。子组继承父组的所有权限，规则来得更简单，同时意味着管理更容易。
为了更进一步实现权限的继承，最直接的就是在Group上引入&#8220;父子关系&#8221;。<br />
User与Group是多对多的关系。即一个User可以属于多个
Group之中，一个Group可以包括多个User。子Group与父Group是多对一的关系。Operator某种意义上类似于
Resource&nbsp;+&nbsp;Privilege概念，但这里的Resource仅包括Resource&nbsp;Type不表示Resource&nbsp;Instance。
Group&nbsp;可以直接映射组织结构，Role&nbsp;可以直接映射组织结构中的业务角色，比较直观，而且也足够灵活。Role对系统的贡献实质上就是提供了一个
比较粗颗粒的分配单位。<br />
Group与Operator是多对多的关系。各概念的关系图示如下：&nbsp;&nbsp;<br />
&nbsp;<br />
&nbsp;<img alt="" src="http://www.blogjava.net/images/blogjava_net/lvq810/CSDN_Dev_Image_2003-7-171114050.jpg" height="181" width="640" /><br />
解释：<br />
Operator
的定义包括了Resource&nbsp;Type和Method概念。即，What和How的概念。之所以将What和How绑定在一起作为一个Operator
概念而不是分开建模再建立关联，这是因为很多的How对于某What才有意义。比如，发布操作对新闻对象才有意义，对用户对象则没有意义。<br />
How
本身的意义也有所不同，具体来说，对于每一个What可以定义N种操作。比如，对于合同这类对象，可以定义创建操作、提交操作、检查冲突操作等。可以认
为，How概&nbsp;念对应于每一个商业方法。其中，与具体用户身份相关的操作既可以定义在操作的业务逻辑之中，也可以定义在操作级别。比如，创建者的浏览视图
与普通用户的浏&nbsp;览视图要求内容不同。既可以在外部定义两个操作方法，也可以在一个操作方法的内部根据具体逻辑进行处理。具体应用哪一种方式应依据实际情
况进行处理。<br />
这样的架构，应能在易于理解和管理的情况下，满足绝大部分粗粒度权限控制的功能需要。但是除了粗粒度权限，系统中必然还会包括无数对具体Instance的细粒度权限。这些问题，被留给业务逻辑来解决，这样的考虑基于以下两点：<br />
一&nbsp;方
面，细粒度的权限判断必须要在资源上建模权限分配的支持信息才可能得以实现。比如，如果要求创建者和普通用户看到不同的信息内容，那么，资源本身应该
有&nbsp;其创建者的信息。另一方面，细粒度的权限常常具有相当大的业务逻辑相关性。对不同的业务逻辑，常常意味着完全不同的权限判定原则和策略。相比之下，粗
粒度&nbsp;的权限更具通用性，将其实现为一个架构，更有重用价值；而将细粒度的权限判断实现为一个架构级别的东西就显得繁琐，而且不是那么的有必要，用定制的
代码来&nbsp;实现就更简洁，更灵活。<br />
所以细粒度控制应该在底层解决，Resource在实例化的时候，必需指定Owner和
GroupPrivilege在对Resource进行操作时也必然会确定约束类型：究竟是OwnerOK还是GroupOK还是AllOK。Group
应和Role严格分离User和Group是多对多的关系，Group只用于对用户分类，不包含任何Role的意义；Role只授予User，而不是
Group。如果用户需要还没有的多种Privilege的组合，必须新增Role。Privilege必须能够访问Resource，同时带User参
数，这样权限控制就完备了。<br />
思想：<br />
权限系统的核心由以下三部分构成：1.创造权限，2.分配权限，3.使用权限，然后，系统各部分的主要参与者对照如下：1.创造权限&nbsp;-&nbsp;Creator创造，2.分配权限&nbsp;-&nbsp;Administrator&nbsp;分配，3.使用权限&nbsp;-&nbsp;User：<br />
1.&nbsp;Creator&nbsp;创
造&nbsp;Privilege，&nbsp;Creator&nbsp;在设计和实现系统时会划分，一个子系统或称为模块，应该有哪些权限。这里完成的
是&nbsp;Privilege&nbsp;与&nbsp;Resource&nbsp;的对象声明，并没有真正将&nbsp;Privilege&nbsp;与具体Resource&nbsp;实例联系在一起，形成
Operator。<br />
2.&nbsp;Administrator&nbsp;指定&nbsp;Privilege&nbsp;与&nbsp;Resource&nbsp;Instance&nbsp;的关联。在这一
步，&nbsp;权限真正与资源实例联系到了一起，&nbsp;产生了Operator（Privilege&nbsp;Instance）。Administrator利用
Operator这个基本元素，来创造他理想中的权限模型。如，创建角色，创建用户组，给用户组分配用户，将用户组与角色关联等等...这些操作都是
由&nbsp;Administrator&nbsp;来完成的。<br />
3.&nbsp;User&nbsp;使用&nbsp;Administrator&nbsp;分配给的权限去使用各个子系统。
Administrator&nbsp;是用户，在他的心目中有一个比较适合他管理和维护的权限模型。于是，程序员只要回答一个问题，就是什么权限可以访问什么资
源，也就是前面说的&nbsp;Operator。程序员提供&nbsp;Operator&nbsp;就意味着给系统穿上了盔甲。Administrator&nbsp;就可以按照他的意愿来建
立他所希望的权限框架可以自行增加，删除，管理Resource和Privilege之间关系。可以自行设定用户User和角色Role的对应关系。(如
果将&nbsp;Creator看作是&nbsp;Basic&nbsp;的发明者，&nbsp;Administrator&nbsp;就是&nbsp;Basic&nbsp;的使用者，他可以做一些脚本式的编
程)&nbsp;Operator是这个系统中最关键的部分，它是一个纽带，一个系在Programmer，Administrator，User之间的纽带。<br />
用一个功能模块来举例子。<br />
一．建立角色功能并做分配：<br />
1．如果现在要做一个员工管理的模块(即Resources)，这个模块有三个功能，分别是：增加，修改，删除。给这三个功能各自分配一个ID，这个ID叫做功能代号：<br />
Emp_addEmp，Emp_deleteEmp，Emp_updateEmp。<br />
2．建立一个角色(Role)，把上面的功能代码加到这个角色拥有的权限中，并保存到数据库中。角色包括系统管理员，测试人员等。<br />
3．建立一个员工的账号，并把一种或几种角色赋给这个员工。比如说这个员工既可以是公司管理人员，也可以是测试人员等。这样他登录到系统中将会只看到他拥有权限的那些模块。<br />
二．把身份信息加到Session中。<br />
登
录时，先到数据库中查找是否存在这个员工，如果存在，再根据员工的sn查找员工的权限信息，把员工所有的权限信息都入到一个Hashmap中，比如就把上
面的Emp_addEmp等放到这个Hashmap中。然后把Hashmap保存在一个UserInfoBean中。最后把这个UserInfoBean
放到Session中，这样在整个程序的运行过程中，系统随时都可以取得这个用户的身份信息。<br />
三．根据用户的权限做出不同的显示。<br />
可以对比当前员工的权限和给这个菜单分配的&#8220;功能ID&#8221;判断当前用户是否有打开这个菜单的权限。例如：如果保存员工权限的Hashmap中没有这三个ID的任何一个，那这个菜单就不会显示，如果员工的Hashmap中有任何一个ID，那这个菜单都会显示。&nbsp;<br />
对
于一个新闻系统(Resouce)，假设它有这样的功能(Privilege)：查看，发布，删除，修改；假设对于删除，有"新闻系统管理者只能删除一月
前发布的，而超级管理员可删除所有的这样的限制，这属于业务逻辑(Business&nbsp;logic)，而不属于用户权限范围。也就是说权限负责有没有删除的
Permission，至于能删除哪些内容应该根据UserRole&nbsp;or&nbsp;UserGroup来决定(当然给
UserRole&nbsp;or&nbsp;UserGroup分配权限时就应该包含上面两条业务逻辑)。<br />
一&nbsp;个用户可以拥有多种角色，但同一时刻用户只能用一种角
色进入系统。角色的划分方法可以根据实际情况划分，按部门或机构进行划分的，至于角色拥有多少权限，&nbsp;这就看系统管理员赋给他多少的权限了。用户?角色?
权限的关键是角色。用户登录时是以用户和角色两种属性进行登录的（因为一个用户可以拥有多种角色，但同&nbsp;一时刻只能扮演一种角色），根据角色得到用户的权
限，登录后进行初始化。这其中的技巧是同一时刻某一用户只能用一种角色进行登录。<br />
针对不同的&#8220;角色&#8221;动态的建立不同的组，每个项目建立一个单独的
Group，对于新的项目，建立新的&nbsp;Group&nbsp;即可。在权限判断部分，应在商业方法上予以控制。比如：不同用户的&#8220;操作能力&#8221;是不同的(粗粒度的控制
应能满足要求)，不同用户的&#8220;可视区域&#8221;是不同的(体现在对被操作的对象的权限数据，是否允许当前用户访问，这需要对业务数据建模的时候考虑权限控制需
要)。<br />
扩展性：<br />
有了用户/权限管理的基本框架，Who(User/Group)的概念是不会经常需要扩展的。变化的可能是系统中引入新
的&nbsp;What&nbsp;(新的Resource类型)或者新的How(新的操作方式)。那在三个基本概念中，仅在Permission上进行扩展是不够的。这样的
设计中Permission实质上解决了How&nbsp;的问题，即表示了&#8220;怎样&#8221;的操作。那么这个&#8220;怎样&#8221;是在哪一个层次上的定义呢？将Permission
定&nbsp;义在&#8220;商业方法&#8221;级别比较合适。比如，发布、购买、取消。每一个商业方法可以意味着用户进行的一个&#8220;动作&#8221;。定义在商业逻辑的层次上，一方面保证了数
据访&nbsp;问代码的&#8220;纯洁性&#8221;，另一方面在功能上也是&#8220;足够&#8221;的。也就是说，对更低层次，能自由的访问数据，对更高层次，也能比较精细的控制权限。<br />
确
定了Permission定义的合适层次，更进一步，能够发现Permission实际上还隐含了What的概念。也就是说，对于What的How操作才
会是一个完整的Operator。&nbsp;比如，&#8220;发布&#8221;操作，隐含了&#8220;信息&#8221;的&#8220;发布&#8221;概念，而对于&#8220;商品&#8221;而言发布操作是没有意义的。同样的，&#8220;购买&#8221;操
作，隐含了&#8220;商品&#8221;的&#8220;购买&#8221;概念。这&nbsp;里的绑定还体现在大量通用的同名的操作上，比如，需要区分&#8220;商品的删除&#8221;与&#8220;信息的删除&#8221;这两个同名为&#8220;删除&#8221;的
不同操作。<br />
提供权限系统的扩展能力是在Operator&nbsp;(Resource&nbsp;+&nbsp;Permission)的概念上进行扩展。Proxy&nbsp;模式是
一个非常合适的实现方式。实现大致如下：在业务逻辑层(EJB&nbsp;Session&nbsp;Facade&nbsp;[Stateful&nbsp;SessionBean]中)，取得
该商业方法的Methodname，再根据Classname和&nbsp;Methodname&nbsp;检索Operator&nbsp;数据，然后依据这个Operator信息
和Stateful中保存的User信息判断当前用户是否具备该方法的操作权限。<br />
应用在&nbsp;EJB&nbsp;模式下，可以定义一个很明确
的&nbsp;Business层次，而一个Business&nbsp;可能意味着不同的视图，当多个视图都对应于一个业务逻辑的时候，比如，Swing&nbsp;Client以
及&nbsp;Jsp&nbsp;Client&nbsp;访问的是同一个&nbsp;EJB&nbsp;实现的&nbsp;Business。在&nbsp;Business&nbsp;层上应用权限较能提供集中的控制能力。实际上，如
果权限系统提供了查询能力，那么会发现，在视图层次已经可以不去理解权限，它只需要根据查询结果控制界面就可以了。<br />
灵活性：<br />
Group和
Role，只是一种辅助实现的手段，不是必需的。如果系统的Role很多，逐个授权违背了&#8220;简单，方便&#8221;的目的，那就引入Group，将权限相同的
Role组成一个Group进行集中授权。Role也一样，是某一类Operator的集合，是为了简化针对多个Operator的操作。<br />
Role把具体的用户和组从权限中解放出来。一个用户可以承担不同的角色，从而实现授权的灵活性。当然，Group也可以实现类似的功能。但实际业务中，Group划分多以行政组织结构或业务功能划分；如果为了权限管理强行将一个用户加入不同的组，会导致管理的复杂性。<br />
Domain
的应用。为了授权更灵活，可以将Where或者Scope抽象出来，称之为Domain，真正的授权是在Domain的范围内进行，具体的
Resource将分属于不同的Domain。&nbsp;比如：一个新闻机构有国内与国外两大分支，两大分支内又都有不同的资源（体育类、生活类、时事政治类）。
假如所有国内新闻的权限规则都是一样的，所有国外&nbsp;新闻的权限规则也相同。则可以建立两个域，分别授权，然后只要将各类新闻与不同的域关联，受域上的权限
控制，从而使之简化。<br />
权限系统还应该考虑将功能性的授权与资源性的授权分开。很多系统都只有对系统中的数据（资源）的维护有权限控制，但没有对系统功能的权限控制。<br />
权
限系统最好是可以分层管理而不是集中管理。大多客户希望不同的部门能且仅能管理其部门内部的事务，而不是什么都需要一个集中的Administrator
或Administrators组来管理。虽然你可以将不同部门的人都加入Administrators组，但他们的权限过大，可以管理整个系统资源而不
是该部门资源。<br />
正向授权与负向授权：正向授权在开始时假定主体没有任何权限，然后根据需要授予权限，适合于权限要求严格的系统。负向授权在开始时假定主体有所有权限，然后将某些特殊权限收回。<br />
权限计算策略：系统中User，Group，Role都可以授权，权限可以有正负向之分，在计算用户的净权限时定义一套策略。<br />
系
统中应该有一个集中管理权限的AccessService，负责权限的维护（业务管理员、安全管理模块）与使用（最终用户、各功能模块），该
AccessService在实现时要同时考虑一般权限与特殊权限。虽然在具体实现上可以有很多，比如用Proxy模式，但应该使这些Proxy依赖于
AccessService。各模块功能中调用AccessService来检查是否有相应的权限。所以说，权限管理不是安全管理模块自己一个人的事情，
而是与系统各功能模块都有关系。每个功能模块的开发人员都应该熟悉安全管理模块，当然，也要从业务上熟悉本模块的安全规则。<br />
技术实现：<br />
1．表单式认证，这是常用的，但用户到达一个不被授权访问的资源时，Web容器就发<br />
出一个html页面，要求输入用户名和密码。&nbsp;<br />
2．一个基于Servlet&nbsp;Sign&nbsp;in/Sign&nbsp;out来集中处理所有的Request，缺点是必须由应用程序自己来处理。<br />
3．用Filter防止用户访问一些未被授权的资源，Filter会截取所有Request/Response，<br />
然后放置一个验证通过的标识在用户的Session中，然后Filter每次依靠这个标识来决定是否放行Response。<br />
这个模式分为：<br />
Gatekeeper&nbsp;：采取Filter或统一Servlet的方式。<br />
Authenticator：&nbsp;在Web中使用JAAS自己来实现。<br />
用户资格存储LDAP或数据库：<br />
1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Gatekeeper拦截检查每个到达受保护的资源。首先检查这个用户是否有已经创建好的Login&nbsp;Session，如果没有，Gatekeeper&nbsp;检查是否有一个全局的和Authenticator相关的session？<br />
2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果没有全局的session，这个用户被导向到Authenticator的Sign-on&nbsp;页面，<br />
要求提供用户名和密码。<br />
3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Authenticator接受用户名和密码，通过用户的资格系统验证用户。<br />
4.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果验证成功，Authenticator将创建一个全局Login&nbsp;session，并且导向Gatekeeper<br />
来为这个用户在他的web应用中创建一个Login&nbsp;Session。<br />
5.&nbsp;&nbsp;&nbsp;&nbsp;Authenticator和Gatekeepers联合分享Cookie，或者使用Tokens在Query字符里。
<img src ="http://www.blogjava.net/lvq810/aggbug/225985.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lvq810/" target="_blank">lvq810</a> 2008-09-01 01:21 <a href="http://www.blogjava.net/lvq810/articles/225985.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>结合Struts和Hibernate谈J2EE架构的数据表示[转]</title><link>http://www.blogjava.net/lvq810/articles/212412.html</link><dc:creator>lvq810</dc:creator><author>lvq810</author><pubDate>Thu, 03 Jul 2008 11:47:00 GMT</pubDate><guid>http://www.blogjava.net/lvq810/articles/212412.html</guid><wfw:comment>http://www.blogjava.net/lvq810/comments/212412.html</wfw:comment><comments>http://www.blogjava.net/lvq810/articles/212412.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lvq810/comments/commentRss/212412.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lvq810/services/trackbacks/212412.html</trackback:ping><description><![CDATA[在 struts+ hibernate 这种结构中，是不应该把Hibernate产生的PO直接传递给JSP的，不管他是Iterator，还是List，这是一个设计错误。
<br />
<br />
我来谈谈在J2EE架构中各层的数据表示方法：
<br />
<br />
Web层的数据表示是FormBean，数据来源于HTML Form POST
<br />
业务层的数据表示是VO
<br />
持久层的数据表示是PO，其数据来源于数据库，持久层的数据表示例如CMP
<br />
<br />
在一个规范的J2EE架构中，不同层的数据表示应该被限制在层内，而不应该扩散到其它层，这样可以降低层间的耦合性，提高J2EE架构整体的可维
护性和可扩展性。比如说Web层的逻辑进行了修改，那么只需要修改FormBean的结构，而不需要触动业务层和持久层的代码修改。同样滴，当数据库表进
行了小的调整，那么也只需要修改持久层数据表示，而不需要触动业务层代码和Web层代码。
<br />
<br />
不过由于Hibernate的强大功能，例如动态生成PO，PO的状态管理可以脱离Session，使得在应用了Hibernate的J2EE框架中，PO完全可以充当VO，因此我们下面把PO和VO合并，统称为PO。
<br />
<br />
先来谈谈ActionFormBean和持久层的PO之间的重大区别。
<br />
<br />
在简单的应用中，ActionFormBean和PO几乎是没有区别，所以很多人干脆就是用ActionFormBean来充当PO，于是
ActionFormBean从JSP页面到Servlet控制层再到业务层，然后穿过持久层，最后一直映射到数据库表。真是一竿子捅到了底！
<br />
<br />
但是在复杂的应用中，ActionFormBean和PO是分离的，他们也不可能一样。ActionFormBean是和网页里面的Form表单
一一对应的，Form里面有什么元素，Bean里面就有什么属性。而PO和数据库表对应，因此如果数据库表不修改，那么PO也不会修改，如果页面的流程和
数据库表字段对应关系不一致，那么你又如何能够使用ActionFormBean来取代PO呢？
<br />
<br />
比如说吧，用户注册页面要求注册用户的基本信息，因此HTML Form里面包含了基本信息属性，于是你需要一个ActionFormBean来一一对应(注意：是一一对应)，每个Bean属性对应一个文本框或者选择框什么的。
<br />
<br />
而用户这个持久对象呢？他的属性和ActionFormBean有什么明显不同呢？他会有一些ActionFormBean所没有的集合属性，比
如说用户的权限属性，用户的组属性，用户的帖子等等。另外还有可能的是在ActionFormBean里面有3个属性，分别是用户的First
Name, Middle Name, Last Name，而在我的User这个持久对象中就是一个 Name 对象属性。
<br />
<br />
假设我的注册页面原来只要你提供First Name，那么ActionFormBean就这一个属性，后来我要你提供全名，你要改ActionFormBean，加两个属性。但是这个时候PO是不应该修改滴，因为数据库没有改。
<br />
<br />
那么在一个完整的J2EE系统中应该如何进行合理的设计呢？
<br />
<br />
JSP(View) ---&gt; ActionFormBean(Module) ---&gt; Action(Control)
<br />
<br />
ActionFormBean是Web层的数据表示，它和HTML页面Form对应，只要Web页面的操作流程发生改变，它就要相应的进行修改，
它不应该也不能被传递到业务层和持久层，否则一旦页面修改，会一直牵连到业务层和持久层的大面积的代码进行修改，对于软件的可维护性和可扩展性而言，是一
个灾难，Actiont就是他的边界，到此为止！
<br />
<br />
Action(Web Control) ---&gt; Business Bean ---&gt; DAO ---&gt; ORM ---&gt;DB
<br />
<br />
而PO则是业务层和持久层的数据表示，它在业务层和持久层之间进行流动，他不应该也不能被传递到Web层的View中去，而ActionServlet就是他的边界，到此为止！
<br />
<br />
然后来看一看整个架构的流程：
<br />
<br />
当用户通过浏览器访问网页，提交了一个页面。于是Action拿到了这个FormBean，他会把FormBean属性读出来，然后构造一个PO
对象，再调用业务层的Bean类，完成了注册操作，重定向到成功页面。而业务层Bean收到这个PO对象之后，调用DAO接口方法，进行持久对象的持久化
操作。
<br />
<br />
当用户查询某个会员的信息的时候，他用全名进行查询，于是Action得到一个UserNameFormBean包括了3个属性，分别是
first name, middle name, last
name，然后Action把UserNameFormBean的3个属性读出来，构造Name对象，再调用业务Bean，把Name对象传递给业务
Bean，进行查询。
<br />
<br />
业务Bean取得Name(注意: Name对象只是User的一个属性)对象之后调用DAO接口，返回一个User的PO对象，注意这个User不同于在Web层使用的UserFormBean，他有很多集合属性滴。然后业务Bean把User对象返回给Action。
<br />
<br />
Action拿到User之后，把User的基本属性取出(集合属性如果不需要就免了)，构造UserFormBean，然后把UserFormBean  request.setAttribute(...)，然后重定向到查询结果页面。
<br />
<br />
查询页面拿到request对象里面的ActionFormBean，自动调用tag显示之。
<br />
<br />
总结：
<br />
<br />
FormBean是Web层的数据表示，他不能被传递到业务层；PO是持久层的数据表示，在特定情况下，例如Hibernate中，他可以取代
VO出现在业务层，但是不管PO还是VO都必须限制在业务层内使用，最多到达Web层的Control，绝不能被扩散到View去。
<br />
<br />
FormBean和PO之间的数据转化是在Action中进行滴。
<br />
<br />
BTW:
<br />
<br />
JDO1.x还不能像Hibernate功能这样强大，PO不能脱离持久层，所以必须在业务层使用VO，因此必须在业务层进行大量的VO和PO的转化操作，相对于Hibernate来说，编程比较烦琐。
<br />
<br />
当然咯，理论是一回事，实际操作也不一定非要这样干，你可以自行取舍，在实际项目中灵活一点，增加一点bad smell，提高开发效率。只不过在大型项目中最好还是严丝合缝，不然的话，改版的时候会痛苦的很滴。<br />
<br />
文章转载于：http://www.javaeye.com/topic/627<br />
作者：robbin
<img src ="http://www.blogjava.net/lvq810/aggbug/212412.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lvq810/" target="_blank">lvq810</a> 2008-07-03 19:47 <a href="http://www.blogjava.net/lvq810/articles/212412.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>提高网站速度的最佳实践[转]</title><link>http://www.blogjava.net/lvq810/articles/205916.html</link><dc:creator>lvq810</dc:creator><author>lvq810</author><pubDate>Wed, 04 Jun 2008 12:43:00 GMT</pubDate><guid>http://www.blogjava.net/lvq810/articles/205916.html</guid><wfw:comment>http://www.blogjava.net/lvq810/comments/205916.html</wfw:comment><comments>http://www.blogjava.net/lvq810/articles/205916.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lvq810/comments/commentRss/205916.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lvq810/services/trackbacks/205916.html</trackback:ping><description><![CDATA[相信互联网已经越来越成为人们生活中不可或缺的一部分。ajax，flex等等富客户端的应用使得人们越加&#8220;幸福&#8221;地体验着许多原先只能在C/S实
现的功能。比如Google机会已经把最基本的office应用都搬到了互联网上。当然便利的同时毫无疑问的也使页面的速度越来越慢。自己是做前端开发
的，在性能方面，根据yahoo的调查，后台只占5%，而前端高达95%之多，其中有88%的东西是可以优化的。
<p><img title="" alt="" src="http://www.kuqin.com/upimg/allimg/080513/1745420.jpg" onload="ResizeImage(this,520)" />&nbsp;</p>
<p>以上是一张web2.0页面的生命周期图。工程师很形象地讲它分成了&#8220;怀孕，出生，毕业，结婚&#8221;四个阶段。如果在我们点击网页链接的时候能够意识到
这个过程而不是简单的请求-响应的话，我们便可以挖掘出很多细节上可以提升性能的东西。今天听了淘宝小马哥的一个对yahoo开发团队对web性能研究的
一个讲座，感觉收获很大，想在blog上做个分享。</p>
<p>相信很多人都听过优化网站性能的14条规则。更多的信息可见<a hidefocus="" href="http://developer.yahoo.com/performance/rules.html" target="_blank"><font color="#0000ff">developer.yahoo.com</font></a>
<table border="0" width="500">
    <tbody>
        <tr>
            <td>1. 尽可能的减少 HTTP 的请求数</td>
            <td align="right"><span style="color: #ff0000;">[content]</span></td>
        </tr>
        <tr>
            <td>2. 使用 CDN（Content Delivery Network）</td>
            <td align="right"><span style="color: #3366ff;">[server]</span></td>
        </tr>
        <tr>
            <td>3. 添加 Expires 头(或者 Cache-control )</td>
            <td align="right"><span style="color: #3366ff;">[server]</span></td>
        </tr>
        <tr>
            <td>4. Gzip 组件</td>
            <td align="right"><span style="color: #3366ff;">[server]</span></td>
        </tr>
        <tr>
            <td>5. 将 CSS 样式放在页面的上方</td>
            <td align="right"><span style="color: #339966;">[css]</span></td>
        </tr>
        <tr>
            <td>6. 将脚本移动到底部（包括内联的）</td>
            <td align="right"><span style="color: #ff9900;">[javascript]</span></td>
        </tr>
        <tr>
            <td>7. 避免使用 CSS 中的 Expressions</td>
            <td align="right"><span style="color: #339966;">[css]</span></td>
        </tr>
        <tr>
            <td>8. 将 JavaScript 和 CSS 独立成外部文件</td>
            <td align="right"><span style="color: #ff9900;">[javascript] </span><span style="color: #339966;">[css]</span></td>
        </tr>
        <tr>
            <td>9. 减少 DNS 查询</td>
            <td align="right"><span style="color: #ff0000;">[content]</span></td>
        </tr>
        <tr>
            <td>10. 压缩 JavaScript 和 CSS (包括内联的)</td>
            <td align="right"><span style="color: #ff9900;">[javascript] </span><span style="color: #339966;">[css]</span></td>
        </tr>
        <tr>
            <td>11. 避免重定向</td>
            <td align="right"><span style="color: #3366ff;">[server]</span></td>
        </tr>
        <tr>
            <td>12. 移除重复的脚本</td>
            <td align="right"><span style="color: #ff9900;">[javascript]</span></td>
        </tr>
        <tr>
            <td>13. 配置实体标签（ETags）</td>
            <td align="right"><span style="color: #339966;">[css]</span></td>
        </tr>
        <tr>
            <td>14. 使 AJAX 缓存</td>
            <td>&nbsp;</td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;在firefox下有一个插件yslow，集成在firebug中，你可以用它很方便地来看看自己的网站在这几个方面的表现。</p>
<p><img title="" alt="" src="http://www.kuqin.com/upimg/allimg/080513/1745421.jpg" onload="ResizeImage(this,520)" /></p>
<p>这是对用yslow对我的网站<a hidefocus="" href="http://www.space007.com/" target="_blank">西风坊</a>测评的结果，很遗憾，只有51分。呵呵。中国各大网站的分值都不高，刚测了一下，新浪和网易都是31分。然后<a hidefocus="" href="http://www.yahoo.com/" target="_blank">yahoo</a>（美国）的分值确实97分！可见yahoo在这方面作出的努力。从他们总结的这14条规则，已经现在又新增加的20个点来看，有很多细节我们真得是怎么都不会去想，有些做法甚至是有些&#8220;变态&#8221;了。</p>
<p><strong><span style="color: #ff0000;">第一条、尽可能的减少 HTTP 的请求数</span></strong> （<a hidefocus="" href="http://developer.yahoo.com/performance/rules.html#num_http">Make Fewer HTTP Requests</a> ）</p>
<p>http请求是要开销的，想办法减少请求数自然可以提高网页速度。常用的方法，合并css，js（将一个页面中的css和js文件分别合并）以及
Image maps和css
sprites等。当然或许将css，js文件拆分多个是因为css结构，共用等方面的考虑。阿里巴巴中文站当时的做法是开发时依然分开开发，然后在后台
对js，css进行合并，这样对于浏览器来说依然是一个请求，但是开发时仍然能还原成多个，方便管理和重复引用。yahoo甚至建议将首页的css和js
直接写在页面文件里面，而不是外部引用。因为首页的访问量太大了，这么做也可以减少两个请求数。而事实上国内的很多门户都是这么做的。</p>
<p>而css sprites是指只用将页面上的背景图合并成一张，然后通过css的background-position属性定义不过的值来取他的背景。淘宝和阿里巴巴中文站目前都是这样做的。有兴趣的可以看下淘宝和<a hidefocus="" href="http://img.china.alibaba.com/images/cn/home/071028/icon_sum.gif" target="_blank">阿里巴巴的背景图</a>。</p>
<p><a hidefocus="" href="http://www.csssprites.com/">http://www.csssprites.com/</a>&nbsp;这是个工具网站，它可以自动将你上传的图片合并并给出对应的background-position坐标。并将结果以png和gif的格式输出。</p>
<p><span style="color: #ff0000;"><strong>第二条、使用CDN（内容分发网络）: </strong></span>Use a Content Delivery Network</p>
<p>说实话，对于CDN这一块自己并不是很了解，简单地讲，通过在现有的Internet中增加一层新的网络架构，将网站的内容发布到最接近用户的
cache服务器内，通过DNS负载均衡的技术，判断用户来源就近访问cache服务器取得所需的内容，杭州的用户访问近杭州服务器上的内容，北京的访问
近北京服务器上的内容。这样可以有效减少数据在网络上传输的时间，提高速度。更详细地内容大家可以参考百度百科上对于<a hidefocus="" href="http://baike.baidu.com/view/21895.htm" target="_blank">CDN</a>的解释。<span lang="EN-US">Yahoo!</span><span style="font-family: 宋体;">把静态内容分布到</span><span lang="EN-US">CDN</span><span style="font-family: 宋体;">减少了用户影响时间</span><span lang="EN-US">20%</span><span style="font-family: 宋体;">或更多。</span></p>
<p>CDN技术示意图：</p>
<p><img title="" alt="" src="http://www.kuqin.com/upimg/allimg/080513/1745422.jpg" onload="ResizeImage(this,520)" /></p>
<p>CDN组网示意图：</p>
<p>&nbsp;<img title="" alt="" src="http://www.kuqin.com/upimg/allimg/080513/1745423.jpg" onload="ResizeImage(this,520)" /></p>
<p><span style="color: #ff0000;"><strong>第三条、 添加Expire/Cache-Control 头</strong></span>：Add an Expires Header</p>
<p>现在越来越多的图片，脚本，css，flash被嵌入到页面中，当我们访问他们的时候势必会做许多次的http请求。其实我们可以通过设置<span lang="EN-US"><span class="hilite1">Expires</span> <span class="hilite2">header
来缓存这些文件。Expire其实就是通过header报文来指定特定类型的文件在览器中的缓存时间。大多数的图片，flash在发布后都是不需要经常修
改的，做了缓存以后这样浏览器以后就不需要再从服务器下载这些文件而是而直接从缓存中读取，这样再次访问页面的速度会大大加快。</span></span>一个典型的HTTP 1.1协议返回的头信息：<br />
HTTP/1.1 200 OK<br />
Date: Fri, 30 Oct 1998 13:19:41 GMT<br />
Server: Apache/1.3.3 (Unix)<br />
Cache-Control: max-age=3600, must-revalidate<br />
Expires: Fri, 30 Oct 1998 14:19:41 GMT<br />
Last-Modified: Mon, 29 Jun 1998 02:28:12 GMT<br />
ETag: "3e86-410-3596fbbc"<br />
Content-Length: 1040<br />
Content-Type: text/html</p>
<p>其中通过服务器端脚本设置Cache-Control和Expires可以完成。</p>
<p>如，在php中设置30天后过期：<br />
</p>
<pre mergenum="0"><code class="php"><font face="NSimsun">&lt;!--<br />
<br />
pHeader(<span class="string2"><font color="#ff00ff">"Cache-Control:&nbsp;must-revalidate"</font></span>);<span class="keyword"><strong><font color="#000080"><br />
<br />
$</font></strong></span><span class="variable"><font color="#4040c2">offset</font></span>&nbsp;=&nbsp;<span class="number"><font color="#ff0000">60</font></span>&nbsp;*&nbsp;<span class="number"><font color="#ff0000">60</font></span>&nbsp;*&nbsp;<span class="number"><font color="#ff0000">24</font></span>&nbsp;*&nbsp;<span class="number"><font color="#ff0000">30</font></span>;<br />
<br />
<span class="keyword"><strong><font color="#000080">$</font></strong></span><span class="variable"><font color="#4040c2">ExpStr</font></span>&nbsp;=&nbsp;<span class="string2"><font color="#ff00ff">"Expires:&nbsp;"</font></span>&nbsp;.&nbsp;<span class="func"><font color="#e17100">gmdate</font></span>(<span class="string2"><font color="#ff00ff">"D,&nbsp;d&nbsp;M&nbsp;Y&nbsp;H:i:s"</font></span>,&nbsp;<span class="func"><font color="#e17100">time</font></span>()&nbsp;+&nbsp;<span class="keyword"><strong><font color="#000080">$</font></strong></span><span class="variable"><font color="#4040c2">offset</font></span>)&nbsp;.&nbsp;<span class="string2"><font color="#ff00ff">"&nbsp;GMT"</font></span>;Header(<span class="keyword"><strong><font color="#000080">$</font></strong></span><span class="variable"><font color="#4040c2">ExpStr</font></span>);<br />
<br />
--&gt;</font></code></pre>
<p><textarea style="display: none;"> &lt;!--pHeader("Cache-Control:
must-revalidate");$offset = 60 * 60 * 24 * 30;$ExpStr = "Expires: " .
gmdate("D, d M Y H:i:s", time() + $offset) . "
GMT";Header($ExpStr);--&gt;</textarea> </p>
<p>在asp中设置绝对时间过期：</p>
<p><span style="font-style: italic;">&lt;% Response.ExpiresAbsolute=#May 31,2010 13:30:15 GMT# %&gt;</span><br />
&nbsp;</p>
<p>也可以通过配置服务器本身完成，这些偶就不是很清楚了，呵呵。想了解跟多的朋友可以参考<a hidefocus="" href="http://www.web-caching.com/">http://www.web-caching.com/</a>&nbsp;</p>
<p>据我了解，目前阿里巴巴中文站的Expires过期时间是30天。不过期间也有过问题，特别是对于脚本过期时间的设置还是应该仔细考虑下，不然相应的脚本功能更新后客户端可能要过很长一段时间才能&#8220;感知&#8221;到这样的变化。以前做[<a hidefocus="" href="http://www.kuqin.com/webpagedesign/20080513/8444.html" target="_blank">suggest项目</a>] 的时候就遇到过这个问题。所以，哪些应该缓存，哪些不该缓存还是应该仔细斟酌一番。</p>
<p><strong><span style="color: #ff0000;">第四条、启用Gzip压缩：</span></strong>Gzip Components</p>
<p><font size="2">Gzip的思想就是把文件先在服务器端进行压缩，然后再传输。这样可以显著减少文件传输的大小。传输完毕后浏览器会
重新对压缩过的内容进行解压缩，并执行。目前的浏览器都能&#8220;良好&#8221;地支持
gzip。不仅浏览器可以识别，而且各大&#8220;爬虫&#8221;也同样可以识别，各位seoer可以放下心了。而且gzip的压缩比例非常大，一般压缩率为85%，就是
说服务器端100K的页面可以压缩到25K左右再发送到客户端。具体的Gzip压缩原理大家可以参考csdn上的《</font><a hidefocus="" href="http://www.kuqin.com/algorithm/20080513/8445.html" target="_blank"><font size="2">gzip压缩算法</font></a><font size="2">》</font>&nbsp;这篇文章。雅虎特别强调，&nbsp;<strong>所有的文本内容都应该被gzip压缩: html (php), js, css, xml, txt&#8230; </strong>这一点我们网站做得不错，是一个A。以前我们的首页也并不是A，因为首页上还有很多广告代码投放的js，这些广告代码拥有者的网站的js没有经过gzip压缩，也会拖累我们网站。</p>
<p>以上三点大多属于服务器端的内容，本人也是粗浅地了解而已。说得不对的地方有待各位指正。&nbsp;</p>
<p><span style="color: #ff0000;"><strong>第五条、将css放在页面最上面 </strong></span><span style="color: #000000;"><span>（ Put Stylesheets at the Top）</span></span></p>
<p><span><span style="color: #000000;">将css放在页面最上面，这是为什么？因为ie，
firefox等浏览器在css全部传输完全之前不会去渲染任何的东西。理由诚如小马哥说得那样很简单。css，全称Cascading Style
Sheets&nbsp;(层叠样式表单)。层叠即意味这后面的css可以覆盖前面的css，级别高的css可以覆盖级别低的css。在[<a hidefocus="" href="http://www.kuqin.com/webpagedesign/20080513/8443.html" target="_blank">css之！important</a>] 这篇文章的最下面曾简单地提到过这层级关系，这里我们只需要知道css可以被覆盖的。既然前面的可以被覆盖，浏览器在他完全加载完毕之后再去渲染无疑也是合情合理的<font face="宋体">很多浏览器下，如</font><span lang="EN-US">IE</span><span style="font-family: 宋体;">，把样式表放在</span><span lang="EN-US">页面</span><span style="font-family: 宋体;">的底部的问题在于它禁止了网页内容的顺序显示。浏览器阻止显示以免重画页面元素，那用户只能看到空白页了。</span><span lang="EN-US">Firefox</span><span style="font-family: 宋体;">不会阻止显示，但这意味着当样式表下载后，有些页面元素可能需要重画，这导致闪烁问题。所以我们应该尽快让css加载完毕</span></span></span></p>
<p><span style="color: #000000;"><span>顺着这层意思，如果我们再细究的话，其实还有可以优化的地方。比如本站上面包含的两个css文件，&lt;<span class="start-tag">link</span><span class="attribute-name"> rel</span>=<span class="attribute-value">"stylesheet" </span><span class="attribute-name">rev</span>=<span class="attribute-value">"stylesheet" </span><span class="attribute-name">href</span>=<span class="attribute-value">"http://www.space007.com/themes/google/style/google.css" </span><span class="attribute-name">type</span>=<span class="attribute-value">"text/css" </span><span class="attribute-name">media</span>=<span class="attribute-value">"screen" </span><span class="error"><span class="attribute-name">/</span></span>&gt; 和&lt;<span class="start-tag">link</span><span class="attribute-name"> rel</span>=<span class="attribute-value">"stylesheet" </span><span class="attribute-name">rev</span>=<span class="attribute-value">"stylesheet" </span><span class="attribute-name">href</span>=<span class="attribute-value">"http://www.space007.com/css/print.css" </span><span class="attribute-name">type</span>=<span class="attribute-value">"text/css" </span><span class="attribute-name">media</span>=<span class="attribute-value">"print" </span><span class="error"><span class="attribute-name">/</span></span>&gt;。
从media就可以看出第一个css是针对浏览器的，第二个css文件是针对打印样式的。从用户的行为习惯上来将，要打印页面的动作一定是发生在页面页面
显示出来之后的。所以比较好的方法应该是在页面加载完毕之后再动态地为这张页面加上针对打印设备的css，这样又可以提高一点速度。（哈哈）</span></span></p>
<p>&nbsp;<span style="color: #000000;"><span><span style="color: #ff0000;"><strong>第六条、将script放在页面最下面 </strong><span style="color: #000000;">（Put Scripts at the Bottom ）</span></span></span></span></p>
<p><span style="color: #ff0000;"><span><span style="color: #000000;">将脚本放在页面最下面的目的有那么两点：</span></span></span>&nbsp;1、
因为防止script脚本的执行阻塞页面的下载。在页面loading的过程中，当浏览器读到js执行语句的时候一定会把它全部解释完毕后在会接下来读下
面的内容。不信你可以写一个js死循环看看页面下面的东西还会不会出来。（setTimeout 和
setInterval的执行有点类似于多线程，在相应的响应时间之前也会继续下面的内容渲染。）浏览器这么做的逻辑是因为js随时可能执行
&nbsp;location.href或是其他可能完全中断此页面过程的函数，即如此，当然得等他执行完毕之后再加载咯。所以放在页面最后，可以有效减少页面可视
元素的加载时间。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2、<span style="font-family: 宋体;">脚本引起的第二个问题是它阻塞并行下载数量。<span lang="EN-US">HTTP/1.1<span style="font-family: 宋体;" lang="EN-US"><span lang="EN-US">规范</span></span></span><span style="font-family: 宋体;">建议浏览器每个主机的并行下载数不超过</span><span lang="EN-US">2</span><span style="font-family: 宋体;">个（IE只能为2个，其他浏览器如ff等都是默认设置为2个，不过新出的ie8可以达6个）。因此如果您把图像文件分布到多台机器的话，您可以达到超过</span><span lang="EN-US">2</span><span style="font-family: 宋体;">个的并行下载。但是当脚本文件下载时，浏览器不会启动其他的并行下载。</span></span></p>
<p><span style="font-family: 宋体;"><span style="font-family: 宋体;">当然对各个网站来说，把脚本都放到页面底部加载的可行性还是值得商榷的。就比如阿里巴巴中文站的页面。很多地方有内联的js，页面的显示严重依赖于此，我承认这和无侵入脚本的理念相差甚远，但是很多&#8220;历史遗留问题&#8221;却不是那么容易解决的。</span></span></p>
<p><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial">&nbsp;<span style="color: #000000;"><span><span style="color: #ff0000;"><strong>第七条、<strong>避免在</strong><strong>CSS</strong><strong>中使用</strong><strong>Expressions </strong></strong><span style="color: #000000;">（Avoid CSS Expressions ）</span></span></span></span></font></span></span></p>
<p>css表达是的执行次数是远远多于我们想象的，往往会严重地影响性能。而且，它只能在IE中执行。所以因尽量地避免它。这一条以前倒没想过，个人用
这个就是在对ie使用max-width和min-width属性的时候。大家知道IE是不支持max-width和min-width属性的。有时候的
页面（特别是自适应大小的页面）为了能在分辨率小到一定程度后还能显示要用到这个功能，怎么办。当时我的做法就是利用expressions:&nbsp;</p>
<p>&nbsp;</p>
<pre mergenum="3"><code class="css">&nbsp;<span class="attrib"><strong><font color="#000080">min-width</font></strong></span>:<span class="number"><font color="#ff0000">952px</font></span>;&nbsp;<span class="attrib"><strong><font color="#000080">width</font></strong></span>:expression((document.documentElement.clientWidth&nbsp;&amp;lt;<span class="number"><font color="#ff0000">952</font></span>&nbsp;)&nbsp;?&nbsp;&amp;quot;<span class="number"><font color="#ff0000">952</font></span>&amp;quot;:&amp;quot;<span class="value"><font color="#0000ff">auto</font></span>&amp;quot;)&nbsp;</code></pre>
<p><textarea style="display: none;"> min-width:952px; width:expression((document.documentElement.clientWidth &lt;952 ) ? "952":"auto") </textarea> </p>
<p>不过从今天应该寻找新的办法了。目前的解决办法是通过两层的嵌套：</p>
<p>css文件：</p>
<pre mergenum="4"><code class="css">&nbsp;<span class="color"><font color="#008000">#main</font></span>_box{<span class="attrib"><strong><font color="#000080">width</font></strong></span>:<span class="number"><font color="#ff0000">70%</font></span>;<span class="attrib"><strong><font color="#000080">height</font></strong></span>:<span class="number"><font color="#ff0000">100px</font></span>;&nbsp;<span class="attrib"><strong><font color="#000080">background</font></strong></span>:<span class="color"><font color="#008000">#ffffcc</font></span>;&nbsp;<span class="attrib"><strong><font color="#000080">min-width</font></strong></span>:<span class="number"><font color="#ff0000">600px</font></span>;<span class="attrib"><strong><font color="#000080">margin</font></strong></span>:<span class="value"><font color="#0000ff">auto</font></span>;&nbsp;} <br />
<br />
#p_main_box{&nbsp;<span class="attrib"><strong><font color="#000080">border-left</font></strong></span>:<span class="number"><font color="#ff0000">600px</font></span>&nbsp;<span class="value"><font color="#0000ff">solid</font></span>&nbsp;<span class="color"><font color="#008000">#ffffcc</font></span>;&nbsp;<span class="attrib"><strong><font color="#000080">height</font></strong></span>:<span class="number"><font color="#ff0000">1px</font></span>;&nbsp;} <br />
<br />
#m_main_box{&nbsp;<span class="attrib"><strong><font color="#000080">margin-left</font></strong></span>:-<span class="number"><font color="#ff0000">600px</font></span>;&nbsp;<span class="attrib"><strong><font color="#000080">position</font></strong></span>:<span class="value"><font color="#0000ff">relative</font></span>;&nbsp;<span class="attrib"><strong><font color="#000080">height</font></strong></span>:<span class="number"><font color="#ff0000">1px</font></span>;&nbsp;<span class="attrib"><strong><font color="#000080">text-align</font></strong></span>:<span class="value"><font color="#0000ff">center</font></span>;&nbsp;} <br />
<br />
<br />
<br />
</code></pre>
<p><textarea style="display: none;"> #main_box{width:70%;height:100px;
background:#ffffcc; min-width:600px;margin:auto; } #p_main_box{
border-left:600px solid #ffffcc; height:1px; } #m_main_box{
margin-left:-600px; position:relative; height:1px; text-align:center; }
</textarea> </p>
<p>html文件：</p>
<pre mergenum="2"><code class="html"><strong><font color="#000080"><span class="tag">&lt;/p&gt;</span><span class="tag">&lt;div</span></font></strong><span class="aname"><font color="#800080">&nbsp;id</font></span>=<span class="avalue"><font color="#ff00ff">"main_box"</font></span><strong><font color="#000080"><span class="tag">&gt;</span><span class="tag">&lt;div</span></font></strong><span class="aname"><font color="#800080">&nbsp;id</font></span>=<span class="avalue"><font color="#ff00ff">"p_main_box"</font></span><strong><font color="#000080"><span class="tag">&gt;</span><span class="tag">&lt;div</span></font></strong><span class="aname"><font color="#800080">&nbsp;id</font></span>=<span class="avalue"><font color="#ff00ff">"m_main_box"</font></span><span class="tag"><strong><font color="#000080">&gt;</font></strong></span>最小宽度600px；<font color="#000080"><span class="tag">&lt;/div&gt;</span><span class="tag">&lt;/div&gt;</span><span class="tag">&lt;/div&gt;</span><span class="tag">&lt;p</span></font><span class="tag">&gt;</span></code>
<p><input id="cloudream-runcode0" value="运行代码" type="button" /><br />
</p>
<br />
</pre>
<p><textarea id="cloudream-precode0" style="display: none;" mergenum="1">&lt;/p&gt;&lt;div
id="main_box"&gt;&lt;div id="p_main_box"&gt;&lt;div
id="m_main_box"&gt;最小宽度600px；&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;
p&gt;</textarea> </p>
<p>不过这样就多了两层无意义的嵌套，肯定不好。还需要一个更好的办法。<span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial">&nbsp;</font></span></span></p>
<p><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><strong>第八条、<strong>把javascript和css都放到外部文件中</strong><strong> </strong></strong><span style="color: #000000;">（Make JavaScript and CSS External ）</span></span></span></span></font></span></span></p>
<p>这点我想还是很容易理解的。不仅从性能优化上会这么做，用代码易于维护的角度看也应该这么做。把css和js写在页面内容可以减少2次请求，但也增
大了页面的大小。如果已经对css和js做了缓存，那也就没有2次多余的http请求了。当然，我在前面中也说过，有些特殊的页面开发人员还是会选择内联
的css和js文件。</p>
<p><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><strong>第九条、减少DNS查询 </strong><span style="color: #000000;">(Reduce DNS Lookups) </span></span></span></span></font></span></span></p>
<p><span style="color: #ff0000;"><span><span style="color: #000000;"><font face="Arial"><span style="font-family: 宋体;"><span style="font-family: 宋体;">在Internet
上域名与IP地址之间是一一对应的，域名（kuqin.com）很好记，但计算机不认识，计算机之间的&#8220;相认&#8221;还要转成ip地址。在网络上每台计算机都对
应有一个独立的ip地址。在域名和ip地址之间的转换工作称为域名解析，也称DNS查询。一次DNS的解析过程会消耗20-120毫秒的时间,在dns查
询结束之前，浏览器不会下载该域名下的任何东西。所以减少dns查询的时间可以加快页面的加载速度。yahoo的建议一个页面所包含的域名数尽量控制在2
-4个。这就需要对页面整体有一个很好的规划。目前我们这点做的不好，很多打点的广告投放系统拖累了我们。</span></span></font></span></span></span></p>
<p><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><strong>第十条、</strong><strong><span style="color: #ff0000;">压缩 JavaScript 和 CSS&nbsp;</span></strong><strong> </strong><span style="color: #000000;">(Minify JavaScript ) </span></span></span></span></font></span></span></span></span></span></font></span></span></p>
<p>压缩js和css的左右很显然，减少页面字节数。容量小页面加载速度自然也就快。而且压缩除了减少体积以外还可以起到一定的保护左右。这点我们做得不错。常用的压缩工具有JsMin、YUI compressor等。另外像<a hidefocus="" href="http://dean.edwards.name/packer/">http://dean.edwards.name/packer/</a>还给我们提供了一个非常方便的在线压缩工具。你可以在jQuery的网页看到压缩过的js文件和没有压缩过的js文件的容量差别：</p>
<p><img title="" alt="" src="http://www.kuqin.com/upimg/allimg/080513/1745424.jpg" onload="ResizeImage(this,520)" /></p>
<p>当然，压缩带来的一个弊端就是代码的可读性没了。相信很多做前端的朋友都遇到过这个问题：看Google的效果很酷，可是去看他的源代码却是一大堆
挤在一起的字符，连函数名都是替换过的，汗死！自己的代码也这样岂不是对维护非常不方便。所有阿里巴巴中文站目前采用的做法是在js和css发布的时候在
服务器端进行压缩。这样在我们很方便地维护自己的代码。</p>
<p><span style="color: #000000;"><span style="color: #ff0000;"><span><span style="color: #000000;"><font face="Arial"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="color: #ff0000;"><span><span style="color: #000000;"><font face="Arial"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><strong>第十一条、</strong><strong><span style="color: #ff0000;">避免重定向</span></strong><strong> </strong><span style="color: #000000;">(Avoid Redirects ) </span></span></span></span></font></span></span></span></span></font></span></span></span></span></span></font></span></span></span></span></p>
<p><span style="color: #000000;"><span style="color: #ff0000;"><span><span style="color: #000000;"><font face="Arial"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><span style="color: #000000;">不久前在ieblog上看到过《<a hidefocus="" href="http://blogs.msdn.com/ie/archive/2005/04/11/407189.aspx" target="_blank">Internet Explorer and Connection Limits</a>》这篇文章，比如 当你输入<a href="http://www.kuqin.com/" target="_blank">http://www.ithao123.com</a> 的时候服务器会自动产生一个301服务器转向 <a href="http://www.kuqin.com/">http://www.kuqin.com/</a> ，你看浏览器的地址栏就能看出来。这种重定向自然也是需要消耗时间的。当然这只是一个例子，发生重定向的原因还有很多，但是不变的是每增加一次重定向就会增加一次web请求，所以因该尽量减少。</span></span></span></span></font></span></span></span></span></span></font></span></span></span></span></font></span></span></span></span></p>
<p><span style="color: #000000;"><span style="color: #ff0000;"><span><span style="color: #000000;"><font face="Arial"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><span style="color: #000000;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><strong>第十二条、</strong><strong><span style="color: #ff0000;">移除重复的脚本</span></strong><strong> </strong><span style="color: #000000;">(Remove Duplicate Scripts ) </span></span></span></span></font></span></span></span></span></span></span></font></span></span></span></span></span></font></span></span></span></span></font></span></span></span></span></p>
<p><span style="color: #000000;"><span style="color: #ff0000;"><span><span style="color: #000000;"><font face="Arial"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="color: #000000;"><span style="color: #ff0000;"><span><span style="color: #000000;"><font face="Arial"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="color: #ff0000;"><span><span style="color: #000000;"><font face="Arial"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><span style="color: #000000;">这点我想不说也知道，不仅是从性能上考虑，代码规范上看也是这样。但是不得不承认，很多时候我们会因为图一时之快而加上一些或许是重复的代码。或许一个统一的css框架和js框架可以比较好的解决我们的问题。小猪的观点很对，不仅是要做到不重复，更是要做到可重用。</span></span></span></span></font></span></span></span></span></font></span></span></span></span></span></font></span></span></span></span></span></span></font></span></span></span></span></p>
<p><span style="color: #000000;"><span style="color: #ff0000;"><span><span style="color: #000000;"><font face="Arial"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="color: #000000;"><span style="color: #ff0000;"><span><span style="color: #000000;"><font face="Arial"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="color: #ff0000;"><span><span style="color: #000000;"><font face="Arial"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><span style="color: #000000;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><strong>第十三条、</strong><strong><span style="color: #ff0000;">配置</span><span style="color: #ff0000;">实体标签（ETags）</span></strong><strong> </strong><span style="color: #000000;">(Configure ETags ) </span></span></span></span></font></span></span></span></span></span></span></font></span></span></span></span></font></span></span></span></span></span></font></span></span></span></span></span></span></font></span></span></span></span></p>
<p>这点我也不懂，呵呵。在inforQ上找到一篇解释得比较详细的说明《<a hidefocus="" href="http://www.kuqin.com/web/20080513/8442.html" target="_blank">使用ETags减少Web应用带宽和负载</a>》，有兴趣的同学可以去看看。</p>
<p><span style="color: #000000;"><span style="color: #ff0000;"><span><span style="color: #000000;"><font face="Arial"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="color: #000000;"><span style="color: #ff0000;"><span><span style="color: #000000;"><font face="Arial"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="color: #ff0000;"><span><span style="color: #000000;"><font face="Arial"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><span style="color: #000000;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><span style="color: #000000;"><span style="font-family: 宋体;"><span style="font-family: 宋体;"><font face="Arial"><span style="color: #000000;"><span><span style="color: #ff0000;"><strong>第十四条、</strong><strong><span style="color: #ff0000;">使 AJAX 缓存 </span></strong><span style="color: #000000;">(Make Ajax Cacheable ) </span></span></span></span></font></span></span></span></span></span></span></font></span></span></span></span></span></span></font></span></span></span></span></font></span></span></span></span></span></font></span></span></span></span></span></span></font></span></span></span></span></p>
<p>ajax还要去缓存？做ajax请求的时候往往还要增加一个时间戳去避免他缓存。It's important to remember
that "asynchronous" does not imply
"instantaneous".（记住&#8220;异步&#8221;不是&#8220;瞬间&#8221;这一点很重要）。记住，即使AJAX是动态产生的而且只对一个用户起作用，他们依然可以被缓
存。</p>
<p style="color: red;">&nbsp;</p>
<img src ="http://www.blogjava.net/lvq810/aggbug/205916.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lvq810/" target="_blank">lvq810</a> 2008-06-04 20:43 <a href="http://www.blogjava.net/lvq810/articles/205916.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>当前Java软件开发中几种认识误区[转]</title><link>http://www.blogjava.net/lvq810/articles/182709.html</link><dc:creator>lvq810</dc:creator><author>lvq810</author><pubDate>Thu, 28 Feb 2008 08:09:00 GMT</pubDate><guid>http://www.blogjava.net/lvq810/articles/182709.html</guid><wfw:comment>http://www.blogjava.net/lvq810/comments/182709.html</wfw:comment><comments>http://www.blogjava.net/lvq810/articles/182709.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lvq810/comments/commentRss/182709.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lvq810/services/trackbacks/182709.html</trackback:ping><description><![CDATA[<p align="center"><a href="http://www.jdon.com/aboutme.htm">板桥里人</a>
http://www.jdon.com <br />
</p>
<p>　　越来越多人开始使用Java，但是他们大多数人没有做好足够的思想准备(没有接受OO思想体系<a href="http://www.jdon.com/trainning.htm" target="_blank">相关培训</a>)，以致不能很好驾驭Java项目，甚至
导致开发后的Java系统性能缓慢甚至经常当机。很多人觉得这是Java复杂导致，其实根本原因在于：我们原先掌握的关于软件知识(OO方面)不是太贫乏就是不恰当，存在认识上和方法上的误区。</p>
<p><strong>软件的生命性</strong></p>
<p>　　软件是有生命的，这可能是老调重弹了，但是因为它事关分层架构的原由，反复强调都不过分。</p>
<p>　　一个有生命的软件首先必须有一个灵活可扩展的基础架构，其次才是完整的功能。</p>
<p>　　目前很多人对软件的思想还是焦点落在后者：完整的功能，觉得一个软件功能越完整越好，其实关键还是架构的灵活性，就是前者，基础架构好，功能添加只是时间和工作量问题，但是如果架构不好，功能再完整，也不可能包括未来所有功能，软件是有生命的，在未来成长时，更多功能需要加入，但是因为基础架构不灵活不能方便加入，死路一条。<br />
<br />
正因为普通人对软件存在短视误区，对功能追求高于基础架构，很多吃了亏的老程序员就此离开软件行业，带走宝贵的失败经验，新的盲目的年轻程序员还是使用老的思维往前冲。其实很多国外免费开源框架如ofbiz
compiere和slide也存在这方面陷阱，貌似非常符合胃口，其实类似国内那些几百元的盗版软件，扩展性以及持续发展性严重不足。</p>
<p>　　那么选择现在一些流行的框架如Hibernate、Spring/Jdonframework是否就表示基础架构打好了呢？其实还不尽然，关键还是取决于你如何使用这些框架来搭建你的业务系统。</p>
<p><strong>存储过程和复杂SQL语句的陷阱</strong></p>
<p>　　首先谈谈存储过程使用的误区，使用存储过程架构的人以为可以解决性能问题，其实它正是导致性能问题的罪魁祸首之一，打个比喻：如果一个人频临死亡，打一针可以让其延长半年，但是打了这针，其他所有医疗方案就全部失效，请问你会使用这种短视方案吗？</p>
<p>　　为什么这样说呢？如果存储过程都封装了业务过程，那么运行负载都集中在数据库端，要中间J2EE应用服务器干什么？要中间服务器的分布式计算和集群能力做什么？只能回到过去集中式数据库主机时代。现在软件都是面向互联网的，不象过去那样局限在一个小局域网，多用户并发访问量都是无法确定和衡量，依靠一台数据库主机显然是不能够承受这样恶劣的用户访问环境的。（当然搞数据库集群也只是五十步和百步的区别）。</p>
<p>
从分层角度来看，现在三层架构：表现层、业务层和持久层，三个层次应该分割明显，职责分明：持久层职责持久化保存业务模型对象，业务层对持久层的调用只是帮助我们激活曾经委托其保管的对象，所以，不能因为持久层是保管者，我们就以其为核心围绕其编程，除了要求其归还模型对象外，还要求其做其做复杂的业务组合。打个比喻：你在火车站将水果和盘子两个对象委托保管处保管，过了两天来取时，你还要求保管处将水果去皮切成块，放在盘子里，做成水果盘给你，合理吗？</p>
<p>　　上面是谈过分依赖持久层的一个现象，还有一个正好相反现象，持久层散发出来，开始挤占业务层，腐蚀业务层，整个业务层到处看见的是数据表的影子（包括数据表的字段），而不是业务对象。这样程序员应该多看看<a href="http://www.martinfowler.com/eaaCatalog/index.html" target="_blank">OO经典PoEAA</a>。<a href="http://www.martinfowler.com/eaaCatalog/index.html" target="_blank">PoEAA</a>
认为除了持久层，不应该在其他地方看到数据表或表字段名。</p>
<p>　　当然适量使用存储过程，使用数据库优点也是允许的。按照Evans DDD理论，可以将SQL语句和存储过程作为规则Specification一部分。
</p>
<p><strong>Hibernate等ORM问题</strong><br />
现在使用Hibernate人也不少，但是他们发现Hibernate性能缓慢，所以寻求解决方案，其实并不是
Hibernate性能缓慢，而是我们使用方式发生错误：</p>
<p>　　&#8220;最近本人正搞一个项目，项目中我们用到了struts1.2+hibernate3,
由于关系复杂表和表之间的关系很多，在很多地方把lazy都设置false，所以导致数据一加载很慢，而且查询一条数据更是非常的慢。&#8221;</p>
<p>　　Hibernate是一个基于对象模型持久化的技术，因此，关键是我们需要设计出高质量的对象模型，遵循DDD领域建模原则，减少降低关联，通过分层等有效办法处理关联。如果采取围绕数据表进行设计编程，加上表之间关系复杂（没有科学方法处理、侦察或减少这些关系），必然导致
系统运行缓慢，其实同样问题也适用于当初对EJB的实体Bean的CMP抱怨上，实体Bean是Domain Model持久化，如果不首先设计Domain
Model，而是设计数据表，和持久化工具设计目标背道而驰，能不出问题吗？关于这个问题N多年就在Jdon争论过。</p>
<p>　　这里同样延伸出另外一个问题：数据库设计问题，数据库是否需要在项目开始设计？<br />
如果我们进行数据库设计，那么就产生了一系列问题：当我们使用Hibernate实现持久保存时，必须考虑事先设计好的数据库表结构以及他们的关系如何和业务对象实现映射，这实际上是非常难实现的，这也是很多人觉得使用ORM框架棘手根本原因所在。</p>
<p>　　当然，也有脑力相当发达的人可以
实现，但是这种围绕数据库实现映射的结果必然扭曲业务对象，这类似于两个板块（数据表和业务对象）相撞，必然产生地震，地震的结果是两败俱伤，
软的一方吃亏，业务对象是代码，相当于数据表结构，属于软的一方，最后导致业务对象变成数据传输对象DTO, DTO满天飞，性能和维护问题随之而来。</p>
<p>　　领域建模解决了上述众多不协调问题，特别是ORM痛苦使用问题，关于ORM/Hibernate使用还是那句老话：如果你不掌握领域建模方法，那么就不要用Hibernate，对于这个层次的你：也许No
ORM 更是一个简单之道： No ORM: The simplest solution<br />
<a href="http://www.theserverside.com/blogs/thread.tss?thread_id=41715" target="_blank">http://www.theserverside.com/blogs/thread.tss?thread_id=41715</a></p>
<p><strong>Spring分层矛盾问题</strong><br />
Spring是以挑战EJB面貌出现，其本身拥有的强大组件定制功能是优点，但是存在实战的一些问题，Spring作为业务层框架，不支持业务层Session
功能。</p>
<p>　　具体举例如下：当我们实现购物车之类业务功能时，需要将购物场合保存到Session中，由于业务层没有方便的Session支持，我们只得将购物车保存到
HttpSession，而HttpSession只有通过HttpRequest才能获得，再因为在Spring业务层容器中是无法访问到HttpRequest这个对象的，所以，
最后我们只能将&#8220;购物车保存到HttpSession&#8221;这个功能放在表现层中实现，而这个功能明显应该属于业务层功能，这就导致我们的Java项目层次混乱，维护性差。
违背了使用Spring和分层架构最初目的。</p>
<p>　　相关案例：请教一个在完整提交前临时保存的问题:<br />
<a href="http://www.jdon.com/jive/article.jsp?forum=46&amp;thread=28429" target="_blank">http://www.jdon.com/jive/article.jsp?forum=46&amp;thread=28429</a></p>
<p><strong>领域驱动设计DDD</strong><br />
现在回到我们讨论的重点上来，分层架构是我们使用Java的根本原因之一，域建模专家Eric
Evans在他的&#8220;Domain Model
Design&#8221;一书中开篇首先强调的是分层架构，整个DDD理论实际是告诉我们如何使用模型对象oo技术和分层架构来设计实现一个Java项目。</p>
<p>　　我们现在很多人知道Java项目基本有三层：表现层　业务层和持久层，当我们执著于讨论各层框架如何选择之时，实际上我们真正的项目开发工作还没有开始，
就是我们选定了某种框架的组合（如Struts+Spring+Hibernate或Struts+EJB或Struts+JdonFramework），我们还没有意识到业务层工作还需要大量工作，DDD提供了在业务层中再划分新的层次思想，如领域层和服务层，甚至再细分为作业层、能力层、策略层等等。通过层次细化方式达到复杂软件的松耦合。DDD提供了如何细分层次的方式</p>
<p>　　当我们将精力花费在架构技术层面的讨论和研究上时，我们可能忘记以何种依据选择这些架构技术？选择标准是什么？领域驱动设计DDD
回答了这样的问题，DDD会告诉你如果一个框架不能协助你实现分层架构，那就抛弃它，同时，DDD也指出选择框架的考虑目的，使得你不会
人云亦云，陷入复杂的技术细节迷雾中，迷失了架构选择的根本方向。</p>
<p>　　现在也有些人误以为DDD是一种新的理论，其实DDD和设计模式一样，不是一种新的理论，而是实战经验的总结，它将前人
使用面向模型设计的方法经验提炼出来，供后来者学习，以便迅速找到驾驭我们软件项目的根本之道。</p>
<p>　　现在Evans　DDD概念很火，因为它将著名的<a href="http://www.martinfowler.com/eaaCatalog/index.html" target="_blank">PoEAA</a>进行了具化，实现了<a href="http://www.martinfowler.com/eaaCatalog/index.html" target="_blank">PoEAA</a>可操作性，这也是MF大力推崇的原因。最近(8月8日)一位老外博客上用微软的.NET架构和Evans
DDD比较的文章：<a href="http://weblogs.asp.net/pgielens/archive/2006/08/08/Organizing-Domain-Logic.aspx">http://weblogs.asp.net/pgielens/archive/2006/08/08/Organizing-Domain-Logic.aspx</a>，这篇文章比较了微软的三层服务应用架构[Microsoft
TLSA]和Evans DDD的架构， 使用Microsoft .NET Pet Shop
4为例子，解释两个目标的区别，并且表明<br />
微软是如何在案例中更好地实现支持后者。这篇文章帮助哪些.NET平台上有域设计知识的人实现更好地提高。</p>
<p>　　另外一本关于.NET的DDD书籍也已经出版，这些都说明Evans
DDD这把火已经烧到.NET领域，当然DDD在Java领域生根开花多年，Evans的DDD书籍就是以Java为例子的，笔者板桥里人也率先在2005年推出DDD框架JdonFramework
1.3版本，这些都说明，Java在整个软件业先进思想的实践上总是领先一步。<br />
</p>
<p>参考文章：</p>
<p><a href="http://www.jdon.com/mda/modeling.html" target="_blank"><strong>面向对象与领域建模 </strong></a></p>
<p><a href="http://www.jdon.com/mda/ddd.html" target="_blank"><strong>实战DDD(Domain-Driven Design领域驱动设计)</strong></a></p>
<p><a href="http://www.jdon.com/mda/dddcase2.html" target="_blank"><strong>领域模型驱动设计(DDD)之模型提炼</strong> </a></p>
<p><a href="http://www.jdon.com/artichect/dbover.htm" target="_blank"><strong>数据库时代的终结</strong></a></p>
<p><a href="http://www.jdon.com/artichect/javaee.html"><strong>Java
EE/J2EE面向对象编程之道</strong></a></p>
<p><a href="http://www.jdon.com/jivejdon/query/searchThreadAction.shtml?query=ddd" target="_blank"><strong>更多关于DDD讨论</strong></a></p>
<p><strong><a href="http://www.jdon.com/jivejdon/query/searchThreadAction.shtml?query=ejb3" target="_blank">更多关于EJB3讨论</a></strong></p>
<p><strong><a href="http://www.jdon.com/jivejdon/query/searchThreadAction.shtml?query=ioc" target="_blank">更多关于IOC讨论</a></strong></p>
<p><strong><a href="http://www.jdon.com/jivejdon/query/searchThreadAction.shtml?query=aop" target="_blank">更多关于AOP讨论</a></strong></p>
<p><strong><a href="http://www.jdon.com/jivejdon/query/searchThreadAction.shtml?query=spring" target="_blank">更多关于Spring讨论</a></strong></p>
<p><strong><a href="http://www.jdon.com/jivejdon/query/searchThreadAction.shtml?query=jdonframework" target="_blank">更多关于JdonFramework讨论</a></strong></p>
<img src ="http://www.blogjava.net/lvq810/aggbug/182709.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lvq810/" target="_blank">lvq810</a> 2008-02-28 16:09 <a href="http://www.blogjava.net/lvq810/articles/182709.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>