﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-把永恒在一刹那间收藏-文章分类-j2ee</title><link>http://www.blogjava.net/gm_jing/category/13229.html</link><description>生活之点点滴滴</description><language>zh-cn</language><lastBuildDate>Tue, 11 Jan 2011 21:30:47 GMT</lastBuildDate><pubDate>Tue, 11 Jan 2011 21:30:47 GMT</pubDate><ttl>60</ttl><item><title>【转】session 的讨论</title><link>http://www.blogjava.net/gm_jing/articles/338938.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Wed, 24 Nov 2010 08:09:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/338938.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/338938.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/338938.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/338938.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/338938.html</trackback:ping><description><![CDATA[<strong>一、术语session</strong>
<p>　　在我的经验里，session这个词被滥用的程度大概仅次于transaction，更加有趣的是transaction和session在某些语境下的含义是相同的。</p>
<p>　　session，中文经常翻译为会话，其本来的含义是指有始有终的一系列动作/消息，比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程
能称之为一个session。有时候我们能看到这样的话&#8220;在一个浏览器会话期间，...&#8221;，这里的会话一词用的就是其本义，是指从一个浏览器窗口打开到关
闭这个期间①。最混乱的是&#8220;用户（客户端）在一次会话期间&#8221;这样一句话，他可能指用户的一系列动作（一般情况下是同某个具体目的相关的一系列动作，比如从
登录到选购商品到结账登出这样一个网上购物的过程，有时候也被称为一个transaction），然而有时候也可能仅仅是指一次连接，也有可能是指含义
①，其中的差别只能靠上下文来推断②。</p>
<p>　　然而当session一词和网络协议相关联时，他又往往隐含了&#8220;面向连接&#8221;和/或&#8220;保持状态&#8221;这样两个含义，&#8220;面向连接&#8221;指的是在通信双方在通
信之前要先建立一个通信的渠道，比如打电话，直到对方接了电话通信才能开始，和此相对的是写信，在你把信发出去的时候你并不能确认对方的地址是否正确，通
信渠道不一定能建立，但对发信人来说，通信已开始了。&#8220;保持状态&#8221;则是指通信的一方能够把一系列的消息关联起来，使得消息之间能互相依赖，比如一个服务员
能够认出再次光临的老顾客并且记得上次这个顾客还欠店里一块钱。这一类的例子有&#8220;一个TCP session&#8221;或&#8220;一个POP3 session&#8221;③。</p>
<p>　　而到了web服务器蓬勃发展的时代，session在web研发语境下的语义又有了新的扩展，他的含义是指一类用来在客户端和服务器之间保持状
态的解决方案④。有时候session也用来指这种解决方案的存储结构，如&#8220;把xxx保存在session里&#8221;⑤。由于各种用于web研发的语言在一定程
度上都提供了对这种解决方案的支持，所以在某种特定语言的语境下，session也被用来指代该语言的解决方案，比如经常把Java里提供的
javax.servlet.http.HttpSession简称为session⑥。</p>
<p>　　鉴于这种混乱已不可改动，本文中session一词的运用也会根据上下文有不同的含义，请大家注意分辨。</p>
<p>　　在本文中，使用中文&#8220;浏览器会话期间&#8221;来表达含义①，使用&#8220;session机制&#8221;来表达含义④，使用&#8220;session&#8221;表达含义⑤，使用具体的&#8220;HttpSession&#8221;来表达含义⑥</p>
<p><strong>　　二、HTTP协议和状态保持</strong></p>
<p>　　HTTP协议本身是无状态的，这和HTTP协议本来的目的是相符的，客户端只需要简单的向服务器请求下载某些文件，无论是客户端还是服务器都没
有必要纪录彼此过去的行为，每一次请求之间都是独立的，好比一个顾客和一个自动售货机或一个普通的（非会员制）大卖场之间的关系相同。</p>
<p>　　然而聪明（或贪心？）的人们非常快发现如果能够提供一些按需生成的动态信息会使web变得更加有用，就像给有线电视加上点播功能相同。这种需求
一方面迫使HTML逐步添加了表单、脚本、DOM等客户端行为，另一方面在服务器端则出现了CGI规范以响应客户端的动态请求，作为传输载体的HTTP协
议也添加了文件上载、cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的session机
制则是又一种在客户端和服务器之间保持状态的解决方案。</p>
<p>　　让我们用几个例子来描述一下cookie和session机制之间的差别和联系。笔者原来常去的一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠，然而一次性消费5杯咖啡的机会微乎其微，这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案：</p>
<p>　　1、该店的店员非常厉害，能记住每位顾客的消费数量，只要顾客一走进咖啡店，店员就知道该怎么对待了。这种做法就是协议本身支持状态。</p>
<p>　　2、发给顾客一张卡片，上面记录着消费的数量，一般更有个有效期限。每次消费时，如果顾客出示这张卡片，则此次消费就会和以前或以后的消费相联系起来。这种做法就是在客户端保持状态。</p>
<p>　　3、发给顾客一张会员卡，除了卡号之外什么信息也不纪录，每次消费时，如果顾客出示该卡片，则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。</p>
<p>　　由于HTTP协议是无状态的，而出于种种考虑也不希望使之成为有状态的，因此，后面两种方案就成为现实的选择。具体来说cookie机制采用的
是在客户端保持状态的方案，而session机制采用的是在服务器端保持状态的方案。同时我们也看到，由于采用服务器端保持状态的方案在客户端也需要保存
一个标识，所以session机制可能需要借助于cookie机制来达到保存标识的目的，但实际上他更有其他选择。</p>
<p><strong>　　三、理解cookie机制</strong> </p>
<p>　　cookie机制的基本原理就如上面的例子相同简单，不过更有几个问题需要解决：&#8220;会员卡&#8221;怎么分发；&#8220;会员卡&#8221;的内容；及客户怎么使用&#8220;会员卡&#8221;。</p>
<p>　　正统的cookie分发是通过扩展HTTP协议来实现的，服务器通过在HTTP的响应头中加上一行特别的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或VBScript也能生成cookie。</p>
<p>　　而cookie的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie，如果某个cookie所声明的作
用范围大于等于将要请求的资源所在的位置，则把该cookie附在请求资源的HTTP请求头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出
示，如果某家分店还发行了自己的会员卡，那么进这家店的时候除了要出示麦当劳的会员卡，还要出示这家店的会员卡。</p>
<p>　　cookie的内容主要包括：名字，值，过期时间，路径和域。</p>
<p>　　其中域能指定某一个域比如.google.com，相当于总店招牌，比如宝洁公司，也能指定一个域下的具体某台机器比如www.google.com或froogle.google.com，能用飘柔来做比。</p>
<p>　　路径就是跟在<a href="http://sudu.cn/domain/" style="color: black; text-decoration: none;" target="_blank">域名</a>后面的URL路径，比如/或/foo等等，能用某飘柔专柜做比。</p>
<p>　　路径和域合在一起就构成了cookie的作用范围。</p>
<p>　　如果不设置过期时间，则表示这个cookie的生命期为浏览器会话期间，只要关闭浏览器窗口，cookie就消失了。这种生命期为浏览器会话期
的cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里，当然这种行为并不是规范规定的。如果设置了过期时间，浏览
器就会把cookie保存到硬盘上，关闭后再次打开浏览器，这些cookie仍然有效直到超过设定的过期时间。</p>
<p>　　<span style="color: red;"><span style="background-color: yellow;">存储在硬盘上的cookie能在不同的浏览器进程间共享，比如两个IE窗口。而对于保存在内存里的cookie，不同的浏览器有不同的处理方
式。对于IE，在一个打开的窗口上按Ctrl-N（或从文件菜单）打开的窗口能和原窗口共享，而使用其他方式新开的IE进程则不能共享已打开的窗口的内存
cookie；对于Mozilla
Firefox0.8，所有的进程和标签页都能共享同样的cookie。一般来说是用javascript的window.open打开的窗口会和原窗口
共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用session机制的web应用程式研发者造成非常
大的困扰。</span></span></p>
<p>　　下面就是个goolge设置cookie的响应头的例子</p>
<p>HTTP/1.1 302 Found<br />
Location: http://www.google.com/intl/zh-CN/<br />
Set-Cookie:
PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8;
expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com<br />
Content-Type: text/html</p>
<p align="center"><img alt="Session详解" src="http://www.webjx.com/upfiles/20051029/20051029091712_1.jpg" /></p>
<p>　　这是使用HTTPLook这个HTTP Sniffer软件来俘获的HTTP通讯纪录的一部分</p>
<p align="center"><img alt="Session详解" src="http://www.webjx.com/upfiles/20051029/20051029091723_2.jpg" /></p>
<p><br />
浏览器在再次访问goolge的资源时自动向外发送cookie</p>
<p align="center"><img alt="Session详解" src="http://www.webjx.com/upfiles/20051029/20051029091735_3.jpg" /> </p>
<p>　　使用Firefox能非常容易的观察现有的cookie的值</p>
<p>　　使用HTTPLook配合Firefox能非常容易的理解cookie的工作原理。</p>
<p align="center"><img alt="Session详解" src="http://www.webjx.com/upfiles/20051029/20051029091747_4.jpg" /></p>
<p><br />
IE也能设置在接受cookie前询问</p>
<p align="center"><img alt="Session详解" src="http://www.webjx.com/upfiles/20051029/20051029091758_5.jpg" /> </p>
<p>　　这是个询问接受cookie的对话框。</p>
<p><strong>　　四、理解session机制</strong></p>
<p>　session机制是一种服务器端的机制，服务器使用一种类似于散列表的结构（也可能就是使用散列表）来保存信息。</p>
<p>　　当程式需要为某个客户端的请求创建一个session的时候，服务器首先检查这个客户端的请求里是否已包含了一个session标识 -
称为session id，如果已包含一个session id则说明以前已为此客户端创建过session，服务器就按照session
id把这个session检索出来使用（如果检索不到，可能会新建一个），如果客户端请求不包含session
id，则为此客户端创建一个session并且生成一个和此session相关联的session id，session
id的值应该是个既不会重复，又不容易被找到规律以仿造的字符串，这个session id将被在本次响应中返回给客户端保存。</p>
<p>　　保存这个session
id的方式能采用cookie，这样在交互过程中浏览器能自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于
SEEESIONID，而。比如weblogic对于web应用程式生成的
cookie，JSESSIONID=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764，
他的名字就是JSESSIONID。</p>
<p>　　由于cookie能被人为的禁止，必须有其他机制以便在cookie被禁止时仍然能够把session
id传递回服务器。经常被使用的一种技术叫做URL重写，就是把session
id直接附加在URL路径的后面，附加方式也有两种，一种是作为URL路径的附加信息，表现形式为http://.....
/xxx;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
另一种是作为查询字符串附加在URL后面，表现形式为http://.....
/xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764<br />
这两种方式对于用户来说是没有差别的，只是服务器在解析的时候处理的方式不同，采用第一种方式也有利于把session id的信息和正常程式参数区分开来。</p>
<p>　　为了在整个交互过程中始终保持状态，就必须在每个客户端可能请求的路径后面都包含这个session id。</p>
<p>　　另一种技术叫做表单隐藏字段。就是服务器会自动修改表单，添加一个隐藏字段，以便在表单提交时能够把session id传递回服务器。比如下面的表单</p>
<p>&lt;form name="testform" action="/xxx"&gt;<br />
&lt;input type="text"&gt;<br />
&lt;/form&gt;</p>
<p>　　在被传递给客户端之前将被改写成</p>
<p>&lt;form name="testform" action="/xxx"&gt;<br />
&lt;input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764"&gt;<br />
&lt;input type="text"&gt;<br />
&lt;/form&gt;</p>
<p>　　这种技术目前已较少应用，笔者接触过的非常古老的iPlanet6(SunONE应用服务器的前身)就使用了这种技术。实际上这种技术能简单的用对action应用URL重写来代替。</p>
<p>　　在谈论session机制的时候，常常听到这样一种误解&#8220;只要关闭浏览器，session就消失了&#8221;。其实能想象一下会员卡的例子，除非顾客主
动对店家提出销卡，否则店家绝对不会轻易删除顾客的资料。对session来说也是相同的，除非程式通知服务器删除一个session，否则服务器会一直
保留，程式一般都是在用户做log
off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器他将要关闭，因此服务器根本不会有机会知道浏览器已关闭，之所以
会有这种错觉，是<span style="color: red;"><span style="background-color: #acff58;"><span style="background-color: yellow;"><span style="background-color: yellow;"><span style="color: red;">大<span style="color: red;">部分session机制都使用会话cookie来保存session id</span></span></span></span></span></span>，而关闭浏览器后这个session
id就消失了，再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上，或使用某种手段改写浏览器发出的
HTTP请求头，把原来的session id发送给服务器，则再次打开浏览器仍然能够找到原来的session。</p>
<p>　　恰恰是由于关闭浏览器不会导致session被删除，迫使服务器为seesion设置了一个失效时间，当距离客户端上一次使用session的时间超过这个失效时间时，服务器就能认为客户端已停止了活动，才会把session删除以节省存储空间。</p>
<p><strong>　　五、理解javax.servlet.http.HttpSession</strong></p>
<p>　　HttpSession是Java平台对session机制的实现规范，因为他仅仅是个接口，具体到每个web应用服务器的提供商，除了对规范支持之外，仍然会有一些规范里没有规定的细微差异。这里我们以BEA的Weblogic Server8.1作为例子来演示。</p>
<p>　　首先，Weblogic
Server提供了一系列的参数来控制他的HttpSession的实现，包括使用cookie的开关选项，使用URL重写的开关选项，session持
久化的设置，session失效时间的设置，及针对cookie的各种设置，比如设置cookie的名字、路径、域，cookie的生存时间等。</p>
<p>　　一般情况下，session都是存储在内存里，当服务器进程被停止或重启的时候，内存里的session也会被清空，如果设置了session
的持久化特性，服务器就会把session保存到硬盘上，当服务器进程重新启动或这些信息将能够被再次使用，Weblogic
Server支持的持久性方式包括文件、数据库、客户端cookie保存和复制。</p>
<p>　　复制严格说来不算持久化保存，因为session实际上还是保存在内存里，不过同样的信息被复制到各个cluster内的服务器进程中，这样即使某个服务器进程停止工作也仍然能从其他进程中取得session。</p>
<p>　　cookie生存时间的设置则会影响浏览器生成的cookie是否是个会话cookie。默认是使用会话cookie。有兴趣的能用他来试验我们在第四节里提到的那个误解。</p>
<p>　　cookie的路径对于web应用程式来说是个非常重要的选项，Weblogic Server对这个选项的默认处理方式使得他和其他服务器有明显的差别。后面我们会专题讨论。</p>
<p>　　关于session的设置参考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869</p>
<p><strong>　　六、HttpSession常见问题</strong></p>
<p>　　（在本小节中session的含义为⑤和⑥的混合）</p>
<p>　　1、session在何时被创建</p>
<p>　　一个常见的误解是以为session在有客户端访问时就被创建，然而事实是直到某server端程式调用
HttpServletRequest.getSession(true)这样的语句时才被创建，注意如果JSP没有显示的使用 &lt;%@page
session="false"%&gt; 关闭session，则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession
session = HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。</p>
<p>　　由于session会消耗内存资源，因此，如果不打算使用session，应该在所有的JSP中关闭他。</p>
<p>　　2、session何时被删除</p>
<p>　　综合前面的讨论，session在下列情况下被删除a.程式调用HttpSession.invalidate();或b.距离上一次收到客户端发送的session id时间间隔超过了session的超时设置;或c.服务器进程被停止（非持久session）</p>
<p>　　3、怎么做到在浏览器关闭时删除session</p>
<p>　　严格的讲，做不到这一点。能做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作，然后向服务器发送一个请求来删除session。不过对于浏览器崩溃或强行杀死进程这些非常规手段仍然无能为力。</p>
<p>　　4、有个HttpSessionListener是怎么回事</p>
<p>　　你能创建这样的listener去监视session的创建和销毁事件，使得在发生这样的事件时你能做一些相应的工作。注意是session的
创建和销毁动作触发listener，而不是相反。类似的和HttpSession有关的listener更有
HttpSessionBindingListener，HttpSessionActivationListener和
HttpSessionAttributeListener。</p>
<p>　　5、存放在session中的对象必须是可序列化的吗</p>
<p>　　不是必需的。需求对象可序列化只是为了session能够在集群中被复制或能够持久保存或在必要时server能够暂时把session交换出
内存。在Weblogic
Server的session中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本如果session中有不可序列化
的对象，在session销毁时会有一个Exception，非常奇怪。</p>
<p>　　6、怎么才能正确的应付客户端禁止cookie的可能性</p>
<p>　　对所有的URL使用URL重写，包括超链接，form的action，和重定向的URL，具体做法参见[6]<br />
http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770</p>
<p>　　7、开两个浏览器窗口访问应用程式会使用同一个session还是不同的session</p>
<p>　　参见第三小节对cookie的讨论，对session来说是只认id不认人，因此不同的浏览器，不同的窗口打开方式及不同的cookie存储方式都会对这个问题的答案有影响。</p>
<p>　　8、怎么防止用户打开两个浏览器窗口操作导致的session混乱</p>
<p>　　这个问题和防止表单多次提交是类似的，能通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端，同时保存在
session里，客户端提交表单时必须把这个id也返回服务器，程式首先比较返回的id和保存在session里的值是否一致，如果不一致则说明本次操
作已被提交过了。能参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript
window.open打开的窗口，一般不设置这个id，或使用独立的id，以防主窗口无法操作，建议不要再window.open打开的窗口里做修改操
作，这样就能不用设置。</p>
<p>　　9、为什么在Weblogic Server中改动session的值后要重新调用一次session.setValue<br />
做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改动，需要向其他服务器进程复制新的session值。</p>
<p>　　10、为什么session不见了</p>
<p>　　排除session正常失效的因素之外，服务器本身的可能性应该是微乎其微的，虽然笔者在iPlanet6SP1加若干补丁的Solaris版
本上倒也遇见过；浏览器插件的可能性次之，笔者也遇见过3721插件造成的问题；理论上防火墙或代理服务器在cookie处理上也有可能会出现问题。</p>
<p>　　出现这一问题的大部分原因都是程式的错误，最常见的就是在一个应用程式中去访问另外一个应用程式。我们在下一节讨论这个问题。</p>
<p id="#7">　　<strong>七、跨应用程式的session共享</strong><br />
<br />
常常有这样的情况，一个大项目被
分割成若干小项目研发，为了能够互不干扰，需求每个小项目作为一个独立的web应用程式研发，可是到了最后忽然发现某几个小项目之间需要共享一些信息，或
想使用session来实现SSO(single sign
on)，在session中保存login的用户信息，最自然的需求是应用程式间能够访问彼此的session。</p>
<p>　　然而按照Servlet规范，session的作用范围应该仅仅限于当前应用程式下，不同的应用程式之间是不能够互相访问对方的session
的。各个应用服务器从实际效果上都遵守了这一规范，不过实现的细节却可能各有不同，因此解决跨应用程式session共享的方法也各不相同。</p>
<p>　　首先来看一下Tomcat是怎么实现web应用程式之间session的隔离的，从Tomcat设置的cookie路径来看，他对不同的应用程
式设置的cookie路径是不同的，这样不同的应用程式所用的session
id是不同的，因此即使在同一个浏览器窗口里访问不同的应用程式，发送给服务器的session id也能是不同的。<br />
</p>
<p align="center"><img alt="Session详解" src="http://www.webjx.com/upfiles/20051029/20051029091813_6.jpg" /> <img alt="Session详解" src="http://www.webjx.com/upfiles/20051029/20051029091833_7.jpg" /> </p>
<p>　　根据这个特性，我们能推测Tomcat中session的内存结构大致如下。<br />
</p>
<p align="center"><img alt="Session详解" src="http://www.webjx.com/upfiles/20051029/20051029091848_8.jpg" /> </p>
<p>　　笔者以前用过的iPlanet也采用的是同样的方式，估计SunONE和iPlanet之间不会有太大的差别。对于这种方式的服务器，解决的思
路非常简单，实际实行起来也不难。要么让所有的应用程式共享一个session id，要么让应用程式能够获得其他应用程式的session id。</p>
<p>　　iPlanet中有一种非常简单的方法来实现共享一个session id，那就是把各个应用程式的cookie路径都设为/（实际上应该是/NASApp，对于应用程式来讲他的作用相当于根）。</p>
<p>&lt;session-info&gt;<br />
&lt;path&gt;/NASApp&lt;/path&gt;<br />
&lt;/session-info&gt;</p>
<p>　　需要注意的是，操作共享的session应该遵循一些编程约定，比如在session
attribute名字的前面加上应用程式的前缀，使得setAttribute("name",
"neo")变成setAttribute("app1.name", "neo")，以防止命名空间冲突，导致互相覆盖。</p>
<p><br />
在Tomcat中则没有这么方便的选择。在Tomcat版本3上，我们还能有一些手段来共享session。对于版本4以上的
Tomcat，目前笔者尚未发现简单的办法。只能借助于第三方的力量，比如使用文件、数据库、JMS或客户端cookie，URL参数或隐藏字段等手段。</p>
<p>　　我们再看一下Weblogic Server是怎么处理session的。<br />
</p>
<p align="center"><img alt="Session详解" src="http://www.webjx.com/upfiles/20051029/20051029091902_9.jpg" /> </p>
<p align="center"><img alt="Session详解" src="http://www.webjx.com/upfiles/20051029/20051029091916_10.jpg" /> </p>
<p>　　从截屏画面上能看到Weblogic Server对所有的应用程式设置的cookie的路径都是/，这是不是意味着在Weblogic
Server中默认的就能共享session了呢？然而一个小实验即可证实即使不同的应用程式使用的是同一个session，各个应用程式仍然只能访问自
己所设置的那些属性。这说明Weblogic Server中的session的内存结构可能如下<br />
</p>
<p align="center"><img alt="Session详解" src="http://www.webjx.com/upfiles/20051029/20051029091659_11.jpg" /> </p>
<p>　　对于这样一种结构，在session机制本身上来解决session共享的问题应该是不可能的了。除了借助于第三方的力量，比如使用文件、数据
库、JMS或客户端cookie，URL参数或隐藏字段等手段，更有一种较为方便的做法，就是把一个应用程式的session放到
ServletContext中，这样另外一个应用程式就能从ServletContext中取得前一个应用程式的引用。示例代码如下，</p>
<p>　　应用程式A</p>
<p>context.setAttribute("appA", session); </p>
<p>　　应用程式B</p>
<p>contextA = context.getContext("/appA");<br />
HttpSession sessionA = (HttpSession)contextA.getAttribute("appA"); </p>
<p>　　值得注意的是这种用法不可移植，因为根据ServletContext的JavaDoc，应用服务器能处于安全的原因对于context.getContext("/appA");返回空值，以上做法在Weblogic Server 8.1中通过。</p>
<p>　　那么Weblogic
Server为什么要把所有的应用程式的cookie路径都设为/呢？原来是为了SSO，凡是共享这个session的应用程式都能共享认证的信息。一个
简单的实验就能证实这一点，修改首先登录的那个应用程式的描述符weblogic.xml，把cookie路径修改为/appA访问另外一个应用程式会重
新需求登录，即使是反过来，先访问cookie路径为/的应用程式，再访问修改过路径的这个，虽然不再提示登录，不过登录的用户信息也会丢失。注意做这个
实验时认证方式应该使用FORM，因为浏览器和web服务器对basic认证方式有其他的处理方式，第二次请求的认证不是通过session来实现的。具
体请参看[7] secion 14.8 Authorization，你能修改所附的示例程式来做这些试验。</p>
<p>　　八、总结</p>
<p>　　session机制本身并不复杂，然而其实现和设置上的灵活性却使得具体情况复杂多变。这也需求我们不能把仅仅某一次的经验或某一个浏览器，服务器的经验当作普遍适用的经验，而是始终需要具体情况具体分析。</p>
<p>　　关于作者：</p>
<p>　　郎云鹏（dev2dev ID: hippiewolf），软件工程师，从事J2EE研发<br />
电子邮件：langyunpeng@yahoo.com.cn<br />
地址：大连软件园路31号科技大厦A座大连博涵咨询服务有限公司</p>
<img src ="http://www.blogjava.net/gm_jing/aggbug/338938.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2010-11-24 16:09 <a href="http://www.blogjava.net/gm_jing/articles/338938.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[引用]很好的解决中文乱码的filter(包括get和post提交两种方式) </title><link>http://www.blogjava.net/gm_jing/articles/325096.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Fri, 02 Jul 2010 09:55:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/325096.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/325096.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/325096.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/325096.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/325096.html</trackback:ping><description><![CDATA[<br />
<p>/**<br />
&nbsp;* Tomcat
GET方式表单提交乱码解决。<br />
&nbsp;* @author BeanSoft<br />
&nbsp;*<br />
&nbsp;*/<br />
public class
TomcatFormFilter implements Filter {<br />
&nbsp;public void destroy() {<br />
&nbsp;&nbsp;//
TODO Auto-generated method stub<br />
&nbsp;&nbsp;<br />
&nbsp;}</p>
<p>&nbsp;public void doFilter(ServletRequest request, ServletResponse
response, FilterChain chain) throws IOException, ServletException {<br />
&nbsp;&nbsp;//
1. ServletRequest 转成 HttpServletRequest<br />
&nbsp;&nbsp;HttpServletRequest req =
(HttpServletRequest)request;<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;// 2.
如果是GET，就生成一个Wrapper对象，覆盖获取参数的方法进行转码<br />
&nbsp;&nbsp;if("GET".equalsIgnoreCase(req.getMethod()))
{<br />
&nbsp;&nbsp;&nbsp;req = new MyRequest(req);<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;// 3.
如果是POST，request.setCharacterEncoding("UTF-8")<br />
&nbsp;&nbsp;else
if("POST".equalsIgnoreCase(req.getMethod())) {<br />
&nbsp;&nbsp;&nbsp;req.setCharacterEncoding("UTF-8");<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;//
4.调用chain.doFilter继续向后执行<br />
&nbsp;&nbsp;chain.doFilter(req, response);<br />
&nbsp;}</p>
<p>&nbsp;public void init(FilterConfig arg0) throws ServletException {<br />
&nbsp;&nbsp;//
TODO Auto-generated method stub<br />
&nbsp;&nbsp;<br />
&nbsp;}</p>
<p>}</p>
<p>/**<br />
&nbsp;* 包装器，覆盖 getParameter()和 getParameterValues() 这两个方法来进行转码。<br />
&nbsp;*
@author BeanSoft<br />
&nbsp;*<br />
&nbsp;*/<br />
class MyRequest extends
HttpServletRequestWrapper {</p>
<p>&nbsp;public MyRequest(HttpServletRequest request) {<br />
&nbsp;&nbsp;super(request);<br />
&nbsp;&nbsp;//
TODO Auto-generated constructor stub<br />
&nbsp;}<br />
&nbsp;<br />
&nbsp;/**<br />
&nbsp; * 字符串转码。<br />
&nbsp;
* @param input 输入字符串<br />
&nbsp; * @param srcEncoding 源字符串的编码<br />
&nbsp; * @param
targetEncoding 目标编码<br />
&nbsp; * @return 转换过的内容<br />
&nbsp; */<br />
&nbsp;public String
changeEncoding(String input, String srcEncoding, String targetEncoding) {<br />
&nbsp;&nbsp;try
{<br />
&nbsp;&nbsp;&nbsp;// 1. 获取源编码的bytes[]<br />
&nbsp;&nbsp;&nbsp;byte[] data =
input.getBytes(srcEncoding);<br />
&nbsp;&nbsp;&nbsp;// 2. 将bytes[]按照制定编码转换为String<br />
&nbsp;&nbsp;&nbsp;return
new String(data, targetEncoding);<br />
&nbsp;&nbsp;} catch
(UnsupportedEncodingException e) {<br />
&nbsp;&nbsp;&nbsp;// TODO Auto-generated catch
block<br />
&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;return input;<br />
&nbsp;}</p>
<p>&nbsp;@Override<br />
&nbsp;public String getParameter(String name) {<br />
&nbsp;&nbsp;String
value = super.getParameter(name);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;if(value != null) {<br />
&nbsp;&nbsp;&nbsp;value
= changeEncoding(value, "ISO8859-1", "UTF-8");<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;return
value;<br />
&nbsp;}</p>
<p>&nbsp;@Override<br />
&nbsp;public String[] getParameterValues(String name) {<br />
&nbsp;&nbsp;String[]
values = super.getParameterValues(name);<br />
&nbsp;&nbsp;if(values != null
&amp;&amp; values.length &gt; 0) {<br />
&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt;
values.length; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;String value = values[i];<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;values[i]
= changeEncoding(value, "ISO8859-1", "UTF-8");<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;return
values;<br />
&nbsp;}<br />
&nbsp;<br />
}</p>
<img src ="http://www.blogjava.net/gm_jing/aggbug/325096.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2010-07-02 17:55 <a href="http://www.blogjava.net/gm_jing/articles/325096.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>几种通讯协议的比较[转]</title><link>http://www.blogjava.net/gm_jing/articles/306142.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Wed, 16 Dec 2009 05:15:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/306142.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/306142.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/306142.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/306142.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/306142.html</trackback:ping><description><![CDATA[一、综述 <br />
本文比较了RMI，Hessian，Burlap，Httpinvoker，web service等5种通讯协议的在不同的数据结构和不同数据量时的传输性能。 <br />
RMI是java语言本身提供的远程通讯协议，稳定高效，是EJB的基础。但它只能用于JAVA程序之间的通讯。 <br />
Hessian和Burlap是caucho公司提供的开源协议，基于HTTP传输，服务端不用开防火墙端口。协议的规范公开，可以用于任意语言。 <br />
Httpinvoker是SpringFramework提供的远程通讯协议，只能用于JAVA程序间的通讯，且服务端和客户端必须使用SpringFramework。 <br />
Web service是连接异构系统或异构语言的首选协议，它使用SOAP形式通讯，可以用于任何语言，目前的许多开发工具对其的支持也很好。 <br />
<br />
测试结果显示，几种协议的通讯效率依次为： <br />
RMI &gt; Httpinvoker &gt;= Hessian &gt;&gt; Burlap &gt;&gt; web service <br />
RMI不愧是JAVA的首选远程调用协议，非常高效稳定，特别是在大数据量的情况下，与其他通讯协议的差距尤为明显。 <br />
HttpInvoker使用java的序列化技术传输对象，与RMI在本质上是一致的。从效率上看，两者也相差无几，HttpInvoker与RMI的传输时间基本持平。 <br />
Hessian在传输少量对象时，比RMI还要快速高效，但传输数据结构复杂的对象或大量数据对象时，较RMI要慢20%左右。 <br />
Burlap仅在传输1条数据时速度尚可，通常情况下，它的毫时是RMI的3倍。 <br />
Web Service的效率低下是众所周知的，平均来看，Web Service的通讯毫时是RMI的10倍。 <br />
<br />
二、结果分析 <br />
1、直接调用 <br />
直接调用的所有毫时都接近0，这说明程序处理几乎没有花费时间，记录的全部时间都是远程调用耗费的。 <br />
2、RMI调用 <br />
与设想的一样，RMI理所当然是最快的，在几乎所有的情况下，它的毫时都是最少的。特别是在数据结构复杂，数据量大的情况下，与其他协议的差距尤为明显。 <br />
为了充分发挥RMI的性能，另外做了测试类，不使用Spring，用原始的RMI形式（继承UnicastRemoteObject对象）提供服务并远程调用，与Spring对POJO包装成的RMI进行效率比较。结果显示：两者基本持平，Spring提供的服务还稍快些。 <br />
初步认为，这是因为Spring的代理和缓存机制比较强大，节省了对象重新获取的时间。 <br />
3、Hessian调用 <br />
caucho公司的resin服务器号称是最快的服务器，在java领域有一定的知名度。Hessian做为resin的组成部分，其设计也非常精简高效，实际运行情况也证明了这一点。平均来看，Hessian较RMI要慢20%左右，但这只是在数据量特别大，数据结构很复杂的情况下才能体现出来，中等或少量数据时，Hessian并不比RMI慢。 <br />
Hessian的好处是精简高效，可以跨语言使用，而且协议规范公开，我们可以针对任意语言开发对其协议的实现。目前已有实现的语言有：java, c++, .net, python, ruby。还没有delphi的实现。 <br />
另外，Hessian与WEB服务器结合非常好，借助WEB服务器的成熟功能，在处理大量用户并发访问时会有很大优势，在资源分配，线程排队，异常处理等方面都可以由成熟的WEB服务器保证。而RMI本身并不提供多线程的服务器。而且，RMI需要开防火墙端口，Hessian不用。 <br />
4、Burlap调用 <br />
Burlap与Hessian都是caucho公司的开源产品，只不过Hessian采用二进制的方式，而Burlap采用xml的格式。 <br />
测试结果显示，Burlap在数据结构不复杂，数据量中等的情况下，效率还是可以接受的，但如果数据量大，效率会急剧下降。平均计算，Burlap的调用毫时是RMI的3倍。 <br />
我认为，其效率低有两方面的原因，一个是XML数据描述内容太多，同样的数据结构，其传输量要大很多；另一方面，众所周知，对xml的解析是比较费资源的，特别对于大数据量情况下更是如此。 <br />
5、HttpInvoker调用 <br />
HttpInvoker是SpringFramework提供的JAVA远程调用方法，使用java的序列化机制处理对象的传输。从测试结果看，其效率还是可以的，与RMI基本持平。 <br />
不过，它只能用于JAVA语言之间的通讯，而且，要求客户端和服务端都使用SPRING框架。 <br />
另外，HttpInvoker 并没有经过实践的检验，目前还没有找到应用该协议的项目。 <br />
6、web service调用 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本次测试选用了apache的AXIS组件作为WEB SERVICE的实现，AXIS在WEB SERVICE领域相对成熟老牌。 <br />
为了仅测试数据传输和编码、解码的时间，客户端和服务端都使用了缓存，对象只需实例化一次。但是，测试结果显示，web service的效率还是要比其他通讯协议慢10倍。 <br />
如果考虑到多个引用指向同一对象的传输情况，web service要落后更多。因为RMI，Hessian等协议都可以传递引用，而web service有多少个引用，就要复制多少份对象实体。 <br />
Web service传输的冗余信息过多是其速度慢的原因之一，监控发现，同样的访问请求，描述相同的数据，web service返回的数据量是hessian协议的6.5倍。另外，WEB SERVICE的处理也很毫时，目前的xml解析器效率普遍不高，处理xml &lt;-&gt; bean很毫资源。从测试结果看，异地调用比本地调用要快，也从侧面说明了其毫时主要用在编码和解码xml文件上。这比冗余信息更为严重，冗余信息占用的只是网络带宽，而每次调用的资源耗费直接影响到服务器的负载能力。（MS的工程师曾说过，用WEB SERVICE不能负载100个以上的并发用户。） <br />
测试过程中还发现，web service编码不甚方便，对非基本类型需要逐个注册序列化和反序列化类，很麻烦，生成stub更累，不如spring + RMI/hessian处理那么流畅简洁。而且，web service不支持集合类型，只能用数组，不方便。
<img src ="http://www.blogjava.net/gm_jing/aggbug/306142.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2009-12-16 13:15 <a href="http://www.blogjava.net/gm_jing/articles/306142.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SiteMesh</title><link>http://www.blogjava.net/gm_jing/articles/285852.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Tue, 07 Jul 2009 09:43:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/285852.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/285852.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/285852.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/285852.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/285852.html</trackback:ping><description><![CDATA[<p><span class="style7"><font face="宋体" color="#0000ff" size="4"><font size="5">&nbsp; </p>
<p><strong>一、SiteMesh项目简介</strong></p>
<p></font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#000000">OS(OpenSymphony)的SiteMesh是一个用来在JSP中实现页面布局和装饰（layout and decoration）<br />
的框架组件，能够帮助网站开发人员较容易实现页面中动态内容和静态装饰外观的分离。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sitemesh是由一个基于Web页面布局、装饰以及与现存Web应用整合的框架。它能帮助我们在由大<br />
量页面构成的项目中创建一致的页面布局和外观，如一致的导航条，一致的banner，一致的版权，等等。<br />
它不仅仅能处理动态的内容，如jsp，php，asp等产生的内容，它也能处理静态的内容，如htm的内容，<br />
使得它的内容也符合你的页面结构的要求。甚至于它能将HTML文件象include那样将该文件作为一个面板<br />
的形式嵌入到别的文件中去。所有的这些，都是GOF的Decorator模式的最生动的实现。尽管它是由java语言来实现的，但它能与其他Web应用很好地集成。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 官方：<a href="http://www.opensymphony.com/sitemesh/"><font color="#1d58d1">http://www.opensymphony.com/sitemesh/</font></a><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 下载地址：<a href="http://www.opensymphony.com/sitemesh/download.action"><font color="#1d58d1">http://www.opensymphony.com/sitemesh/download.action</font></a> 目前的最新版本是<font size="3"><span class="releaseName"><strong>Version 2.3</strong></span>；<br />
</font><br />
<font color="#0000ff" size="5"><strong>二、为什么要使用SiteMesh?<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</strong><font size="4"><font color="#000000">我们的团队开发J2EE应用的时候，经常会碰到一个比较头疼的问题：<br />
<br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#000000"><font size="4"><font color="#ff0000">由于Web页面是由不同的人所开发，所以开发出来的界面通常是千奇百怪，通常让项目管理人员苦笑不得。</font><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></font><font color="#000000" size="4">而实际上，任何一个项目都会要求界面的统一风格和美观，既然风格统一，那就说明UI层肯定有很多可以抽出来<br />
共用的静态或动态部分；如何整合这些通用的静态或动态UI呢？Apache Tiles框架站了出来很好的解决了这一问题，<br />
再加上他与struts的完美集成，导致大小项目都把他作为UI层的首选框架，<br />
</font><font color="#0000ff"><br />
</font><font color="#ff0000" size="5"><strong>但是</strong>：<br />
</font><br />
<font size="4">&nbsp;&nbsp;&nbsp;<font color="#000000">Tiles确实有着它很多的不足之处,下文我会介绍,本文想说的是，除了Apache Tiles框架,其实我们还有更好的解<br />
决方案，那就是:</font></font><strong><font size="5">SiteMesh；<br />
<br />
<font color="#ff0000">本文</font></font><br />
</strong><font size="4">&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;<font color="#000000">介绍了一个基于Web页面的布局、装饰以及应用整合的框架Sitemesh，它能帮助你为你的应用创建一致的外观，<br />
很好的取代Apache Tiles;<br />
</font></font><br />
<font color="#0000ff"><strong>三、SiteMesh VS Apache Tiles</strong></font><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#000000"><font size="4">用过struts的朋友应该对Apache Tiles的不会陌生，我曾经有一篇文章介绍过</font><a title="struts中tiles框架的组合与继承" href="http://www.blogjava.net/yjhmily/archive/2006/10/25/77263.html"><font color="#1d58d1" size="4">struts中tiles框架的组合与继承</font></a><font size="4">，<br />
现在怎么看怎么觉得复杂;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从使用角度来看，Tiles似乎是Sitemesh标签&lt;page:applyDecorator&gt;的一个翻版。其实sitemesh最强的<br />
一个特性是sitemesh将decorator模式用在过滤器上。任何需要被装饰的页面都不知道它要被谁装饰，所以它就<br />
可以用来装璜来自php、asp</font><font size="4">、CGI等产生的页面了。你可以定义若干个装饰器，根据参数动态地选择装饰器，<br />
产生动态的外观以满足你的需求。它也有一套功能强大的属性体系，它能帮助你构建功能强大而灵活的装饰器。<br />
相比较而言，在这方面Tiles就逊色许多。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 个人觉得在团队开发里面，Apache Tiles框架会导致所有人不仅仅要了解并且清楚Apache Tiles的存在，<br />
并且要特别熟悉每一个Tiles layout模板的作用，否则就可能出现用错模板的情况；除此之外，每个人涉及到<br />
的所有WEB页面都需要去配置文件里面逐个配置，不仅麻烦出错的几率还高；<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 而以上所有的不足都是SiteMesh所不存在的；<br />
<br />
<br />
<font color="#0000ff" size="5"><strong>四、SiteMesh的基本原理<br />
</strong></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font size="3"><font size="4">一个请求到服务器后，如果该请求需要sitemesh装饰，服务器先解释被请求的资源，然后根据配置文件<br />
获得用于该请求的装饰器，最后用装饰器装饰被请求资源，将结果一同返回给客户端浏览器。</font><br />
</font></font></font><br />
<strong>五、如何使用SiteMesh<br />
<br />
</strong>&nbsp;&nbsp;<font size="4"> 这里以struts2+spring2+hibernate3构架的系统为例</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font size="4"><font color="#000000">1、下载SiteMesh</font><font color="#000000"> <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 下载地址：<a href="http://www.opensymphony.com/sitemesh/download.action"><font color="#1d58d1">http://www.opensymphony.com/sitemesh/download.action</font></a> 目前的最新版本是<font size="3"><span class="releaseName"><strong>Version 2.3</strong></span>；<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2、在工程中引入SiteMesh的必要jar包，和struts2-sitemesh-plugin-2.0.8.jar；<br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3、修改你的web.xml,在里面加入sitemesh的过滤器，示例代码如下：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</p>
<div style="border-right: rgb(204,204,204) 1px solid; padding-right: 5px; border-top: rgb(204,204,204) 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: rgb(204,204,204) 1px solid; width: 98%; padding-top: 4px; border-bottom: rgb(204,204,204) 1px solid; background-color: rgb(238,238,238)"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)"> sitemesh配置 </span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">filter</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">filter-name</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">sitemesh</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">filter-name</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">filter-class</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; com.opensymphony.module.sitemesh.filter.PageFilter<br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">filter-class</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">filter</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">filter-mapping</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">filter-name</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">sitemesh</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">filter-name</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">url-pattern</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">/*</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">url-pattern</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">filter-mapping</span><span style="color: rgb(0,0,255)">&gt;</span></div>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意过滤器的位置：应该在struts2的org.apache.struts2.dispatcher.FilterDispatcher过滤器之前org.apache.struts2.dispatcher.ActionContextCleanUp过滤器之后，否则会有问题；<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4、在下载的SiteMesh包中找到sitemesh.xml，(\sitemesh-2.3\src\example-webapp\WEB-INF目录下就有)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 将其拷贝到/WEB-INF目录下；<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5、在sitemesh.xml文件中有一个property结点(如下)，该结点指定了decorators.xml在工程中的位置，让sitemesh.xml能找到他;<br />
按照此路径新建decorators.xml文件，当然这个路径你可以任意改变，只要property结点的value值与其匹配就行；</p>
<p>&nbsp;</p>
<div style="border-right: rgb(204,204,204) 1px solid; padding-right: 5px; border-top: rgb(204,204,204) 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: rgb(204,204,204) 1px solid; width: 98%; padding-top: 4px; border-bottom: rgb(204,204,204) 1px solid; background-color: rgb(238,238,238)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">property </span><span style="color: rgb(255,0,0)">name</span><span style="color: rgb(0,0,255)">="decorators-file"</span><span style="color: rgb(255,0,0)"> value</span><span style="color: rgb(0,0,255)">="/WEB-INF/sitemesh/decorators.xml"</span><span style="color: rgb(0,0,255)">/&gt;<br />
<br />
</span></div>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6、在WebRoot目录下新建decorators目录，并在该目录下新建一个模板jsp，根据具体项目风格编辑该模板，<br />
如下示例：我的模板：main.jsp</p>
<p>&nbsp;</p>
<div style="border-right: rgb(204,204,204) 1px solid; padding-right: 5px; border-top: rgb(204,204,204) 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: rgb(204,204,204) 1px solid; width: 98%; padding-top: 4px; border-bottom: rgb(204,204,204) 1px solid; background-color: rgb(238,238,238)"><span style="color: rgb(0,0,0); background-color: rgb(255,255,0)">&lt;%</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">@ page language</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">=</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">java</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)"> pageEncoding</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">=</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">UTF-8</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(255,255,0)">%&gt;</span><span style="color: rgb(0,0,0)"><br />
</span><span style="color: rgb(0,0,0); background-color: rgb(255,255,0)">&lt;%</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">@taglib prefix</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">=</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">decorator</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)"><br />
&nbsp;&nbsp;&nbsp;&nbsp; uri</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">=</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">http://www.opensymphony.com/sitemesh/decorator</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(255,255,0)">%&gt;</span><span style="color: rgb(0,0,0)"><br />
</span><span style="color: rgb(0,0,0); background-color: rgb(255,255,0)">&lt;%</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">@taglib prefix</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">=</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">page</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)"> uri</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">=</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">http://www.opensymphony.com/sitemesh/page</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(255,255,0)">%&gt;</span><span style="color: rgb(0,0,0)"><br />
</span><span style="color: rgb(0,0,255)">&lt;!</span><span style="color: rgb(255,0,255)">DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 Transitional//EN" <br />
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
</span><span style="color: rgb(0,0,0); background-color: rgb(255,255,0)">&lt;%</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)"><br />
&nbsp;&nbsp;&nbsp;&nbsp; response.setHeader(</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">Pragma</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">, </span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">no-cache</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">);<br />
&nbsp;&nbsp;&nbsp;&nbsp; response.setHeader(</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">Cache-Control</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">, </span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">no-cache</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">);<br />
&nbsp;&nbsp;&nbsp;&nbsp; response.setDateHeader(</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">Expires</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">"</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">, </span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">0</span><span style="color: rgb(0,0,0); background-color: rgb(245,245,245)">);<br />
</span><span style="color: rgb(0,0,0); background-color: rgb(255,255,0)">%&gt;</span><span style="color: rgb(0,0,0)"><br />
</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">html</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">head</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">title</span><span style="color: rgb(0,0,255)">&gt;&lt;</span><span style="color: rgb(128,0,0)">decorator:title </span><span style="color: rgb(255,0,0)">default</span><span style="color: rgb(0,0,255)">="kangxm test"</span><span style="color: rgb(255,0,0)"> </span><span style="color: rgb(0,0,255)">/&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">title</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)"> 页面Head由引用模板的子页面来替换 </span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">decorator:head </span><span style="color: rgb(0,0,255)">/&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">head</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">body </span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="page-home"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">div </span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="page-total"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">div </span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="page-header"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">table </span><span style="color: rgb(255,0,0)">width</span><span style="color: rgb(0,0,255)">="100%"</span><span style="color: rgb(255,0,0)"> border</span><span style="color: rgb(0,0,255)">="0"</span><span style="color: rgb(255,0,0)"> cellspacing</span><span style="color: rgb(0,0,255)">="0"</span><span style="color: rgb(255,0,0)"> cellpadding</span><span style="color: rgb(0,0,255)">="0"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">tr</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">td</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">div </span><span style="color: rgb(255,0,0)">class</span><span style="color: rgb(0,0,255)">="topFunc"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我的账户<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 退出<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">div</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">td</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">tr</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">table</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">div</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">div</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)"> end header </span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)">&nbsp;&nbsp; Menu Tag begin </span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">div </span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="page-menu"</span><span style="color: rgb(255,0,0)"> style</span><span style="color: rgb(0,0,255)">="margin-top: 8px; margin-bottom: 8px;"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">div</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这里放菜单<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">div</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">div</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)">&nbsp;&nbsp; Menu Tag end </span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">div </span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="page-content"</span><span style="color: rgb(255,0,0)"> class</span><span style="color: rgb(0,0,255)">="clearfix"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">center</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">table </span><span style="color: rgb(255,0,0)">width</span><span style="color: rgb(0,0,255)">="100%"</span><span style="color: rgb(255,0,0)"> border</span><span style="color: rgb(0,0,255)">="0"</span><span style="color: rgb(255,0,0)"> cellpadding</span><span style="color: rgb(0,0,255)">="0"</span><span style="color: rgb(255,0,0)"> cellspacing</span><span style="color: rgb(0,0,255)">="0"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">tr</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">td</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">decorator:body </span><span style="color: rgb(0,0,255)">/&gt;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)"> 这里的内容由引用模板的子页面来替换 </span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">td</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">tr</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">table</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">center</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">div</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)"> end content </span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">div </span><span style="color: rgb(255,0,0)">id</span><span style="color: rgb(0,0,255)">="page-footer"</span><span style="color: rgb(255,0,0)"> class</span><span style="color: rgb(0,0,255)">="clearfix"</span><span style="color: rgb(0,0,255)">&gt;<br />
</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这里放页面底部<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)"> end footer </span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">div</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)"> end page </span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">body</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">html</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
</span></div>
<p>&nbsp;</p>
<p><br />
这就是个简单的模板，页面的头和脚都由模板里的静态HTML决定了，主页面区域用的是&lt;decorator:body /&gt;标签；<br />
也就是说凡是能进入过滤器的请求生成的页面都会默认加上模板上的头和脚，然后页面自身的内容将自动放到&lt;decorator:body /&gt;标签所在位置；<br />
<br />
&lt;decorator:title default="Welcome to test sitemesh!" /&gt;：读取被装饰页面的标题，并给出了默认标题。<br />
&lt;decorator:head /&gt;：读取被装饰页面的&lt;head&gt;中的内容；<br />
&lt;decorator:body /&gt;：读取被装饰页面的&lt;body&gt;中的内容；<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7、说到这里大家就要想了，那如果某个特殊的需求请求路径在过滤器的范围内，但又不想使用模板怎么办？<br />
你总不能这么不讲道理吧！<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 大家放心吧，SiteMesh早就考虑到这一点了，上面第5步说道的decorators.xml这个时候就起到作用了！<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
下面是我的decorators.xml：</p>
<p>&nbsp;</p>
<div style="border-right: rgb(204,204,204) 1px solid; padding-right: 5px; border-top: rgb(204,204,204) 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: rgb(204,204,204) 1px solid; width: 98%; padding-top: 4px; border-bottom: rgb(204,204,204) 1px solid; background-color: rgb(238,238,238)"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: rgb(0,0,255)">&lt;?</span><span style="color: rgb(255,0,255)">xml version="1.0" encoding="ISO-8859-1"</span><span style="color: rgb(0,0,255)">?&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">decorators </span><span style="color: rgb(255,0,0)">defaultdir</span><span style="color: rgb(0,0,255)">="/decorators"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,128,0)">&lt;!--</span><span style="color: rgb(0,128,0)"> Any urls that are excluded will never be decorated by Sitemesh </span><span style="color: rgb(0,128,0)">--&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">excludes</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">pattern</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">/index.jsp*</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">pattern</span><span style="color: rgb(0,0,255)">&gt;<br />
</span><span style="color: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">pattern</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">/login/*</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">pattern</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">excludes</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">decorator </span><span style="color: rgb(255,0,0)">name</span><span style="color: rgb(0,0,255)">="main"</span><span style="color: rgb(255,0,0)"> page</span><span style="color: rgb(0,0,255)">="main.jsp"</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;</span><span style="color: rgb(128,0,0)">pattern</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)">/*</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">pattern</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">decorator</span><span style="color: rgb(0,0,255)">&gt;</span><span style="color: rgb(0,0,0)"><br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: rgb(0,0,255)">&lt;/</span><span style="color: rgb(128,0,0)">decorators</span><span style="color: rgb(0,0,255)">&gt;</span></div>
<p>&nbsp;</p>
<p><br />
decorators.xml有两个主要的结点：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; decorator结点指定了模板的位置和文件名，通过pattern来指定哪些路径引用哪个模板<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; excludes结点则指定了哪些路径的请求不使用任何模板<br />
<br />
如上面代码，/index.jsp和凡是以/login/开头的请求路径一律不使用模板；<br />
<br />
另外还有一点要注意的是：decorators结点的defaultdir属性指定了模板文件存放的目录;<br />
<br />
<font color="#0000ff" size="5"><strong>六、实战感受</strong></font><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 刚刚做完一个用到sitemesh的项目，跟以前用tiles框架相比，最大的感受就是简单，系统设计阶段<br />
就把模板文件和sitemesh框架搭好了！哪些页面使用框架哪些不使用，全部都通过UI Demo很快就定义出来了；<br />
在接下来的开发中所有成员几乎感受不到sitemesh的存在，各自仅仅关心自己的模块功能实现；</p>
<p></font></font><font color="#0000ff" size="5"><strong>七、总结<br />
<br />
</strong><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;<font size="4">使用sitemesh给我们带来的是不仅仅是页面结构问题，它的出现让我们有更多的时间去关注底层业务<br />
逻辑，而不是整个页面的风格和结构。它让我们摆脱了大量用include方式复用页面尴尬局面，也避免了tiles<br />
框架在团队开发中的复杂度，它还提供了很大的灵活性以及给我们提供了整合异构Web系统页面的一种方案。</font></font></font></font></font></font></span></p>
<img src ="http://www.blogjava.net/gm_jing/aggbug/285852.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2009-07-07 17:43 <a href="http://www.blogjava.net/gm_jing/articles/285852.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JavaEE集群原理</title><link>http://www.blogjava.net/gm_jing/articles/270009.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Mon, 11 May 2009 02:32:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/270009.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/270009.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/270009.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/270009.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/270009.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Preface More and more mission-critical and large scale applications are now running on Java 2, Enterprise Edition (J2EE). Those mission-critical applications such as banking and billing ask for more...&nbsp;&nbsp;<a href='http://www.blogjava.net/gm_jing/articles/270009.html'>阅读全文</a><img src ="http://www.blogjava.net/gm_jing/aggbug/270009.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2009-05-11 10:32 <a href="http://www.blogjava.net/gm_jing/articles/270009.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]JNDI的一篇文章</title><link>http://www.blogjava.net/gm_jing/articles/265884.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Wed, 15 Apr 2009 15:26:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/265884.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/265884.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/265884.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/265884.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/265884.html</trackback:ping><description><![CDATA[<h3 class="" title="">&nbsp;</h3>
<strong></strong>------------ <br />
JNDI是 Java 命名与目录接口（Java Naming and Directory Interface），在J2EE规范中是重要的规范之一，不少专家认为，没有透彻理解JNDI的意义和作用，就没有真正掌握J2EE特别是EJB的知识。 <br />
<br />
那么，JNDI到底起什么作用？<span style="color: red">//带着问题看文章是最有效的</span> <br />
<br />
要了解JNDI的作用，我们可以从&#8220;如果不用JNDI我们怎样做？用了JNDI后我们又将怎样做？&#8221;这个问题来探讨。 <br />
<br />
没有JNDI的做法： <br />
<br />
程序员开发时，知道要开发访问MySQL数据库的应用，于是将一个对 MySQL JDBC 驱动程序类的引用进行了编码，并通过使用适当的 JDBC URL 连接到数据库。 <br />
就像以下代码这样： <br />
<br />
<div class="blog_content">
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://avaj.javaeye.com/blog/151736#"><img alt="复制代码" src="http://avaj.javaeye.com/images/icon_copy.gif" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>Connection&nbsp;conn=</span><span class="keyword">null</span><span>; &nbsp;&nbsp;</span></span></li>
    <li><span class="keyword">try</span><span>&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;Class.forName(</span><span class="string">"com.mysql.jdbc.Driver"</span><span>, &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">true</span><span>,&nbsp;Thread.currentThread().getContextClassLoader()); &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;conn=DriverManager. &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;getConnection(</span><span class="string">"jdbc:mysql://MyDBServer?user=qingfeng&amp;password=mingyue"</span><span>); &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;...... &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;conn.close(); &nbsp;&nbsp;</span></li>
    <li><span>}&nbsp;</span><span class="keyword">catch</span><span>(Exception&nbsp;e)&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;e.printStackTrace(); &nbsp;&nbsp;</span></li>
    <li><span>}&nbsp;</span><span class="keyword">finally</span><span>&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;</span><span class="keyword">if</span><span>(conn!=</span><span class="keyword">null</span><span>)&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">try</span><span>&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;conn.close(); &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span class="keyword">catch</span><span>(SQLException&nbsp;e)&nbsp;{} &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">Connection conn=null;
try {
Class.forName("com.mysql.jdbc.Driver",
true, Thread.currentThread().getContextClassLoader());
conn=DriverManager.
getConnection("jdbc:mysql://MyDBServer?user=qingfeng&amp;password=mingyue");
......
conn.close();
} catch(Exception e) {
e.printStackTrace();
} finally {
if(conn!=null) {
try {
conn.close();
} catch(SQLException e) {}
}
}</pre>
<br />
<br />
<br />
这是传统的做法，也是以前非Java程序员（如Delphi、VB等）常见的做法。这种做法一般在小规模的开发过程中不会产生问题，只要程序员熟悉Java语言、了解JDBC技术和MySQL，可以很快开发出相应的应用程序。 <br />
<br />
没有JNDI的做法存在的问题： <br />
1、数据库服务器名称MyDBServer 、用户名和口令都可能需要改变，由此引发JDBC URL需要修改； <br />
2、数据库可能改用别的产品，如改用DB2或者Oracle，引发JDBC驱动程序包和类名需要修改； <br />
3、随着实际使用终端的增加，原配置的连接池参数可能需要调整； <br />
4、...... <br />
<br />
解决办法： <br />
程序员应该不需要关心&#8220;具体的数据库后台是什么？JDBC驱动程序是什么？JDBC URL格式是什么？访问数据库的用户名和口令是什么？&#8221;等等这些问题，程序员编写的程序应该没有对 JDBC 驱动程序的引用，没有服务器名称，没有用户名称或口令 —— 甚至没有数据库池或连接管理。而是把这些问题交给J2EE容器来配置和管理，程序员只需要对这些配置和管理进行引用即可。 <br />
<br />
由此，就有了JNDI。 <br />
<span style="color: red">//看的出来，是为了一个最最核心的问题：是为了解耦，是为了开发出更加可维护、可扩展//的系统</span> <br />
<br />
用了JNDI之后的做法： <br />
首先，<span style="color: red">在在J2EE容器中配置JNDI参数</span>，定义一个数据源，也就是JDBC引用参数，给这个数据源设置一个名称；然后，在程序中，通过数据源名称引用数据源从而访问后台数据库。 <br />
<br />
<span style="color: red">//红色的字可以看出，JNDI是由j2ee容器提供的功能</span> <br />
<br />
具体操作如下（以JBoss为例）： <br />
1、配置数据源 <br />
在JBoss 的 D:\jboss420GA\docs\examples\jca 文件夹下面，有很多不同数据库引用的数据源定义模板。将其中的 mysql-ds.xml 文件Copy到你使用的服务器下，如 D:\jboss420GA\server\default\deploy。 <br />
修改 mysql-ds.xml 文件的内容，使之能通过JDBC正确访问你的MySQL数据库，如下： <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://avaj.javaeye.com/blog/151736#"><img alt="复制代码" src="http://avaj.javaeye.com/images/icon_copy.gif" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>&lt;?xml&nbsp;version=</span><span class="string">"1.0"</span><span>&nbsp;encoding=</span><span class="string">"UTF-8"</span><span>?&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&lt;datasources&gt; &nbsp;&nbsp;</span></li>
    <li><span>&lt;local-tx-datasource&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;jndi-name&gt;MySqlDS&lt;/jndi-name&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;connection-url&gt;jdbc:mysql:</span><span class="comment">//localhost:3306/lw&lt;/connection-url&gt; </span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;driver-</span><span class="keyword">class</span><span>&gt;com.mysql.jdbc.Driver&lt;/driver-</span><span class="keyword">class</span><span>&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;user-name&gt;root&lt;/user-name&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;password&gt;rootpassword&lt;/password&gt; &nbsp;&nbsp;</span></li>
    <li><span>&lt;exception-sorter-</span><span class="keyword">class</span><span>-name&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter &nbsp;&nbsp;</span></li>
    <li><span>&lt;/exception-sorter-</span><span class="keyword">class</span><span>-name&gt; &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;metadata&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;type-mapping&gt;mySQL&lt;/type-mapping&gt; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/metadata&gt; &nbsp;&nbsp;</span></li>
    <li><span>&lt;/local-tx-datasource&gt; &nbsp;&nbsp;</span></li>
    <li><span>&lt;/datasources&gt;&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;datasources&gt;
&lt;local-tx-datasource&gt;
&lt;jndi-name&gt;MySqlDS&lt;/jndi-name&gt;
&lt;connection-url&gt;jdbc:mysql://localhost:3306/lw&lt;/connection-url&gt;
&lt;driver-class&gt;com.mysql.jdbc.Driver&lt;/driver-class&gt;
&lt;user-name&gt;root&lt;/user-name&gt;
&lt;password&gt;rootpassword&lt;/password&gt;
&lt;exception-sorter-class-name&gt;
org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter
&lt;/exception-sorter-class-name&gt;
&lt;metadata&gt;
&lt;type-mapping&gt;mySQL&lt;/type-mapping&gt;
&lt;/metadata&gt;
&lt;/local-tx-datasource&gt;
&lt;/datasources&gt;</pre>
<br />
<br />
这里，定义了一个名为MySqlDS的数据源，其参数包括JDBC的URL，驱动类名，用户名及密码等。 <br />
<br />
2、在程序中引用数据源： <br />
<br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://avaj.javaeye.com/blog/151736#"><img alt="复制代码" src="http://avaj.javaeye.com/images/icon_copy.gif" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>Connection&nbsp;conn=</span><span class="keyword">null</span><span>; &nbsp;&nbsp;</span></span></li>
    <li><span class="keyword">try</span><span>&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;Context&nbsp;ctx=</span><span class="keyword">new</span><span>&nbsp;InitialContext(); &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;Object&nbsp;datasourceRef=ctx.lookup(</span><span class="string">"java:MySqlDS"</span><span>);&nbsp;</span><span class="comment">//引用数据源 </span><span>&nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;DataSource&nbsp;ds=(Datasource)datasourceRef; &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;conn=ds.getConnection(); &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;...... &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;c.close(); &nbsp;&nbsp;</span></li>
    <li><span>}&nbsp;</span><span class="keyword">catch</span><span>(Exception&nbsp;e)&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;e.printStackTrace(); &nbsp;&nbsp;</span></li>
    <li><span>}&nbsp;</span><span class="keyword">finally</span><span>&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;</span><span class="keyword">if</span><span>(conn!=</span><span class="keyword">null</span><span>)&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">try</span><span>&nbsp;{ &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;conn.close(); &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span class="keyword">catch</span><span>(SQLException&nbsp;e)&nbsp;{&nbsp;} &nbsp;&nbsp;</span></span></li>
    <li><span>&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">Connection conn=null;
try {
Context ctx=new InitialContext();
Object datasourceRef=ctx.lookup("java:MySqlDS"); //引用数据源
DataSource ds=(Datasource)datasourceRef;
conn=ds.getConnection();
......
c.close();
} catch(Exception e) {
e.printStackTrace();
} finally {
if(conn!=null) {
try {
conn.close();
} catch(SQLException e) { }
}
}</pre>
<br />
<br />
直接使用JDBC或者通过JNDI引用数据源的编程代码量相差无几，但是现在的程序可以不用关心具体JDBC参数了。<span style="color: red">//解藕了，可扩展了</span> <br />
在系统部署后，如果数据库的相关参数变更，只需要重新配置 mysql-ds.xml 修改其中的JDBC参数，只要保证数据源的名称不变，那么程序源代码就无需修改。 <br />
<br />
由此可见，<span style="color: red">JNDI避免了程序与数据库之间的紧耦合，使应用更加易于配置、易于部署</span>。 <br />
<br />
JNDI的扩展： <br />
JNDI在满足了数据源配置的要求的基础上，还进一步扩充了作用：所有与系统外部的资源的引用，都可以通过JNDI定义和引用。 <br />
<span style="color: red">//注意什么叫资源</span> <br />
<br />
所以，在J2EE规范中，J2EE 中的资源并不局限于 JDBC 数据源。引用的类型有很多，其中包括资源引用（已经讨论过）、环境实体和 EJB 引用。特别是 EJB 引用，它暴露了 JNDI 在 J2EE 中的另外一项关键角色：查找其他应用程序组件。 <br />
<br />
EJB 的 JNDI 引用非常类似于 JDBC 资源的引用。在服务趋于转换的环境中，这是一种很有效的方法。可以对应用程序架构中所得到的所有组件进行这类配置管理，从 EJB 组件到 JMS 队列和主题，再到简单配置字符串或其他对象，这可以降低随时间的推移服务变更所产生的维护成本，同时还可以简化部署，减少集成工作。外部资源&#8221;。 <br />
<br />
<br />
总结： <br />
<span style="color: red">J2EE 规范要求所有 J2EE 容器都要提供 JNDI 规范的实现。</span><span style="color: red">//sun 果然喜欢制定规范</span>JNDI 在 J2EE 中的角色就是&#8220;交换机&#8221; —— J2EE 组件在运行时间接地查找其他组件、资源或服务的通用机制。在多数情况下，提供 JNDI 供应者的容器可以充当有限的数据存储，这样管理员就可以设置应用程序的执行属性，并让其他应用程序引用这些属性（Java 管理扩展（Java Management Extensions，JMX）也可以用作这个目的）。JNDI 在 J2EE 应用程序中的主要角色就是提供间接层，这样组件就可以发现所需要的资源，而不用了解这些间接性。 <br />
<br />
在 J2EE 中，JNDI 是把 J2EE 应用程序合在一起的粘合剂，JNDI 提供的间接寻址允许跨企业交付可伸缩的、功能强大且很灵活的应用程序。这是 J2EE 的承诺，而且经过一些计划和预先考虑，这个承诺是完全可以实现的。 <br />
<br />
<strong><br />
从上面的文章中可以看出： <br />
1、JNDI 提出的目的是为了解藕，是为了开发更加容易维护，容易扩展，容易部署的应用。 <br />
2、JNDI 是一个sun提出的一个规范(类似于jdbc),具体的实现是各个j2ee容器提供商，sun&nbsp;&nbsp; 只是要求，j2ee容器必须有JNDI这样的功能。 <br />
3、JNDI 在j2ee系统中的角色是&#8220;交换机&#8221;，是J2EE组件在运行时间接地查找其他组件、资源或服务的通用机制。 <br />
4、JNDI 是通过资源的名字来查找的，资源的名字在整个j2ee应用中(j2ee容器中)是唯一的。 <br />
<br />
再转一篇文章： <br />
</strong><br />
<br />
JNDI全称 Java Naming and Directory Interface <br />
JNDI是Java平台的一个标准扩展，提供了一组接口、类和关于命名空间的概念。如同其它很多Java技术一样，JDNI是provider-based的技术，暴露了一个API和一个服务供应接口（SPI）。这意味着任何基于名字的技术都能通过JNDI而提供服务，只要JNDI支持这项技术。JNDI目前所支持的技术包括LDAP、CORBA Common Object Service（COS）名字服务、RMI、NDS、DNS、Windows注册表等等。很多J2EE技术，包括EJB都依靠JNDI来组织和定位实体。 <br />
JDNI通过绑定的概念将对象和名称联系起来。在一个文件系统中，文件名被绑定给文件。在DNS中，一个IP地址绑定一个URL。在目录服务中，一个对象名被绑定给一个对象实体。 <br />
JNDI中的一组绑定作为上下文来引用。每个上下文暴露的一组操作是一致的。例如，每个上下文提供了一个查找操作，返回指定名字的相应对象。每个上下文都提供了绑定和撤除绑定名字到某个对象的操作。JNDI使用通用的方式来暴露命名空间，即使用分层上下文以及使用相同命名语法的子上下文。 <br />
jndi的用途： <br />
1。你可以用jndi来得到object类的属性 <br />
如： <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://avaj.javaeye.com/blog/151736#"><img alt="复制代码" src="http://avaj.javaeye.com/images/icon_copy.gif" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>Attribute&nbsp;attr&nbsp;=directory.getAttributes(personName).get(</span><span class="string">"email"</span><span>);&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>String&nbsp;email&nbsp;=&nbsp;(String)attr.get();&nbsp;&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">Attribute attr =directory.getAttributes(personName).get("email");
String email = (String)attr.get(); </pre>
<br />
2。你可以用jndi来搜索对象 <br />
如： <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://avaj.javaeye.com/blog/151736#"><img alt="复制代码" src="http://avaj.javaeye.com/images/icon_copy.gif" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>foxes&nbsp;=&nbsp;directory.search(</span><span class="string">"o=Wiz,c=US"</span><span>,&nbsp;</span><span class="string">"sn=Fox"</span><span>,&nbsp;controls);&nbsp;&nbsp;&nbsp;</span></span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">foxes = directory.search("o=Wiz,c=US", "sn=Fox", controls); </pre>
<br />
查找谁的名字叫Fox在wiz部门的员工？ <br />
3。你可以用jndi通过naming/directory服务查询像printers和databases的对象 <br />
如：查询 Printer <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://avaj.javaeye.com/blog/151736#"><img alt="复制代码" src="http://avaj.javaeye.com/images/icon_copy.gif" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>Printer&nbsp;printer&nbsp;=&nbsp;(Printer)namespace.lookup(printerName);&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>printer.print(document);&nbsp;&nbsp;&nbsp;</span></li>
</ol>
</div>
<pre class="java" style="display: none" name="code">Printer printer = (Printer)namespace.lookup(printerName);
printer.print(document);
</pre>
<br />
4。你可以用jndi列表出命名空间的特殊级别的内容 <br />
如： <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://avaj.javaeye.com/blog/151736#"><img alt="复制代码" src="http://avaj.javaeye.com/images/icon_copy.gif" /></a></div>
</div>
<ol class="dp-j">
    <li><span><span>NamingEnumeration&nbsp;list&nbsp;=&nbsp;namespace.list(</span><span class="string">"o=Widget,&nbsp;c=US"</span><span>;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span class="keyword">while</span><span>&nbsp;(list.hasMore())&nbsp;{&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>NameClassPair&nbsp;entry&nbsp;=&nbsp;(NameClassPair)list.next();&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>display(entry.getName(),&nbsp;entry.getClassName());&nbsp; &nbsp;&nbsp;</span></li>
    <li><span>}&nbsp;&nbsp;&nbsp;</span></li>
</ol>
</div>
</div>
<img src ="http://www.blogjava.net/gm_jing/aggbug/265884.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2009-04-15 23:26 <a href="http://www.blogjava.net/gm_jing/articles/265884.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JMS基本概念（转）</title><link>http://www.blogjava.net/gm_jing/articles/240431.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Thu, 13 Nov 2008 17:53:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/240431.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/240431.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/240431.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/240431.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/240431.html</trackback:ping><description><![CDATA[<div class="postTitle">&nbsp;</div>
<h3><span style="color: #3064ff">1.JMS概述</span></h3>
JAVA 消息服务(JMS)定义了Java 中访问消息中间件的接口。JMS只是接口，并没有给予实现，实现JMS接口的消息中间件称为JMS Provider<br />
<br />
消息中间件提供企业数据的异步传输，通过消息中间件，一些原本互相孤立的业务组件可以组合成一个可靠的、灵活的系统。也可以模拟实现同步传输，但其实质仍然是异步的。<br />
<br />
<p>JMS 支持两种消息类型PTP （Point-to-Point）和Pub/Sub（Publish-Subscribe），分别称作：PTP Domain 和Pub/Sub Domain。<br />
PTP的主要特点是：<br />
1）一条消息只有一个使用者<br />
2）不存在时间限制<br />
Pub/Sub主要特点有：<br />
1）一条消息可以有多个使用者<br />
2）存在时间限制。订阅者可以使用持久方式来订阅消息，但是也会过期或者取消订阅<br />
</p>
<p>这两种接口都继承统一的JMS Parent 接口，JMS 主要接口如下所示：</p>
<div>
<table border="1">
    <colgroup>
    <col>
    <col>
    <col></colgroup>
    <tbody>
        <tr>
            <td><strong>JMS Parent </strong></td>
            <td><strong>PTPDomain</strong></td>
            <td><strong>Pub/Sub Domain </strong></td>
        </tr>
        <tr>
            <td>ConnectionFactory</td>
            <td>QueueConnectionFactory</td>
            <td>TopicConnectionFactory</td>
        </tr>
        <tr>
            <td>Connection</td>
            <td>QueueConnection</td>
            <td>TopicConnection</td>
        </tr>
        <tr>
            <td>Destination</td>
            <td>Queue</td>
            <td>Topic</td>
        </tr>
        <tr>
            <td>Session</td>
            <td>QueueSession</td>
            <td>TopicSession</td>
        </tr>
        <tr>
            <td>MessageProducer</td>
            <td>QueueSender</td>
            <td>TopicPublisher</td>
        </tr>
        <tr>
            <td>MessageConsumer</td>
            <td>QueueReceiver,QueueBrowser</td>
            <td>TopicSubscriber</td>
        </tr>
    </tbody>
</table>
</div>
<p>以下是对这些接口的简单描述：</p>
<p>ConnectionFactory ：连接工厂，JMS 用它创建连接</p>
<p>Connection ：JMS 客户端到JMS Provider 的连接</p>
<p>Destination ：消息的目的地</p>
<p>Session： 一个发送或接收消息的线程</p>
<p>MessageProducer： 由Session 对象创建的用来发送消息的对象</p>
<p>MessageConsumer： 由Session 对象创建的用来接收消息的对象</p>
<p><br />
</p>
<h3><span style="color: #3064ff">2.JMS消息模型</span></h3>
<p>JMS 消息由以下几部分组成：消息头，属性，消息体。</p>
<p><span><strong>消息头(Header) </strong></span>-消息头包含消息的识别信息和路由信息，消息头包含一些标准的属性如：JMSDestination,JMSMessageID等。</p>
<div>
<table border="1">
    <colgroup>
    <col>
    <col></colgroup>
    <tbody>
        <tr>
            <td><strong>消息头</strong></td>
            <td><strong>由谁设置</strong></td>
        </tr>
        <tr>
            <td>JMSDestination</td>
            <td>send 或 publish 方法</td>
        </tr>
        <tr>
            <td>JMSDeliveryMode</td>
            <td>send 或 publish 方法</td>
        </tr>
        <tr>
            <td>JMSExpiration</td>
            <td>send 或 publish 方法</td>
        </tr>
        <tr>
            <td>JMSPriority</td>
            <td>send 或 publish 方法</td>
        </tr>
        <tr>
            <td>JMSMessageID</td>
            <td>send 或 publish 方法</td>
        </tr>
        <tr>
            <td>JMSTimestamp</td>
            <td>send 或 publish 方法</td>
        </tr>
        <tr>
            <td>JMSCorrelationID</td>
            <td>客户</td>
        </tr>
        <tr>
            <td>JMSReplyTo</td>
            <td>客户</td>
        </tr>
        <tr>
            <td>JMSType</td>
            <td>客户</td>
        </tr>
        <tr>
            <td>JMSRedelivered</td>
            <td>JMS Provider</td>
        </tr>
    </tbody>
</table>
</div>
<p><strong>属性(Properties)</strong> -除了消息头中定义好的标准属性外，JMS提供一种机制增加新属性到消息头中，这种新属性包含以下几种：</p>
<div>
<ol type="1">
    <li>
    <p>应用需要用到的属性;</p>
    <li>
    <p>消息头中原有的一些可选属性;</p>
    <li>
    <p>JMS Provider 需要用到的属性。</p>
    </li>
</ol>
</div>
<p>标准的JMS 消息头包含以下属性：</p>
<p>JMSDestination --消息发送的目的地。</p>
<p>JMSDeliveryMode --传递模式，有两种模式：PERSISTENT和NON_PERSISTENT，PERSISTENT表示该消息一定要被送到目的地，否则会导致应用错误。 NON_PERSISTENT表示偶然丢失该消息是被允许的，这两种模式使开发者可以在消息传递的可靠性和吞吐量之间找到平衡点。</p>
<p>JMSMessageID 唯一识别每个消息的标识，由JMS Provider 产生。</p>
<p>JMSTimestamp 一个消息被提交给JMS Provider 到消息被发出的时间。</p>
<p>JMSCorrelationID 用来连接到另外一个消息，典型的应用是在回复消息中连接到原消息。</p>
<p>JMSReplyTo 提供本消息回复消息的目的地址。</p>
<p>JMSRedelivered如果一个客户端收到一个设置了JMSRedelivered属性的消息，则表示可能该客户端曾经在早些时候收到过该消息，但并没有签收(acknowledged)。</p>
<p>JMSType 消息类型的识别符。</p>
<p>JMSExpiration 消息过期时间，等于QueueSender 的send方法中的timeToLive值或TopicPublisher 的publish 方法中的timeToLive值加上发送时刻的GMT时间值。如果timeToLive值等于零，则JMSExpiration被设为零，表示该消息永不过期。如果发送后，在消息过期时间之后消息还没有被发送到目的地，则该消息被清除。</p>
<p>JMSPriority 消息优先级，从0-9 十个级别，0-4 是普通消息，5-9 是加急消息。JMS 不要求JMS Provider严格按照这十个优先级发送消息，但必须保证加急消息要先于普通消息到达。</p>
<p><strong>消息体(Body)</strong> - JMS API定义了5种消息体格式，也叫消息类型，你可以使用不同形式发送接收数据并可以兼容现有的消息格式，下面描述这5种类型：</p>
<div>
<table border="1">
    <colgroup>
    <col>
    <col></colgroup>
    <tbody>
        <tr>
            <td><strong>消息类型</strong></td>
            <td><strong>消息体</strong></td>
        </tr>
        <tr>
            <td>TextMessage</td>
            <td>java.lang.String对象，如xml文件内容</td>
        </tr>
        <tr>
            <td>MapMessage</td>
            <td>名/值对的集合，名是String对象，值类型可以是Java任何基本类型</td>
        </tr>
        <tr>
            <td>BytesMessage</td>
            <td>字节流</td>
        </tr>
        <tr>
            <td>StreamMessage</td>
            <td>Java中的输入输出流</td>
        </tr>
        <tr>
            <td>ObjectMessage</td>
            <td>Java中的可序列化对象</td>
        </tr>
        <tr>
            <td>Message</td>
            <td>没有消息体，只有消息头和属性</td>
        </tr>
    </tbody>
</table>
</div>
<p>Message是以上5类的基础。最常用的是ObjectMessage和TextMessage<br />
</p>
<h3><span style="color: #3064ff">3.消息的同步异步接收</span></h3>
<p><strong>消息的同步接收</strong></p>
<p>同步接收是指客户端主动去接收消息，JMS 客户端可以采用MessageConsumer的receive方法去接收下一个消息。</p>
<p><strong>消息的异步接收</strong></p>
<p>异步接收是指当消息到达时，主动通知客户端。JMS客户端可以通过注册一个实现MessageListener接口的对象到MessageConsumer，这样，每当消息到达时，JMS Provider 会调用MessageListener中的onMessage 方法。</p>
<h3><span style="color: #3064ff">4.PTP模型</span></h3>
<p>PTP(Point-to-Point)模型是基于队列的，发送方发消息到队列，接收方从队列接收消息，队列的存在使得消息的异步传输成为可能。和邮件系统中的邮箱一样，队列可以包含各种消息，JMS Provider 提供工具管理队列的创建、删除。JMS PTP模型定义了客户端如何向队列发送消息，从队列接收消息，浏览队列中的消息。</p>
<p>下面描述JMS PTP 模型中的主要概念和对象：</p>
<div>
<table border="1">
    <colgroup>
    <col>
    <col></colgroup>
    <tbody>
        <tr>
            <td><strong>名称</strong></td>
            <td><strong>描述</strong></td>
        </tr>
        <tr>
            <td>Queue</td>
            <td>由JMS Provider 管理，队列由队列名识别，客户端可以通过JNDI接口用队列名得到一个队列对象。</td>
        </tr>
        <tr>
            <td>TemporaryQueue</td>
            <td>由QueueConnection 创建，而且只能由创建它的QueueConnection 使用。</td>
        </tr>
        <tr>
            <td>QueueConnectionFactory</td>
            <td>客户端用QueueConnectionFactory 创建QueueConnection 对象。</td>
        </tr>
        <tr>
            <td>QueueConnection</td>
            <td>一个到JMS PTP provider 的连接，客户端可以用QueueConnection创建QueueSession来发送和接收消息。</td>
        </tr>
        <tr>
            <td>QueueSession</td>
            <td>提 供一些方法创建QueueReceiver、QueueSender、QueueBrowser和TemporaryQueue。如果在 QueueSession关闭时，有一些消息已经被收到，但还没有被签收(acknowledged)，那么，当接收者下次连接到相同的队列时，这些消息 还会被再次接收。</td>
        </tr>
        <tr>
            <td>QueueReceiver</td>
            <td>客户端用QueueReceiver接收队列中的消息，如果用户在QueueReceiver中设定了消息选择条件，那么不符合条件的消息会留在队列中，不会被接收到。</td>
        </tr>
        <tr>
            <td>QueueSender</td>
            <td>客户端用QueueSender 发送消息到队列。</td>
        </tr>
        <tr>
            <td>QueueBrowser</td>
            <td>客户端可以QueueBrowser 浏览队列中的消息，但不会收走消息。</td>
        </tr>
        <tr>
            <td>QueueRequestor</td>
            <td>JMS 提供QueueRequestor类简化消息的收发过程。QueueRequestor的构造函数有两个参数：QueueSession和queue，QueueRequestor 通过创建一个临时队列来完成最终的收发消息请求。</td>
        </tr>
        <tr>
            <td>可靠性(Reliability)</td>
            <td>队列可以长久地保存消息直到接收者收到消息。接收者不需要因为担心消息会丢失而时刻和队列保持激活的连接状态，充分体现了异步传输模式的优势。</td>
        </tr>
    </tbody>
</table>
</div>
<br />
<h3><span style="color: #3064ff">4.Pub/Sub模型</span></h3>
<p>JMS Pub/Sub 模型定义了如何向一个内容节点发布和订阅消息，这些节点被称作主题(topic)。</p>
<p>主题可以被认为是消息的传输中介，发布者(publisher)发布消息到主题，订阅者(subscribe)从主题订阅消息。主题使得消息订阅者和消息发布者保持互相独立，不需要接触即可保证消息的传送。</p>
<p>下面描述JMS Pub/Sub 模型中的主要概念和对象：</p>
<div>
<table border="1">
    <colgroup>
    <col>
    <col></colgroup>
    <tbody>
        <tr>
            <td><strong>名称 </strong></td>
            <td><strong>描述</strong></td>
        </tr>
        <tr>
            <td>订阅(subscription)</td>
            <td>消息订阅分为非持久订阅(non-durable subscription)和持久订阅(durablesubscrip-tion)，非持久订阅只有当客户端处于激活状态，也就是和JMS Provider保持连接状态才能收到发送到某个主题的消息，而当客户端处于离线状态，这个时间段发到主题的消息将会丢失，永远不会收到。持久订阅时，客户端向JMS注册一个识别自己身份的ID，当这个客户端处于离线时，JMS Provider 会为这个ID 保存所有发送到主题的消息，当客户再次连接到JMS Provider时，会根据自己的ID得到所有当自己处于离线时发送到主题的消息。</td>
        </tr>
        <tr>
            <td>Topic</td>
            <td>主题由JMS Provider 管理，主题由主题名识别，客户端可以通过JNDI接口用主题名得到一个主题对象。JMS没有给出主题的组织和层次结构的定义，由JMS Provider 自己定义。</td>
        </tr>
        <tr>
            <td>TemporaryTopic</td>
            <td>临时主题由TopicConnection创建，而且只能由创建它的TopicConnection使用。临时主题不能提供持久订阅功能。</td>
        </tr>
        <tr>
            <td>TopicConnectionFactory</td>
            <td>客户端用TopicConnectionFactory 创建TopicConnection 对象。</td>
        </tr>
        <tr>
            <td>TopicConnection</td>
            <td>TopicConnection 是一个到JMS Pub/Sub provider的连接，客户端可以用TopicConnection创建TopicSession来发布和订阅消息。</td>
        </tr>
        <tr>
            <td>TopicSession</td>
            <td>TopicSession提供一些方法创建TopicPublisher、TopicSubscriber、TemporaryTopic。它还提供unsubscribe方法取消消息的持久订阅。</td>
        </tr>
        <tr>
            <td>TopicPublisher</td>
            <td>客户端用TopicPublisher 发布消息到主题。</td>
        </tr>
        <tr>
            <td>TopicSubscriber</td>
            <td>客户端用TopicSubscriber接收发布到主题上的消息。可以在TopicSubscriber中设置消息过滤功能，这样，不符合要求的消息不会被接收。</td>
        </tr>
        <tr>
            <td>Durable TopicSubscriber</td>
            <td>如果一个客户端需要持久订阅消息，可以使用Durable TopicSubscriber，TopSession提供一个方法createDurableSubscriber创建Durable TopicSubscriber 对象。</td>
        </tr>
        <tr>
            <td>恢复和重新派送(Recovery and Redelivery)</td>
            <td>非持久订阅状态下，不能恢复或重新派送一个未签收的消息。只有持久订阅才能恢复或重新派送一个未签收的消息。</td>
        </tr>
        <tr>
            <td>TopicRequestor</td>
            <td>JMS 提供TopicRequestor类简化消息的收发过程。TopicRequestor的构造函数有两个参数：TopicSession和topic。TopicRequestor 通过创建一个临时主题来完成最终的发布和接收消息请求。</td>
        </tr>
        <tr>
            <td>可靠性(Reliability)</td>
            <td>当所有的消息必须被接收，则用持久订阅模式。当丢失消息能够被容忍，则用非持久订阅模</td>
        </tr>
    </tbody>
</table>
</div>
<p><br />
</p>
<br />
<img src ="http://www.blogjava.net/gm_jing/aggbug/240431.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2008-11-14 01:53 <a href="http://www.blogjava.net/gm_jing/articles/240431.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> servlet 中的 listener 的应用</title><link>http://www.blogjava.net/gm_jing/articles/211624.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Mon, 30 Jun 2008 03:32:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/211624.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/211624.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/211624.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/211624.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/211624.html</trackback:ping><description><![CDATA[<p align="left">&nbsp;</p>
<p><br />
Listener是Servlet的监听器，它可以监听客户端的请求、服务端的操作等。通过监听器，可以自动激发一些操作，比如监听在线的用户的数量。当增加一个HttpSession时，就激发sessionCreated(HttpSessionEvent se)方法，这样就可以给在线人数加1。常用的监听接口有以下几个：</p>
<p>ServletContextAttributeListener监听对ServletContext属性的操作，比如增加、删除、修改属性。</p>
<p>ServletContextListener监听ServletContext。当创建ServletContext时，激发contextInitialized(ServletContextEvent sce)方法；当销毁ServletContext时，激发contextDestroyed(ServletContextEvent sce)方法。</p>
<p>HttpSessionListener监听HttpSession的操作。当创建一个Session时，激发session Created(HttpSessionEvent se)方法；当销毁一个Session时，激发sessionDestroyed (HttpSessionEvent se)方法。</p>
<p>HttpSessionAttributeListener监听HttpSession中的属性的操作。当在Session增加一个属性时，激发attributeAdded(HttpSessionBindingEvent se) 方法；当在Session删除一个属性时，激发attributeRemoved(HttpSessionBindingEvent se)方法；当在Session属性被重新设置时，激发attributeReplaced(HttpSessionBindingEvent se) 方法。 </p>
<p>下面我们开发一个具体的例子，这个监听器能够统计在线的人数。在ServletContext初始化和销毁时，在服务器控制台打印对应的信息。当ServletContext里的属性增加、改变、删除时，在服务器控制台打印对应的信息。 </p>
<p>要获得以上的功能，监听器必须实现以下3个接口：</p>
<p>HttpSessionListener</p>
<p>ServletContextListener</p>
<p>ServletContextAttributeListener </p>
<p>我们看具体的代码，见示例14-9。 </p>
<p>【程序源代码】 </p>
<p>1&nbsp;// ==================== Program Discription =====================<br />
2&nbsp;// 程序名称：示例14-9 : EncodingFilter .java<br />
3&nbsp;// 程序目的：学习使用监听器<br />
4&nbsp;// ==============================================================<br />
5&nbsp;import javax.servlet.http.*;<br />
6&nbsp;import javax.servlet.*;<br />
7<br />
8&nbsp;public class OnLineCountListener implements HttpSessionListener,<br />
ServletContextListener,ServletContextAttributeListener<br />
9&nbsp;{<br />
10&nbsp;&nbsp;private int count;<br />
11&nbsp;&nbsp;private ServletContext context = null;<br />
12&nbsp;&nbsp;<br />
13&nbsp;&nbsp;public OnLineCountListener()<br />
14&nbsp;&nbsp;{<br />
15&nbsp;&nbsp;&nbsp;count=0;<br />
16&nbsp;&nbsp;&nbsp;//setContext();<br />
17&nbsp;&nbsp;}<br />
18&nbsp;&nbsp;//创建一个session时激发<br />
19&nbsp;&nbsp;public void sessionCreated(HttpSessionEvent se) <br />
20&nbsp;&nbsp;{<br />
21&nbsp;&nbsp;&nbsp;count++;<br />
22&nbsp;&nbsp;&nbsp;setContext(se);<br />
23&nbsp;&nbsp;&nbsp;<br />
24&nbsp;&nbsp;}<br />
25&nbsp;&nbsp;//当一个session失效时激发<br />
26&nbsp;&nbsp;public void sessionDestroyed(HttpSessionEvent se) <br />
27&nbsp;&nbsp;{<br />
28&nbsp;&nbsp;&nbsp;count--;<br />
29&nbsp;&nbsp;&nbsp;setContext(se);<br />
30&nbsp;&nbsp;}<br />
31&nbsp;&nbsp;//设置context的属性，它将激发attributeReplaced或attributeAdded方法<br />
32&nbsp;&nbsp;public void setContext(HttpSessionEvent se)<br />
33&nbsp;&nbsp;{<br />
34&nbsp;&nbsp;&nbsp;se.getSession().getServletContext().<br />
setAttribute("onLine",new Integer(count));<br />
35&nbsp;&nbsp;}<br />
36&nbsp;&nbsp; //增加一个新的属性时激发<br />
37&nbsp;&nbsp;public void attributeAdded(ServletContextAttributeEvent event) {<br />
38&nbsp;<br />
39&nbsp;&nbsp;log("attributeAdded('" + event.getName() + "', '" +<br />
40&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; event.getValue() + "')");<br />
41&nbsp;<br />
42&nbsp;&nbsp;&nbsp;&nbsp; }<br />
43&nbsp;&nbsp;&nbsp;&nbsp; <br />
44&nbsp;&nbsp;&nbsp; //删除一个新的属性时激发<br />
45&nbsp;&nbsp;&nbsp;&nbsp; public void attributeRemoved(ServletContextAttributeEvent event) {<br />
46<br />
47&nbsp;&nbsp;log("attributeRemoved('" + event.getName() + "', '" +<br />
48&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; event.getValue() + "')");<br />
49&nbsp;<br />
50&nbsp;&nbsp;&nbsp;&nbsp; }<br />
51<br />
52&nbsp;&nbsp;//属性被替代时激发<br />
53&nbsp;&nbsp;&nbsp;&nbsp; public void attributeReplaced(ServletContextAttributeEvent event) {<br />
54&nbsp;<br />
55&nbsp;&nbsp;&nbsp;log("attributeReplaced('" + event.getName() + "', '" +<br />
56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; event.getValue() + "')");<br />
57&nbsp;&nbsp;&nbsp;&nbsp; }<br />
58&nbsp;&nbsp;&nbsp;&nbsp; //context删除时激发<br />
59&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void contextDestroyed(ServletContextEvent event) {<br />
60&nbsp;<br />
61&nbsp;&nbsp;&nbsp;log("contextDestroyed()");<br />
62&nbsp;&nbsp;&nbsp;this.context = null;<br />
63&nbsp;<br />
64&nbsp;&nbsp;&nbsp;&nbsp; }<br />
65&nbsp;<br />
66&nbsp;&nbsp;&nbsp;&nbsp; //context初始化时激发<br />
67&nbsp;&nbsp;&nbsp;&nbsp; public void contextInitialized(ServletContextEvent event) {<br />
68&nbsp;<br />
69&nbsp;&nbsp;&nbsp;this.context = event.getServletContext();<br />
70&nbsp;&nbsp;&nbsp;log("contextInitialized()");<br />
71&nbsp;<br />
72&nbsp;&nbsp;&nbsp;&nbsp; }<br />
73&nbsp;&nbsp;&nbsp;&nbsp; private void log(String message) {<br />
74&nbsp;<br />
75&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("ContextListener: " + message);<br />
76&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br />
77&nbsp;}<br />
&nbsp;</p>
<p><br />
【程序注解】<br />
在OnLineCountListener里，用count代表当前在线的人数，OnLineCountListener将在Web服务器启动时自动执行。当OnLineCountListener构造好后，把count设置为0。每增加一个Session，OnLineCountListener会自动调用sessionCreated(HttpSessionEvent se)方法；每销毁一个Session，OnLineCountListener会自动调用sessionDestroyed(HttpSessionEvent se)方法。当调用sessionCreated(HttpSessionEvent se)方法时，说明又有一个客户在请求，此时使在线的人数（count）加1，并且把count写到ServletContext中。ServletContext的信息是所有客户端共享的，这样，每个客户端都可以读取到当前在线的人数。 <br />
&nbsp;</p>
<p>&nbsp;</p>
<p><br />
从作用域范围来说,Servlet的作用域有ServletContext,HttpSession,ServletRequest.</p>
<p>Context范围:</p>
<p>ServletContextListener:<br />
对一个应用进行全局监听.随应用启动而启动,随应用消失而消失主要有两个方法:</p>
<p>contextDestroyed(ServletContextEvent event) </p>
<p>在应用关闭的时候调用</p>
<p>contextInitialized(ServletContextEvent event) </p>
<p>在应用启动的时候调用</p>
<p>这个监听器主要用于一些随着应用启动而要完成的工作,也就是很多人说的我想在容器<br />
启动的时候干..........<br />
一般来说对"全局变量"初始化,如</p>
<p>public void contextInitialized(ServletContextEvent event){<br />
ServletContex sc = event.getServletContext();<br />
sc.setAttribute(name,value);<br />
} </p>
<p>以后你就可以在任何servlet中getServletContext().getAttribute(name);<br />
我最喜欢用它来做守护性工作,就是在contextInitialized(ServletContextEvent event) <br />
方法中实现一个Timer,然后就让应用在每次启动的时候让这个Timer工作:<br />
程序代码： <br />
public void contextInitialized(ServletContextEvent event){<br />
timer = new Timer();<br />
timer.schedule(new TimerTask(){<br />
public void run(){<br />
//do any things<br />
}<br />
},0,时间间隔);<br />
} </p>
<p><br />
有人说Timer只能规定从现在开始的多长时间后,每隔多久做一次事或在什么时间做<br />
一次事,那我想在每月1号或每天12点做一项工作如何做呢?<br />
你只要设一个间隔,然后每次判断一下当时是不是那个时间段就行了啊,比如每月一号做,那你<br />
时间间隔设为天,即24小时一个循环,然后在run方法中判断当时日期new Date().getDate()==1<br />
就行了啊.如果是每天的12点,那你时间间隔设为小时,然后在run中判断new Date().getHour()<br />
==12,再做某事就行了.</p>
<p>ServletContextAttributeListener:</p>
<p>这个监听器主要监听ServletContex对象在setAttribute()和removeAttribute()的事件,注意<br />
也就是一个"全局变量"在被Add(第一次set),replace(对已有的变量重新赋值)和remove的时候.<br />
分别调用下面三个方法:<br />
public void attributeAdded(ServletContextAttributeEvent scab)这个方法不仅可以知道<br />
哪些全局变量被加进来,而且可获取容器在启动时自动设置了哪些context变量:<br />
程序代码： <br />
public void attributeAdded(ServletContextAttributeEvent scab){<br />
System.out.println(scab.getName());<br />
}<br />
public void attributeRemoved(ServletContextAttributeEvent scab) </p>
<p>public void attributeReplaced(ServletContextAttributeEvent scab) </p>
<p><br />
Session范围:<br />
HttpSessionListener:<br />
这个监听器主要监听一个Session对象被生成和销毁时发生的事件.对应有两个方法:<br />
程序代码： <br />
public void sessionCreated(HttpSessionEvent se) </p>
<p>public void sessionDestroyed(HttpSessionEvent se) </p>
<p><br />
一般来说,一个session对象被create时,可以说明有一个新客端进入.可以用来粗略统计在线人<br />
数,注意这不是精确的,因为这个客户端可能立即就关闭了,但sessionDestroyed方法却会按一定<br />
的策略很久以后才会发生.</p>
<p>HttpSessionAttributeListener:<br />
和ServletContextAttributeListener一样,它监听一个session对象的Attribut被Add(一个特定<br />
名称的Attribute每一次被设置),replace(已有名称的Attribute的值被重设)和remove时的事件.<br />
对就的有三个方法.<br />
程序代码： <br />
public void attributeAdded(HttpSessionBindingEvent se) </p>
<p>public void attributeRemoved(HttpSessionBindingEvent se) </p>
<p>public void attributeReplaced(HttpSessionBindingEvent se) </p>
<p><br />
上面的几个监听器的方法,都是在监听应用逻辑中servlet逻辑中发生了什么事,一般的来说.<br />
我们只要完成逻辑功能,比如session.setAttribute("aaa","111");我只要把一个名为aaa的变量<br />
放在session中以便以后我能获取它,我并不关心当session.setAttribute("aaa","111");发生时<br />
我还要干什么.(当然有些时候要利用的),但对于下面这个监听器,你应该好好发解一下:</p>
<p>HttpSessionBindingListener:<br />
上面的监听器都是作为一个独立的Listener在容器中控制事件的.而HttpSessionBindingListener<br />
对在一对象中监听该对象的状态,实现了该接口的对象如果被作为value被add到一个session中或从<br />
session中remove,它就会知道自己已经作为一个session对象或已经从session删除,这对于一些非<br />
纯JAVA对象,生命周期长于session的对象,以及其它需要释放资源或改变状态的对象非常重要.<br />
比如:<br />
session.setAttribute("abcd","1111");<br />
以后session.removeAttribute("abcd");因为abcd是一个字符中,你从session中remove后,它就会<br />
自动被垃圾回收器回收,而如果是一个connection:(只是举例,你千万不要加connection往session<br />
中加入)<br />
程序代码： <br />
session.setAttribute("abcd",conn); </p>
<p>以后session.removeAttribute("abcd");这时这个conn被从session中remove了,你已经无法获取它<br />
的句柄,所以你根本没法关闭它.而在没有remove之前你根本不知道什么时候要被remove,你又无法<br />
close(),那么这个connection对象就死了.另外还有一些对象可以在被加入一个session时要锁定<br />
还要被remove时要解锁,应因你在程序中无法判断什么时候被remove(),add还好操作,我可以先加锁<br />
再add,但remove就后你就找不到它的句柄了,根本没法解锁,所以这些操作只能在对象自身中实现.<br />
也就是在对象被add时或remove时通知对象自己回调相应的方法:<br />
程序代码： <br />
MyConn extends Connection implements HttpSessionBindingListener{<br />
public void valueBound(HttpSessionBindingEvent se){<br />
this.initXXX();<br />
}<br />
public void valueUnbound(HttpSessionBindingEvent se){</p>
<p>this.close();<br />
}<br />
} </p>
<p><br />
session.setAttribute("aaa",new MyConn());<br />
这时如果调用session.removeAttribute("aaa"),则触发valueUnbound方法,就会自动关闭自己.<br />
而其它的需要改变状态的对象了是一样.</p>
<p align="left"><br />
&nbsp;</p>
<img src ="http://www.blogjava.net/gm_jing/aggbug/211624.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2008-06-30 11:32 <a href="http://www.blogjava.net/gm_jing/articles/211624.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>The Life Cycles of Enterprise Beans</title><link>http://www.blogjava.net/gm_jing/articles/202649.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Sat, 24 May 2008 13:51:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/202649.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/202649.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/202649.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/202649.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/202649.html</trackback:ping><description><![CDATA[<p>【引用】http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/EJBConcepts9.html<br />
</p>
<blockquote>
<h2><a name="74611"></a><font face="sans-serif" color="#666699">The Life Cycles of Enterprise Beans</font> </h2>
<p><a name="63423"></a><font size="+0">An enterprise bean goes through various stages during its lifetime, or life cycle. Each type of enterprise bean--session, entity, or message-driven--has a different life cycle. </font></p>
<p><a name="64697"></a><font size="+0">The descriptions that follow refer to methods that are explained along with the code examples in the next two chapters. If you are new to enterprise beans, you should skip this section and try out the code examples first.</font> </p>
<h3><a name="63448"></a><font face="sans-serif" color="#666699">The Life Cycle of a Stateful Session Bean </font></h3>
<p><a name="65435"></a><font size="+0"><a href="http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/EJBConcepts9.html#75397">Figure 3-3</a> illustrates the stages that a session bean passes through during its lifetime. The client initiates the life cycle by invoking the <code>create</code> method. The EJB container instantiates the bean and then invokes the <code>setSessionContext</code> and <code>ejbCreate</code> methods in the session bean. The bean is now ready to have its business methods invoked.</font> </p>
<p><a name="75395"></a><font size="+0">
<div align="left"><img height="315" hspace="0" src="http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/images/Fig10.gif" width="581" border="0"  alt="" /> </div>
</font>
<p>&nbsp;</p>
<p><a name="75397"></a><strong><font size="+0">Figure 3-3 Life Cycle of a Stateful Session Bean</font></strong> </p>
<p><a name="65087"></a><font size="+0">While in the ready stage, the EJB container may decide to deactivate, or <em>passivate</em>, the bean by moving it from memory to secondary storage. (Typically, the EJB container uses a least-recently-used algorithm to select a bean for passivation.) The EJB container invokes the bean's <code>ejbPassivate</code> method immediately before passivating it. If a client invokes a business method on the bean while it is in the passive stage, the EJB container activates the bean, moving it back to the ready stage, and then calls the bean's <code>ejbActivate</code> method.</font> </p>
<p><a name="65026"></a><font size="+0">At the end of the life cycle, the client invokes the <code>remove</code> method and the EJB container calls the bean's <code>ejbRemove</code> method. The bean's instance is ready for garbage collection. </font></p>
<p><a name="65027"></a><font size="+0">Your code controls the invocation of only two life-cycle methods--the <code>create</code> and <code>remove</code> methods in the client. All other methods in <a href="http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/EJBConcepts9.html#75397">Figure 3-3</a> are invoked by the EJB container. The <code>ejbCreate</code> method, for example, is inside the bean class, allowing you to perform certain operations right after the bean is instantiated. For instance, you may wish to connect to a database in the <code>ejbCreate</code> method. See Chapter&nbsp;<a href="http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/Resources.html#67831">16</a><a href="http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/Resources.html#74202"></a> for more information.</font> </p>
<h3><a name="64975"></a><font face="sans-serif" color="#666699">The Life Cycle of a Stateless Session Bean</font> </h3>
<p><a name="63469"></a><font size="+0">Because a stateless session bean is never passivated, its life cycle has just two stages: nonexistent and ready for the invocation of business methods. <a href="http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/EJBConcepts9.html#63478">Figure 3-4</a> illustrates the stages of a stateless session bean.</font> </p>
<p><a name="63476"></a><font size="+0">
<div align="left"><img height="366" hspace="0" src="http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/images/Fig11.gif" width="442" border="0"  alt="" /> </div>
</font>
<p>&nbsp;</p>
<p><a name="63478"></a><strong><font size="+0">Figure 3-4 Life Cycle of a Stateless Session Bean</font></strong> </p>
<h3><a name="63498"></a><font face="sans-serif" color="#666699">The Life Cycle of an Entity Bean</font> </h3>
<p><a name="63506"></a><font size="+0"><a href="http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/EJBConcepts9.html#79644">Figure 3-5</a> shows the stages that an entity bean passes through during its lifetime. After the EJB container creates the instance, it calls the <code>setEntityContext</code> method of the entity bean class. The <code>setEntityContext</code> method passes the entity context to the bean.</font> </p>
<p><a name="63507"></a><font size="+0">After instantiation, the entity bean moves to a pool of available instances. While in the pooled stage, the instance is not associated with any particular EJB object identity. All instances in the pool are identical. The EJB container assigns an identity to an instance when moving it to the ready stage.</font> </p>
<p><a name="63508"></a><font size="+0">There are two paths from the pooled stage to the ready stage. On the first path, the client invokes the <code>create</code> method, causing the EJB container to call the <code>ejbCreate</code> and <code>ejbPostCreate</code> methods. On the second path, the EJB container invokes the <code>ejbActivate</code> method. While in the ready stage, an entity bean's business methods may be invoked.</font> </p>
<p><a name="63509"></a><font size="+0">There are also two paths from the ready stage to the pooled stage. First, a client may invoke the <code>remove</code> method, which causes the EJB container to call the <code>ejbRemove</code> method. Second, the EJB container may invoke the <code>ejbPassivate</code> method.</font> </p>
<p><a name="79642"></a><font size="+0">
<div align="left"><img height="579" hspace="0" src="http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/images/Fig12.gif" width="476" border="0"  alt="" /> </div>
</font>
<p>&nbsp;</p>
<p><a name="79644"></a><strong><font size="+0">Figure 3-5 Life Cycle of an Entity Bean</font></strong> </p>
<p><a name="63510"></a><font size="+0">At the end of the life cycle, the EJB container removes the instance from the pool and invokes the <code>unsetEntityContext</code> method.</font> </p>
<p><a name="63517"></a><font size="+0">In the pooled state, an instance is not associated with any particular EJB object identity. With bean-managed persistence, when the EJB container moves an instance from the pooled state to the ready state, it does not automatically set the primary key. Therefore, the <code>ejbCreate</code> and <code>ejbActivate</code> methods must assign a value to the primary key. If the primary key is incorrect, the <code>ejbLoad</code> and <code>ejbStore</code> methods cannot synchronize the instance variables with the database. In the section <a href="http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/BMP2.html#62907">The SavingsAccountEJB Example</a>, the <code>ejbCreate</code> method assigns the primary key from one of the input parameters. The <code>ejbActivate</code> method sets the primary key (<code>id</code>) as follows:</font> </p>
<blockquote>
<pre>id = (String)context.getPrimaryKey();
<a name="63519"> </a>
</pre>
</blockquote>
<p><a name="63520"></a><font size="+0">In the pooled state, the values of the instance variables are not needed. You can make these instance variables eligible for garbage collection by setting them to <code>null</code> in the <code>ejbPasssivate</code> method.</font> </p>
<h3><a name="63945"></a><font face="sans-serif" color="#666699">The Life Cycle of a Message-Driven Bean</font> </h3>
<p><a name="75142"></a><font size="+0"><a href="http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/EJBConcepts9.html#75423">Figure 3-6</a> illustrates the stages in the life cycle of a message-driven bean.</font> </p>
<p><a name="74661"></a><font size="+0">The EJB container usually creates a pool of message-driven bean instances. For each instance, the EJB container instantiates the bean and performs these tasks:</font> </p>
<ol type="1"><font size="+0">
    <li value="1"><a name="74663"></a>It calls the <code>setMessageDrivenContext</code> method to pass the context object to the instance.</font> <font size="+0">
    <li value="2"><a name="74665"></a>It calls the instance's <code>ejbCreate</code> method.</font> </li>
</ol>
<p><a name="75421"></a><font size="+0">
<div align="left"><img height="395" hspace="0" src="http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/images/Fig13.gif" width="456" border="0"  alt="" /> </div>
</font>
<p>&nbsp;</p>
<p><a name="75423"></a><strong><font size="+0">Figure 3-6 Life Cycle of a Message-Driven Bean</font></strong> </p>
<p><a name="74675"></a><font size="+0">Like a stateless session bean, a message-driven bean is never passivated, and it has only two states: nonexistent and ready to receive messages. </font></p>
<p><a name="75125"></a><font size="+0">At the end of the life cycle, the container calls the <code>ejbRemove</code> method. The bean's instance is then ready for garbage collection.</font> </p>
</blockquote>
<img src ="http://www.blogjava.net/gm_jing/aggbug/202649.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2008-05-24 21:51 <a href="http://www.blogjava.net/gm_jing/articles/202649.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>学习LDAP</title><link>http://www.blogjava.net/gm_jing/articles/134896.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Tue, 07 Aug 2007 03:09:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/134896.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/134896.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/134896.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/134896.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/134896.html</trackback:ping><description><![CDATA[<div id=LastMDatecns!4131942a83ee8648!124>学习LDAP</div>
<div class=bvMsg id=msgcns!4131942a83ee8648!124>
<div>
<p><strong><span style="FONT-FAMILY: 宋体">原文：</span><span lang=EN-US><a href="http://ldapman.org/articles/intro_to_ldap.html"><u><font color=#0000ff>http://ldapman.org/articles/intro_to_ldap.html</font></u></a></span></strong>
<p><strong><span style="FONT-FAMILY: 宋体">原文作者：</span><span lang=EN-US><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#100;&#111;&#110;&#110;&#101;&#108;&#108;&#121;&#64;&#108;&#100;&#97;&#112;&#109;&#97;&#110;&#46;&#111;&#114;&#103;"><u><font color=#0000ff>Michael Donnelly</font></u></a></span></strong><span lang=EN-US> </span>
<p><strong><span style="FONT-FAMILY: 宋体">翻译：</span><span lang=EN-US><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#98;&#114;&#105;&#109;&#109;&#101;&#114;&#64;&#108;&#105;&#110;&#117;&#120;&#97;&#105;&#100;&#46;&#99;&#111;&#109;&#46;&#99;&#110;"><u><font color=#0000ff>Brimmer</font></u></a></span></strong>
<p><span style="FONT-FAMILY: 宋体">如果你在计算机行业工作，那么对</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">可能早有耳闻了。想深入地了解</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">吗？那么可以好好地读一下这篇文章。这篇介绍性的文章是一系列介绍如何在企业中设计、实现和集成</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">环境的文章的头一篇。主要是先让你熟悉一下</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">的基本概念，那些比较困难的细节问题将放到以后讨论。在这篇文章中我们将要介绍：</span>
<p><span lang=EN-US><font color=#0000ff><strong><span style="FONT-FAMILY: 宋体">什么是</span>LDAP?</strong></font></span>
<p><span lang=EN-US><font color=#0000ff><strong><span style="FONT-FAMILY: 宋体">什么时候该用</span>LDAP<span style="FONT-FAMILY: 宋体">存储数据？</span></strong></font></span>
<p><span lang=EN-US><font color=#0000ff><strong>LDAP<span style="FONT-FAMILY: 宋体">目录树的结构</span></strong></font></span>
<p><span lang=EN-US><font color=#0000ff><strong><span style="FONT-FAMILY: 宋体">单独的</span>LDAP<span style="FONT-FAMILY: 宋体">记录</span></strong></font></span>
<p><span lang=EN-US><span style="FONT-FAMILY: 宋体"><font color=#0000ff><strong>作为例子的一个单独的数据项</strong></font></span></span>
<p><span lang=EN-US><font color=#0000ff><strong>LDAP<span style="FONT-FAMILY: 宋体">复制</span></strong></font></span>
<p><span lang=EN-US><span style="FONT-FAMILY: 宋体"><font color=#0000ff><strong>安全和访问控制</strong></font></span></span>
<p><span style="FONT-FAMILY: 宋体">现在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">技术不仅发展得很快而且也是激动人心的。在企业范围内实现</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">可以让运行在几乎所有计算机平台上的所有的应用程序从</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录中获取信息。</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录中可以存储各种类型的数据：电子邮件地址、邮件路由信息、人力资源数据、公用密匙、联系人列表，等等。通过把</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录作为系统集成中的一个重要环节，可以简化员工在企业内部查询信息的步骤，甚至连主要的数据源都可以放在任何地方。如果</span><span lang=EN-US>Oracle</span><span style="FONT-FAMILY: 宋体">、</span><span lang=EN-US>Sybase</span><span style="FONT-FAMILY: 宋体">、</span><span lang=EN-US>Informix</span><span style="FONT-FAMILY: 宋体">或</span><span lang=EN-US>Microsoft SQL</span><span style="FONT-FAMILY: 宋体">数据库中已经存储了类似的数据，那么</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">和这些数据库到底有什么不同呢？是什么让它更具优势？请继续读下去吧！</span>
<h2><a></a><span style="FONT-FAMILY: 黑体">什么是</span><span lang=EN-US>LDAP?</span></h2>
<p><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">的英文全称是</span><span lang=EN-US style="COLOR: black">Lightweight Directory Access Protocol</span><span style="COLOR: black; FONT-FAMILY: 宋体">，一般都简称为</span><span lang=EN-US style="COLOR: black">LDAP</span><span style="COLOR: black; FONT-FAMILY: 宋体">。它是基于</span><span lang=EN-US style="COLOR: black">X.500</span><span style="COLOR: black; FONT-FAMILY: 宋体">标准的，但是简单多了并且可以根据需要定制。与</span><span lang=EN-US style="COLOR: black">X.500</span><span style="COLOR: black; FONT-FAMILY: 宋体">不同，</span><span lang=EN-US style="COLOR: black">LDAP</span><span style="COLOR: black; FONT-FAMILY: 宋体">支持</span><span lang=EN-US style="COLOR: black">TCP/IP</span><span style="COLOR: black; FONT-FAMILY: 宋体">，这对访问</span><span lang=EN-US style="COLOR: black">Internet</span><span style="COLOR: black; FONT-FAMILY: 宋体">是必须的。</span><span lang=EN-US style="COLOR: black">LDAP</span><span style="COLOR: black; FONT-FAMILY: 宋体">的核心规范在</span><span lang=EN-US style="COLOR: black">RFC</span><span style="COLOR: black; FONT-FAMILY: 宋体">中都有定义，所有与</span><span lang=EN-US style="COLOR: black">LDAP</span><span style="COLOR: black; FONT-FAMILY: 宋体">相关的</span><span lang=EN-US style="COLOR: black">RFC</span><span style="COLOR: black; FONT-FAMILY: 宋体">都可以在</span><span lang=EN-US style="COLOR: black"><a href="http://www.ldapman.org/ldap_rfcs.html"><u><font color=#0000ff>LDAPman RFC</font></u></a></span><span style="COLOR: black; FONT-FAMILY: 宋体">网页中找到。</span>
<h3><span style="FONT-FAMILY: 宋体">怎么使用</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">这个术语呢？</span></h3>
<p><span style="FONT-FAMILY: 宋体">在日常交谈中，你可能会听到有些人这么说：&#8220;我们要把那些东西存在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">中吗？&#8221;，或者&#8220;从</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">数据库中取出那些数据！&#8221;，又或者&#8220;我们怎么把</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">和关系型数据库集成在一起？&#8221;。严格地说，</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">根本不是数据库而是用来访问存储在信息目录（也就是</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录）中的信息的<strong>协议</strong>。更为确切和正式的说法应该是象这样的：&#8220;通过使用</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">，可以在信息目录的正确位置读取（或存储）数据&#8221;。但是，也没有必要吹毛求疵，尽管表达得不够准确，我们也都知道对方在说什么。</span>
<h3><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录是数据库吗？</span></h3>
<p><span style="FONT-FAMILY: 宋体">就象</span><span lang=EN-US>Sybase</span><span style="FONT-FAMILY: 宋体">、</span><span lang=EN-US>Oracle</span><span style="FONT-FAMILY: 宋体">、</span><span lang=EN-US>Informix</span><span style="FONT-FAMILY: 宋体">或</span><span lang=EN-US>Microsoft</span><span style="FONT-FAMILY: 宋体">的数据库管理系统（</span><span lang=EN-US>DBMS</span><span style="FONT-FAMILY: 宋体">）是用于处理查询和更新关系型数据库那样，</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器也是用来处理查询和更新</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录的。换句话来说</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录也是<strong>一种类型</strong>的数据库，但是不是关系型数据库。不象被设计成每分钟需要处理成百上千条数据变化的数据库，例如：在电子商务中经常用到的在线交易处理（</span><span lang=EN-US>OLTP</span><span style="FONT-FAMILY: 宋体">）系统，</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">主要是优化数据读取的性能。</span>
<h3><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录的优势</span></h3>
<p><span style="FONT-FAMILY: 宋体">现在该说说</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录到底有些什么优势了。现在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">的流行是很多因数共同作用的结果。我在这里说的不过是一些基本的原因，请你注意一下这不过是一小部分原因。</span>
<p><span style="FONT-FAMILY: 宋体">可能</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">最大的优势是：可以在任何计算机平台上，用很容易获得的而且数目不断增加的</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">的客户端程序访问</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录。而且也很容易定制应用程序为它加上</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">的支持。</span>
<p><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">协议是跨平台的和标准的协议，因此应用程序就不用为</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录放在什么样的服务器上操心了。实际上，</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">得到了业界的广泛认可，因为它是</span><span lang=EN-US>Internet</span><span style="FONT-FAMILY: 宋体">的标准。产商都很愿意在产品中加入对</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">的支持，因为他们根本不用考虑另一端（客户端或服务端）是怎么样的。</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器可以是任何一个开发源代码或商用的</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录服务器（或者还可能是具有</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">界面的关系型数据库），因为可以用同样的协议、客户端连接软件包和查询命令与</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器进行交互。与</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">不同的是，如果软件产商想在软件产品中集成对</span><span lang=EN-US>DBMS</span><span style="FONT-FAMILY: 宋体">的支持，那么通常都要对每一个数据库服务器单独定制。</span>
<p><span style="FONT-FAMILY: 宋体">不象很多商用的关系型数据库，你不必为</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">的每一个客户端连接或许可协议付费。</span>
<p><span style="FONT-FAMILY: 宋体">大多数的</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器安装起来很简单，也容易维护和优化。</span>
<p><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器可以用&#8220;推&#8221;或&#8220;拉&#8221;的方法复制部分或全部数据，例如：可以把数据&#8220;推&#8221;到远程的办公室，以增加数据的安全性。复制技术是内置在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器中的而且很容易配置。如果要在</span><span lang=EN-US>DBMS</span><span style="FONT-FAMILY: 宋体">中使用相同的复制功能，数据库产商就会要你支付额外的费用，而且也很难管理。</span>
<p><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">允许你根据需要使用</span><span lang=EN-US>ACI</span><span style="FONT-FAMILY: 宋体">（一般都称为</span><span lang=EN-US>ACL</span><span style="FONT-FAMILY: 宋体">或者访问控制列表）控制对数据读和写的权限。例如，设备管理员可以有权改变员工的工作地点和办公室号码，但是不允许改变记录中其它的域。</span><span lang=EN-US>ACI</span><span style="FONT-FAMILY: 宋体">可以根据谁访问数据、访问什么数据、数据存在什么地方以及其它对数据进行访问控制。因为这些都是由</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录服务器完成的，所以不用担心在客户端的应用程序上是否要进行安全检查。</span>
<p><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">对于这样存储这样的信息最为有用，也就是数据需要从不同的地点读取，但是不需要经常更新。例如，这些信息存储在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录中是十分有效的：</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">公司员工的电话号码簿和组织结构图</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">客户的联系信息</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">计算机管理需要的信息，包括</span><span lang=EN-US>NIS</span><span style="FONT-FAMILY: 宋体">映射、</span><span lang=EN-US>email</span><span style="FONT-FAMILY: 宋体">假名，等等</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">软件包的配置信息</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">公用证书和安全密匙</span>
<h2><a></a><a></a><span style="FONT-FAMILY: 黑体">什么时候该用</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 黑体">存储数据？</span></h2>
<p><span style="COLOR: black; FONT-FAMILY: 宋体">大多数的</span><span lang=EN-US style="COLOR: black">LDAP</span><span style="COLOR: black; FONT-FAMILY: 宋体">服务器都为读密集型的操作进行专门的优化。因此，当从</span><span lang=EN-US style="COLOR: black">LDAP</span><span style="COLOR: black; FONT-FAMILY: 宋体">服务器中读取数据的时候会比从专门为</span><span lang=EN-US style="COLOR: black">OLTP</span><span style="COLOR: black; FONT-FAMILY: 宋体">优化的关系型数据库中读取数据快一个数量级。也是因为专门为读的性能进行优化，大多数的</span><span lang=EN-US style="COLOR: black">LDAP</span><span style="COLOR: black; FONT-FAMILY: 宋体">目录服务器并不适合存储需要需要经常改变的数据。例如，用</span><span lang=EN-US style="COLOR: black">LDAP</span><span style="COLOR: black; FONT-FAMILY: 宋体">服务器来存储电话号码是一个很好的选择，但是它不能作为电子商务站点的数据库服务器。</span><span lang=EN-US style="COLOR: black"></span>
<p><span style="COLOR: black; FONT-FAMILY: 宋体">如果下面每一个问题的答案都是&#8220;是&#8221;，那么把数据存在</span><span lang=EN-US style="COLOR: black">LDAP</span><span style="COLOR: black; FONT-FAMILY: 宋体">中就是一个好主意。</span><span lang=EN-US style="COLOR: black"></span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">需要在任何平台上都能读取数据吗？</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">每一个单独的记录项是不是每一天都只有很少的改变？</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">可以把数据存在平面数据库（</span><span lang=EN-US>flat database</span><span style="FONT-FAMILY: 宋体">）而不是关系型数据库中吗？换句话来说，也就是不管什么范式不范式的，把所有东西都存在一个记录中（差不多只要满足第一范式）。</span>
<p><span style="FONT-FAMILY: 宋体">最后一个问题可能会唬住一些人，其实用平面数据库去存储一些关系型的数据也是很一般的。例如，一条公司员工的记录就可以包含经理的登录名。用</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">来存储这类信息是很方便的。一个简单的判断方法：如果可以把保数据存在一张张的卡片里，就可以很容易地把它存在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录里。</span>
<h2><a></a><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 黑体">目录树的结构</span></h2>
<p><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录以树状的层次结构来存储数据。如果你对自顶向下的</span><span lang=EN-US>DNS</span><span style="FONT-FAMILY: 宋体">树或</span><span lang=EN-US>UNIX</span><span style="FONT-FAMILY: 宋体">文件的目录树比较熟悉，也就很容易掌握</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录树这个概念了。就象</span><span lang=EN-US>DNS</span><span style="FONT-FAMILY: 宋体">的主机名那样，</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录记录的标识名（</span><span lang=EN-US>Distinguished Name</span><span style="FONT-FAMILY: 宋体">，简称</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">）是用来读取单个记录，以及回溯到树的顶部。后面会做详细地介绍。</span>
<p><span style="FONT-FAMILY: 宋体">为什么要用层次结构来组织数据呢？原因是多方面的。下面是可能遇到的一些情况：</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">如果你想把所有的美国客户的联系信息都&#8220;推&#8221;到位于到西雅图办公室（负责营销）的</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器上，但是你不想把公司的资产管理信息&#8220;推&#8221;到那里。</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">你可能想根据目录树的结构给予不同的员工组不同的权限。在下面的例子里，资产管理组对&#8220;</span><span lang=EN-US>asset-mgmt</span><span style="FONT-FAMILY: 宋体">&#8221;部分有完全的访问权限，但是不能访问其它地方。</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">把</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">存储和复制功能结合起来，可以定制目录树的结构以降低对</span><span lang=EN-US>WAN</span><span style="FONT-FAMILY: 宋体">带宽的要求。位于西雅图的营销办公室需要每分钟更新的美国销售状况的信息，但是欧洲的销售情况就只要每小时更新一次就行了。</span>
<h3><span style="FONT-FAMILY: 宋体">刨根问底：基准</span><span lang=EN-US>DN</span></h3>
<p><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录树的最顶部就是根，也就是所谓的&#8220;基准</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">&#8221;。基准</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">通常使用下面列出的三种格式之一。假定我在名为</span><span lang=EN-US>FooBar</span><span style="FONT-FAMILY: 宋体">的电子商务公司工作，这家公司在</span><span lang=EN-US>Internet</span><span style="FONT-FAMILY: 宋体">上的名字是</span><span lang=EN-US>foobar.com</span><span style="FONT-FAMILY: 宋体">。</span>
<p><strong><span lang=EN-US>o="FooBar, Inc.", c=US </span></strong>
<p><em><span style="COLOR: black; FONT-FAMILY: 宋体">（以</span><span lang=EN-US style="COLOR: black">X.500</span></em><em><span style="COLOR: black; FONT-FAMILY: 宋体">格式表示的基准</span><span lang=EN-US style="COLOR: black">DN</span></em><em><span style="COLOR: black; FONT-FAMILY: 宋体">）</span><span lang=EN-US style="COLOR: black"></span></em>
<p><span style="FONT-FAMILY: 宋体">在这个例子中，</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">o=FooBar, Inc. </span><span style="FONT-FAMILY: 宋体">表示组织名，在这里就是公司名的同义词。</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">c=US </span><span style="FONT-FAMILY: 宋体">表示公司的总部在美国。以前，一般都用这种方式来表示基准</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">。但是事物总是在不断变化的，现在所有的公司都已经（或计划）上</span><span lang=EN-US>Internet</span><span style="FONT-FAMILY: 宋体">上。随着</span><span lang=EN-US>Internet</span><span style="FONT-FAMILY: 宋体">的全球化，在基准</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">中使用国家代码很容易让人产生混淆。现在，</span><span lang=EN-US>X.500</span><span style="FONT-FAMILY: 宋体">格式发展成下面列出的两种格式。</span>
<p><strong><span lang=EN-US>o=foobar.com</span></strong>
<p><em><span style="FONT-FAMILY: 宋体">（用公司的</span><span lang=EN-US>Internet</span></em><em><span style="FONT-FAMILY: 宋体">地址表示的基准</span><span lang=EN-US>DN</span></em><em><span style="FONT-FAMILY: 宋体">）</span><span lang=EN-US></span></em>
<p><span style="FONT-FAMILY: 宋体">这种格式很直观，用公司的域名作为基准</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">。这也是现在最常用的格式。</span>
<p><strong><span lang=EN-US>dc=foobar, dc=com</span></strong>
<p><em><span style="FONT-FAMILY: 宋体">（用</span><span lang=EN-US>DNS</span></em><em><span style="FONT-FAMILY: 宋体">域名的不同部分组成的基准</span><span lang=EN-US>DN</span></em><em><span style="FONT-FAMILY: 宋体">）</span><span lang=EN-US></span></em>
<p><span style="FONT-FAMILY: 宋体">就象上面那一种格式，这种格式也是以</span><span lang=EN-US>DNS</span><span style="FONT-FAMILY: 宋体">域名为基础的，但是上面那种格式不改变域名（也就更易读），而这种格式把域名：</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">foobar.com</span><span style="FONT-FAMILY: 宋体">分成两部分</span> <span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">dc=foobar, dc=com</span><span style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 黑体">。</span><span style="FONT-FAMILY: 宋体">在理论上，这种格式可能会更灵活一点，但是对于最终用户来说也更难记忆一点。考虑一下</span><span lang=EN-US>foobar.com</span><span style="FONT-FAMILY: 宋体">这个例子。当</span><span lang=EN-US>foobar.com</span><span style="FONT-FAMILY: 宋体">和</span><span lang=EN-US>gizmo.com</span><span style="FONT-FAMILY: 宋体">合并之后，可以简单的把&#8220;</span><span lang=EN-US>dc=com</span><span style="FONT-FAMILY: 宋体">&#8221;当作基准</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">。把新的记录放到已经存在的</span><span lang=EN-US>dc=gizmo, dc=com</span><span style="FONT-FAMILY: 宋体">目录下，这样就简化了很多工作（当然，如果</span><span lang=EN-US>foobar.com</span><span style="FONT-FAMILY: 宋体">和</span><span lang=EN-US>wocket.edu</span><span style="FONT-FAMILY: 宋体">合并，这个方法就不能用了）。如果</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器是新安装的，我建议你使用这种格式。再请注意一下，如果你打算使用活动目录（</span><span lang=EN-US>Actrive Directory</span><span style="FONT-FAMILY: 宋体">），</span><span lang=EN-US>Microsoft</span><span style="FONT-FAMILY: 宋体">已经限制你必须使用这种格式。</span>
<h3><span style="FONT-FAMILY: 宋体">更上一层楼：在目录树中怎么组织数据</span></h3>
<p><span style="FONT-FAMILY: 宋体">在</span><span lang=EN-US>UNIX</span><span style="FONT-FAMILY: 宋体">文件系统中，最顶层是根目录（</span><span lang=EN-US>root</span><span style="FONT-FAMILY: 宋体">）。在根目录的下面有很多的文件和目录。象上面介绍的那样，</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录也是用同样的方法组织起来的。</span>
<p><span style="FONT-FAMILY: 宋体">在根目录下，要把数据从逻辑上区分开。因为历史上（</span><span lang=EN-US>X.500</span><span style="FONT-FAMILY: 宋体">）的原因，大多数</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录用</span><span lang=EN-US>OU</span><span style="FONT-FAMILY: 宋体">从逻辑上把数据分开来。</span><span lang=EN-US>OU</span><span style="FONT-FAMILY: 宋体">表示&#8220;</span><span lang=EN-US>Organization Unit</span><span style="FONT-FAMILY: 宋体">&#8221;，在</span><span lang=EN-US>X.500</span><span style="FONT-FAMILY: 宋体">协议中是用来表示公司内部的机构：销售部、财务部，等等。现在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">还保留</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">ou=</span><span style="FONT-FAMILY: 宋体">这样的命名规则，但是扩展了分类的范围，可以分类为：</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">ou=people, ou=groups, ou=devices</span><span style="COLOR: black; FONT-FAMILY: 宋体">，等等。更低一级的</span><span lang=EN-US style="COLOR: black">OU</span><span style="COLOR: black; FONT-FAMILY: 宋体">有时用来做更细的归类。例如：</span><span lang=EN-US style="COLOR: black">LDAP</span><span style="COLOR: black; FONT-FAMILY: 宋体">目录树（不包括单独的记录）可能会是这样的：</span><span lang=EN-US style="COLOR: black"></span>
<p><span>&nbsp;&nbsp;&nbsp; </span>dc=foobar, dc=com
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ou=customers
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ou=asia
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ou=europe
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ou=usa
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ou=employees
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ou=rooms
<p><span>&nbsp;&nbsp; </span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ou=groups
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ou=assets-mgmt
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ou=nisgroups
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ou=recipes
<h2><a></a><span style="FONT-FAMILY: 黑体">单独的</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 黑体">记录</span></h2>
<h3><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">是</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">记录项的名字</span></h3>
<p><span style="FONT-FAMILY: 宋体">在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录中的所有记录项都有一个唯一的&#8220;</span><span lang=EN-US>Distinguished Name</span><span style="FONT-FAMILY: 宋体">&#8221;，也就是</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">。每一个</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">记录项的</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">是由两个部分组成的：相对</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">（</span><span lang=EN-US>RDN</span><span style="FONT-FAMILY: 宋体">）和记录在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录中的位置。</span>
<p><span lang=EN-US>RDN</span><span style="FONT-FAMILY: 宋体">是</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">中与目录树的结构无关的部分。在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录中存储的记录项都要有一个名字，这个名字通常存在</span><span lang=EN-US>cn</span><span style="FONT-FAMILY: 宋体">（</span><span lang=EN-US>Common Name</span><span style="FONT-FAMILY: 宋体">）这个属性里。因为几乎所有的东西都有一个名字，在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">中存储的对象都用它们的</span><span lang=EN-US>cn</span><span style="FONT-FAMILY: 宋体">值作为</span><span lang=EN-US>RDN</span><span style="FONT-FAMILY: 宋体">的基础。如果我把最喜欢的吃燕麦粥食谱存为一个记录，我就会用</span><strong><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">cn=Oatmeal Deluxe</span></strong><span style="FONT-FAMILY: 宋体">作为记录项的</span><span lang=EN-US>RDN</span><span style="FONT-FAMILY: 宋体">。</span>
<p><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">我的</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录的基准</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">是</span><strong><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">dc=foobar,dc=com</span></strong>
<p><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">我把自己的食谱作为</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">的记录项存在</span><strong><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">ou=recipes</span></strong>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">我的</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">记录项的</span><span lang=EN-US>RDN</span><span style="FONT-FAMILY: 宋体">设为</span><strong><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">cn=Oatmeal Deluxe</span></strong>
<p><span style="FONT-FAMILY: 宋体">上面这些构成了燕麦粥食谱的</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">记录的完整</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">。记住，</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">的读法和</span><span lang=EN-US>DNS</span><span style="FONT-FAMILY: 宋体">主机名类似。下面就是完整的</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">：</span>
<p><strong><span lang=EN-US>cn=Oatmeal Deluxe,ou=recipes,dc=foobar,dc=com</span></strong>
<h3><span style="FONT-FAMILY: 宋体">举一个实际的例子来说明</span><span lang=EN-US>DN</span></h3>
<p><span style="FONT-FAMILY: 宋体">现在为公司的员工设置一个</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">。可以用基于</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">cn</span><span style="FONT-FAMILY: 宋体">或</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">uid</span><span style="FONT-FAMILY: 宋体">（</span><span lang=EN-US>User ID</span><span style="FONT-FAMILY: 宋体">），作为典型的用户帐号。例如，</span><span lang=EN-US>FooBar</span><span style="FONT-FAMILY: 宋体">的员工</span><span lang=EN-US>Fran Smith</span><span style="FONT-FAMILY: 宋体">（登录名：</span><span lang=EN-US>fsmith</span><span style="FONT-FAMILY: 宋体">）的</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">可以为下面两种格式：</span>
<p><strong><span lang=EN-US>uid=fsmith,ou=employees,dc=foobar,dc=com</span></strong>
<p><em><span style="FONT-FAMILY: 宋体">（基于登录名）</span><span lang=EN-US></span></em>
<p><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">（以及</span><span lang=EN-US>X.500</span><span style="FONT-FAMILY: 宋体">）用</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">uid</span><span style="FONT-FAMILY: 宋体">表示&#8220;</span><span lang=EN-US>User ID</span><span style="FONT-FAMILY: 宋体">&#8221;，不要把它和</span><span lang=EN-US>UNIX</span><span style="FONT-FAMILY: 宋体">的</span><span lang=EN-US>uid</span><span style="FONT-FAMILY: 宋体">号混淆了。大多数公司都会给每一个员工唯一的登录名，因此用这个办法可以很好地保存员工的信息。你不用担心以后还会有一个叫</span><span lang=EN-US>Fran Smith</span><span style="FONT-FAMILY: 宋体">的加入公司，如果</span><span lang=EN-US>Fran</span><span style="FONT-FAMILY: 宋体">改变了她的名字（结婚？离婚？或宗教原因？），也用不着改变</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">记录项的</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">。</span>
<p><strong><span lang=EN-US>cn=Fran Smith,ou=employees,dc=foobar,dc=com</span></strong>
<p><em><span style="FONT-FAMILY: 宋体">（基于姓名）</span><span lang=EN-US></span></em>
<p><span style="FONT-FAMILY: 宋体">可以看到这种格式使用了</span><span lang=EN-US>Common Name</span><span style="FONT-FAMILY: 宋体">（</span><span lang=EN-US>CN</span><span style="FONT-FAMILY: 宋体">）。可以把</span><span lang=EN-US>Common Name</span><span style="FONT-FAMILY: 宋体">当成一个人的全名。这种格式有一个很明显的缺点就是：如果名字改变了，</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">的记录就要从一个</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">转移到另一个</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">。但是，我们应该尽可能地避免改变一个记录项的</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">。</span>
<h2><span style="FONT-FAMILY: 黑体">定制目录的对象类型</span></h2>
<p><span style="FONT-FAMILY: 宋体">你可以用</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">存储各种类型的数据对象，只要这些对象可以用属性来表示，下面这些是可以在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">中存储的一些信息：</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">员工信息：员工的姓名、登录名、口令、员工号、他的经理的登录名，邮件服务器，等等。</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">物品跟踪信息：计算机名、</span><span lang=EN-US>IP</span><span style="FONT-FAMILY: 宋体">地址、标签、型号、所在位置，等等。</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">客户联系列表：客户的公司名、主要联系人的电话、传真和电子邮件，等等。</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">会议厅信息：会议厅的名字、位置、可以坐多少人、电话号码、是否有投影机。</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">食谱信息：菜的名字、配料、烹调方法以及准备方法。</span>
<p><span style="FONT-FAMILY: 宋体">因为</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录可以定制成存储任何文本或二进制数据，到底存什么要由你自己决定。</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录用对象类型（</span><span lang=EN-US>object classes</span><span style="FONT-FAMILY: 宋体">）的概念来定义运行哪一类的对象使用什么属性。在几乎所有的</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器中，你都要根据自己的需要扩展基本的</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录的功能，创建新的对象类型或者扩展现存的对象类型。</span>
<p><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录以一系列&#8220;属性对&#8221;的形式来存储记录项，每一个记录项包括属性类型和属性值（这与关系型数据库用行和列来存取数据有根本的不同）。下面是我存在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录中的一部分食谱记录：</span>
<p><span>&nbsp; </span>dn: cn=Oatmeal Deluxe, ou=recipes, dc=foobar, dc=com
<p><span>&nbsp; </span>cn: Instant Oatmeal Deluxe
<p><span>&nbsp; </span>recipeCuisine: breakfast
<p><span>&nbsp; </span>recipeIngredient: 1 packet instant oatmeal
<p><span>&nbsp; </span>recipeIngredient: 1 cup water
<p><span>&nbsp; </span>recipeIngredient: 1 pinch salt
<p><span>&nbsp; </span>recipeIngredient: 1 tsp brown sugar
<p><span>&nbsp; </span>recipeIngredient: 1/4 apple, any type
<p><span style="FONT-FAMILY: 宋体">请注意上面每一种配料都作为属性</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">recipeIngredient</span><span style="FONT-FAMILY: 宋体">值。</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录被设计成象上面那样为一个属性保存多个值的，而不是在每一个属性的后面用逗号把一系列值分开。</span>
<p><span style="FONT-FAMILY: 宋体">因为用这样的方式存储数据，所以数据库就有很大的灵活性，不必为加入一些新的数据就重新创建表和索引。更重要的是，</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录不必花费内存或硬盘空间处理&#8220;空&#8221;域，也就是说，实际上不使用可选择的域也不会花费你任何资源。</span>
<h2><a></a><span style="FONT-FAMILY: 黑体">作为例子的一个单独的数据项</span></h2>
<p><span style="FONT-FAMILY: 宋体">让我们看看下面这个例子。我们用</span><span lang=EN-US>Foobar, Inc.</span><span style="FONT-FAMILY: 宋体">的员工</span><span lang=EN-US>Fran Smith</span><span style="FONT-FAMILY: 宋体">的</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">记录。这个记录项的格式是</span><span lang=EN-US>LDIF</span><span style="FONT-FAMILY: 宋体">，用来导入和导出</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录的记录项。</span>
<p><span>&nbsp; </span>dn: uid=fsmith, ou=employees, dc=foobar, dc=com
<p><span>&nbsp; </span>objectclass: person
<p><span>&nbsp; </span>objectclass: organizationalPerson
<p><span>&nbsp; </span>objectclass: inetOrgPerson
<p><span>&nbsp; </span>objectclass: foobarPerson
<p><span>&nbsp; </span>uid: fsmith
<p><span>&nbsp; </span>givenname: Fran
<p><span>&nbsp; </span>sn: Smith
<p><span>&nbsp; </span>cn: Fran Smith
<p><span>&nbsp;</span>&nbsp;cn: Frances Smith
<p><span>&nbsp; </span>telephonenumber: 510-555-1234
<p><span>&nbsp; </span>roomnumber: 122G
<p><span>&nbsp; </span>o: Foobar, Inc.
<p><span>&nbsp; </span>mailRoutingAddress: fsmith@foobar.com
<p><span>&nbsp; </span>mailhost: mail.foobar.com
<p><span>&nbsp; </span>userpassword: {crypt}3x1231v76T89N
<p><span>&nbsp; </span>uidnumber: 1234
<p><span>&nbsp; </span>gidnumber: 1200
<p><span>&nbsp; </span>homedirectory: /home/fsmith
<p><span>&nbsp; </span>loginshell: /usr/local/bin/bash
<p><span style="FONT-FAMILY: 宋体">属性的值在保存的时候是保留大小写的，但是在默认情况下搜索的时候是不区分大小写的。某些特殊的属性（例如，</span><span lang=EN-US>password</span><span style="FONT-FAMILY: 宋体">）在搜索的时候需要区分大小写。</span>
<p><span style="FONT-FAMILY: 宋体">让我们一点一点地分析上面的记录项。</span>
<p><span lang=EN-US>dn: uid=fsmith, ou=employees, dc=foobar, dc=com</span>
<p><span style="FONT-FAMILY: 宋体">这是</span><span lang=EN-US>Fran</span><span style="FONT-FAMILY: 宋体">的</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">记录项的完整</span><span lang=EN-US>DN</span><span style="FONT-FAMILY: 宋体">，包括在目录树中的完整路径。</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">（和</span><span lang=EN-US>X.500</span><span style="FONT-FAMILY: 宋体">）使用</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">uid</span><span style="FONT-FAMILY: 宋体">（</span><span lang=EN-US>User ID</span><span style="FONT-FAMILY: 宋体">），不要把它和</span><span lang=EN-US>UNIX</span><span style="FONT-FAMILY: 宋体">的</span><span lang=EN-US>uid</span><span style="FONT-FAMILY: 宋体">号混淆了。</span>
<p><span>&nbsp; </span>objectclass: person
<p><span>&nbsp; </span>objectclass: organizationalPerson
<p><span>&nbsp; </span>objectclass: inetOrgPerson
<p><span>&nbsp; </span>objectclass: foobarPerson
<p><span style="FONT-FAMILY: 宋体">可以为任何一个对象根据需要分配多个对象类型。</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">person</span><span style="FONT-FAMILY: 宋体">对象类型要求</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">cn</span><span style="FONT-FAMILY: 宋体">（</span><span lang=EN-US>common name</span><span style="FONT-FAMILY: 宋体">）和</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">sn</span><span style="FONT-FAMILY: 宋体">（</span><span lang=EN-US>surname</span><span style="FONT-FAMILY: 宋体">）这两个域不能为空。</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">persion</span><span style="FONT-FAMILY: 宋体">对象类型允许有其它的可选域，包括</span><span lang=EN-US>givenname</span><span style="FONT-FAMILY: 宋体">、</span><span lang=EN-US>telephonenumber</span><span style="FONT-FAMILY: 宋体">，等等。</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">organizational Person</span><span style="FONT-FAMILY: 宋体">给</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">person</span><span style="FONT-FAMILY: 宋体">加入更多的可选域，</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">inetOrgPerson</span><span style="FONT-FAMILY: 宋体">又加入更多的可选域（包括电子邮件信息）。最后，</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">foobarPerson</span><span style="FONT-FAMILY: 宋体">是为</span><span lang=EN-US>Foobar</span><span style="FONT-FAMILY: 宋体">定制的对象类型，加入了很多定制的属性。</span>
<p><span>&nbsp; </span>uid: fsmith
<p><span>&nbsp; </span>givenname: Fran
<p><span>&nbsp; </span>sn: Smith
<p><span>&nbsp; </span>cn: Fran Smith
<p><span>&nbsp; </span>cn: Frances Smith
<p><span>&nbsp; </span>telephonenumber: 510-555-1234
<p><span>&nbsp; </span>roomnumber: 122G
<p><span>&nbsp; </span>o: Foobar, Inc.
<p><span style="FONT-FAMILY: 宋体">以前说过了，</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">uid</span><span style="FONT-FAMILY: 宋体">表示</span><span lang=EN-US>User ID</span><span style="FONT-FAMILY: 宋体">。当看到</span><span lang=EN-US>uid</span><span style="FONT-FAMILY: 宋体">的时候，就在脑袋里想一想&#8220;</span><span lang=EN-US>login</span><span style="FONT-FAMILY: 宋体">&#8221;。</span>
<p><span style="FONT-FAMILY: 宋体">请注意</span><span lang=EN-US>CN</span><span style="FONT-FAMILY: 宋体">有多个值。就象上面介绍的，</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">允许某些属性有多个值。为什么允许有多个值呢？假定你在用公司的</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器查找</span><span lang=EN-US>Fran</span><span style="FONT-FAMILY: 宋体">的电话号码。你可能只知道她的名字叫</span><span lang=EN-US>Fran</span><span style="FONT-FAMILY: 宋体">，但是对人力资源处的人来说她的正式名字叫做</span><span lang=EN-US>Frances</span><span style="FONT-FAMILY: 宋体">。因为保存了她的两个名字，所以用任何一个名字检索都可以找到</span><span lang=EN-US>Fran</span><span style="FONT-FAMILY: 宋体">的电话号码、电子邮件和办公房间号，等等。</span>
<p><span>&nbsp; </span>mailRoutingAddress: fsmith@foobar.com
<p><span>&nbsp; </span>mailhost: mail.foobar.com
<p><span style="FONT-FAMILY: 宋体">就象现在大多数的公司都上网了，</span><span lang=EN-US>Foobar</span><span style="FONT-FAMILY: 宋体">用</span><span lang=EN-US>Sendmail</span><span style="FONT-FAMILY: 宋体">发送邮件和处理外部邮件路由信息。</span><span lang=EN-US>Foobar</span><span style="FONT-FAMILY: 宋体">把所有用户的邮件信息都存在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">中。最新版本的</span><span lang=EN-US>Sendmail</span><span style="FONT-FAMILY: 宋体">支持这项功能。</span>
<p><span>&nbsp; </span>Userpassword: {crypt}3x1231v76T89N
<p><span>&nbsp; </span>uidnumber: 1234
<p><span>&nbsp; </span>gidnumber: 1200
<p><span>&nbsp; </span>gecos: Frances Smith
<p><span>&nbsp; </span>homedirectory: /home/fsmith
<p><span>&nbsp; </span>loginshell: /usr/local/bin/bash
<p><span style="FONT-FAMILY: 宋体">注意，</span><span lang=EN-US>Foobar</span><span style="FONT-FAMILY: 宋体">的系统管理员把所有用户的口令映射信息也都存在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">中。</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">FoobarPerson</span><span style="FONT-FAMILY: 宋体">类型的对象具有这种能力。再注意一下，用户口令是用</span><span lang=EN-US>UNIX</span><span style="FONT-FAMILY: 宋体">的口令加密格式存储的。</span><span lang=EN-US>UNIX</span><span style="FONT-FAMILY: 宋体">的</span><span lang=EN-US>uid</span><span style="FONT-FAMILY: 宋体">在这里为</span><span lang=EN-US style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 'Courier New'">uidnumber</span><span style="FONT-SIZE: 10.5pt; COLOR: black; FONT-FAMILY: 黑体">。</span><span style="FONT-FAMILY: 宋体">提醒你一下，关于如何在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">中保存</span><span lang=EN-US>NIS</span><span style="FONT-FAMILY: 宋体">信息，有完整的一份</span><span lang=EN-US>RFC</span><span style="FONT-FAMILY: 宋体">。在以后的文章中我会谈一谈</span><span lang=EN-US>NIS</span><span style="FONT-FAMILY: 宋体">的集成。</span>
<h2><a></a><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 黑体">复制</span></h2>
<p><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器可以使用基于&#8220;推&#8221;或者&#8220;拉&#8221;的技术，用简单或基于安全证书的安全验证，复制一部分或者所有的数据。</span>
<p><span style="FONT-FAMILY: 宋体">例如，</span><span lang=EN-US>Foobar</span><span style="FONT-FAMILY: 宋体">有一个&#8220;公用的&#8221;</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器，地址为</span><span lang=EN-US>ldap.foobar.com</span><span style="FONT-FAMILY: 宋体">，端口为</span><span lang=EN-US>389</span><span style="FONT-FAMILY: 宋体">。</span><span lang=EN-US>Netscape Communicator</span><span style="FONT-FAMILY: 宋体">的电子邮件查询功能、</span><span lang=EN-US>UNIX</span><span style="FONT-FAMILY: 宋体">的&#8220;</span><span lang=EN-US>ph</span><span style="FONT-FAMILY: 宋体">&#8221;命令要用到这个服务器，用户也可以在任何地方查询这个服务器上的员工和客户联系信息。公司的主</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器运行在相同的计算机上，不过端口号是</span><span lang=EN-US>1389</span><span style="FONT-FAMILY: 宋体">。</span>
<p><span style="FONT-FAMILY: 宋体">你可能即不想让员工查询资产管理或食谱的信息，又不想让信息技术人员看到整个公司的</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录。为了解决这个问题，</span><span lang=EN-US>Foobar</span><span style="FONT-FAMILY: 宋体">有选择地把子目录树从主</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器复制到&#8220;公用&#8221;</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器上，不复制需要隐藏的信息。为了保持数据始终是最新的，主目录服务器被设置成即时&#8220;推&#8221;同步。这些种方法主要是为了方便，而不是安全，因为如果有权限的用户想查询所有的数据，可以用另一个</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">端口。</span>
<p><span style="FONT-FAMILY: 宋体">假定</span><span lang=EN-US>Foobar</span><span style="FONT-FAMILY: 宋体">通过从奥克兰到欧洲的低带宽数据的连接用</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">管理客户联系信息。可以建立从</span><span lang=EN-US>ldap.foobar.com:1389</span><span style="FONT-FAMILY: 宋体">到</span><span lang=EN-US>munich-ldap.foobar.com:389</span><span style="FONT-FAMILY: 宋体">的数据复制，象下面这样：</span>
<p><span>&nbsp; </span>periodic pull: ou=asia,ou=customers,o=sendmail.com
<p><span>&nbsp; </span>periodic pull: ou=us,ou=customers,o=sendmail.com
<p><span>&nbsp; </span>immediate push: ou=europe,ou=customers,o=sendmail.com
<p><span style="FONT-FAMILY: 宋体">&#8220;拉&#8221;连接每</span><span lang=EN-US>15</span><span style="FONT-FAMILY: 宋体">分钟同步一次，在上面假定的情况下足够了。&#8220;推&#8221;连接保证任何欧洲的联系信息发生了变化就立即被&#8220;推&#8221;到</span><span lang=EN-US>Munich</span><span style="FONT-FAMILY: 宋体">。</span>
<p><span style="FONT-FAMILY: 宋体">用上面的复制模式，用户为了访问数据需要连接到哪一台服务器呢？在</span><span lang=EN-US>Munich</span><span style="FONT-FAMILY: 宋体">的用户可以简单地连接到本地服务器。如果他们改变了数据，本地的</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器就会把这些变化传到主</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器。然后，主</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器把这些变化&#8220;推&#8221;回本地的&#8220;公用&#8221;</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器保持数据的同步。这对本地的用户有很大的好处，因为所有的查询（大多数是读）都在本地的服务器上进行，速度非常快。当需要改变信息的时候，最终用户不需要重新配置客户端的软件，因为</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录服务器为他们完成了所有的数据交换工作。</span>
<h2><a></a><span style="FONT-FAMILY: 黑体">安全和访问控制</span></h2>
<p><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">提供很复杂的不同层次的访问控制或者</span><span lang=EN-US>ACI</span><span style="FONT-FAMILY: 宋体">。因这些访问可以在服务器端控制，这比用客户端的软件保证数据的安全可安全多了。</span>
<p><span style="FONT-FAMILY: 宋体">用</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">的</span><span lang=EN-US>ACI</span><span style="FONT-FAMILY: 宋体">，可以完成：</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">给予用户改变他们自己的电话号码和家庭地址的权限，但是限制他们对其它数据（如，职务名称，经理的登录名，等等）只有&#8220;只读&#8221;权限。</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">给予&#8220;</span><span lang=EN-US>HR-admins</span><span style="FONT-FAMILY: 宋体">&#8221;组中的所有人权限以改变下面这些用户的信息：经理、工作名称、员工号、部门名称和部门号。但是对其它域没有写权限。</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">禁止任何人查询</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器上的用户口令，但是可以允许用户改变他或她自己的口令。</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">给予经理访问他们上级的家庭电话的只读权限，但是禁止其他人有这个权限。</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">给予&#8220;</span><span lang=EN-US>host-admins</span><span style="FONT-FAMILY: 宋体">&#8221;组中的任何人创建、删除和编辑所有保存在</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">服务器中的与计算机主机有关的信息</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">通过</span><span lang=EN-US>Web</span><span style="FONT-FAMILY: 宋体">，允许&#8220;</span><span lang=EN-US>foobar-sales</span><span style="FONT-FAMILY: 宋体">&#8221;组中的成员有选择地给予或禁止他们自己读取一部分客户联系数据的读权限。这将允许他们把客户联系信息下载到本地的笔记本电脑或个人数字助理（</span><span lang=EN-US>PDA</span><span style="FONT-FAMILY: 宋体">）上。（如果销售人员的软件都支持</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">，这将非常有用）</span>
<p><span lang=EN-US style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">通过</span><span lang=EN-US>Web</span><span style="FONT-FAMILY: 宋体">，允许组的所有者删除或添加他们拥有的组的成员。例如：可以允许销售经理给予或禁止销售人员改变</span><span lang=EN-US>Web</span><span style="FONT-FAMILY: 宋体">页的权限。也可以允许邮件假名（</span><span lang=EN-US>mail aliase</span><span style="FONT-FAMILY: 宋体">）的所有者不经过</span><span lang=EN-US>IT</span><span style="FONT-FAMILY: 宋体">技术人员就直接从邮件假名中删除或添加用户。&#8220;公用&#8221;的邮件列表应该允许用户从邮件假名中添加或删除自己（但是只能是自己）。也可以对</span><span lang=EN-US>IP</span><span style="FONT-FAMILY: 宋体">地址或主机名加以限制。例如，某些域只允许用户</span><span lang=EN-US>IP</span><span style="FONT-FAMILY: 宋体">地址以</span><span lang=EN-US>192.168.200.*</span><span style="FONT-FAMILY: 宋体">开头的有读的权限，或者用户反向查找</span><span lang=EN-US>DNS</span><span style="FONT-FAMILY: 宋体">得到的主机名必须为</span><span lang=EN-US>*.foobar.com</span><span style="FONT-FAMILY: 宋体">。</span>
<p><span style="FONT-FAMILY: 宋体">这不过是让你了解一下可以对</span><span lang=EN-US>LDAP</span><span style="FONT-FAMILY: 宋体">目录进行怎样的访问控制，实际上真正实现起来需要做的工作比这多得多。在以后的文章中我会详细地讨论访问控制。</span></p>
</div>
</div>
<img src ="http://www.blogjava.net/gm_jing/aggbug/134896.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2007-08-07 11:09 <a href="http://www.blogjava.net/gm_jing/articles/134896.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>事务管理方式</title><link>http://www.blogjava.net/gm_jing/articles/133545.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Tue, 31 Jul 2007 04:19:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/133545.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/133545.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/133545.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/133545.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/133545.html</trackback:ping><description><![CDATA[<p>1． 依赖特定事务资源的事务处理<br>这是应用开发中最常见的模式，即通过特定资源提供的事务机制进行事务管理。<br>如通过JDBC、JTA 的rollback、commit方法；Hibernate Transaction 的rollback、commit方法等。<br>这种方法大家已经相当熟悉。</p>
<p><br>2． 依赖容器的参数化事务管理<br>通过容器提供的集约式参数化事务机制，实现事务的外部管理，如EJB 中的事务管理模式。<br>&nbsp;容器管理的参数化事务为程序开发提供了相当的灵活性，同时因为将事务委托给容器进行管理，<br>应用逻辑中无需再编写事务代码，大大节省了代码量（特别是针对需要同时操作多个事务资源的应用），从而提高了生产率。<br></p>
<br><br><br><br>
<hr>
<div class=postcontent><font size=2>【引用】</font> <a href="http://www.blogjava.net/georgehill/archive/2005/07/08/7374.html"><font color=#366900>http://www.blogjava.net/georgehill/archive/2005/07/08/7374.html</font></a>&nbsp;<br><br>事务的4个基本特性（ACID）：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1. Atomic（原子性）：事务中包含的操作被看作一个逻辑单元，这个逻辑单元中的操作要么全部成功，要么全部失败。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2. Consistency（一致性）：只有合法的数据可以被写入数据库，否则事务应该将其回滚到最初状态。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3. Isolation（隔离性）：事务允许多个用户对同一个数据的并发访问，而不破坏数据的正确性和完整性。同时，并行事务的修改必须与其他并行事务的修改相互独立。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4. Durability（持久性）：事务结束后，事务处理的结果必须能够得到固化。
<hr>
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;数据库操作过程中可能出现的3种不确定情况：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1. 脏读取（Dirty Reads）：一个事务读取了另一个并行事务未提交的数据。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2. 不可重复读取（Non-repeatable Reads）：一个事务再次读取之前的数据时，得到的数据不一致，被另一个已提交的事务修改。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3. 虚读（Phantom Reads）：一个事务重新执行一个查询，返回的记录中包含了因为其他最近提交的事务而产生的新记录。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;标准SQL规范中，为了避免上面3种情况的出现，定义了4个事务隔离等级：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1. Read Uncommitted：最低等级的事务隔离，仅仅保证了读取过程中不会读取到非法数据。上诉3种不确定情况均有可能发生。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2. Read Committed：大多数主流数据库的默认事务等级，保证了一个事务不会读到另一个并行事务已修改但未提交的数据，避免了&#8220;脏读取&#8221;。该级别适用于大多数系统。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3. Repeatable Read：保证了一个事务不会修改已经由另一个事务读取但未提交（回滚）的数据。避免了&#8220;脏读取&#8221;和&#8220;不可重复读取&#8221;的情况，但是带来了更多的性能损失。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4. Serializable：最高等级的事务隔离，上面3种不确定情况都将被规避。这个级别将模拟事务的串行执行。
<hr>
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hibernate将事务管理委托给底层的JDBC或者JTA，默认是基于JDBC Transaction的。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hibernate支持&#8220;悲观锁（Pessimistic Locking）&#8221;和&#8220;乐观锁（Optimistic Locking）&#8221;。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;悲观锁对数据被外界修改持保守态度，因此，在整个数据处理过程中，将数据处于锁定状态。悲观锁的实现，往往依靠数据库提供的锁机制。Hibernate通过使用数据库的for update子句实现了悲观锁机制。Hibernate的加锁模式有：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1. LockMode.NONE：无锁机制<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2. LockMode.WRITE：Hibernate在Insert和Update记录的时候会自动获取<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3. LockMode.READ：Hibernate在读取记录的时候会自动获取<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4. LockMode.UPGRADE：利用数据库的for update子句加锁<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5. LockMode.UPGRADE_NOWAIT：Oracle的特定实现，利用Oracle的for update nowait子句实现加锁<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;乐观锁大多是基于数据版本（Version）记录机制实现。Hibernate在其数据访问引擎中内置了乐观锁实现，可以通过class描述符的optimistic-lock属性结合version描述符指定。optimistic-lock属性有如下可选取值：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1. none：无乐观锁<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2. version：通过版本机制实现乐观锁<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3. dirty：通过检查发生变动过的属性实现乐观锁<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4. all：通过检查所有属性实现乐观锁 </div>
<img src ="http://www.blogjava.net/gm_jing/aggbug/133545.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2007-07-31 12:19 <a href="http://www.blogjava.net/gm_jing/articles/133545.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据库连接池实现原理</title><link>http://www.blogjava.net/gm_jing/articles/128119.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Wed, 04 Jul 2007 07:29:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/128119.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/128119.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/128119.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/128119.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/128119.html</trackback:ping><description><![CDATA[<div class=cnt>
<p style="LINE-HEIGHT: 150%"><a href="http://blog.csdn.net/raykcn/archive/2006/11/15/1384779.aspx" target=_blank><span><strong><font face="宋体, MS Song" color=#0000ff size=5><u>Connection Pool</u></font></strong></span></a></p>
数据库连接池的基本原理是在内部对象池中维护一定数量的数据库连接，并对外暴露数据库连接获取和返回方法。如：<br><br>外部使用者可通过getConnection 方法获取连接，使用完毕后再通过releaseConnection 方法将连接返回，注意此时连接并没有关闭，而是由连接池管理器回收，并为下一次使用做好准备。<br><br>数据库连接池技术带来的优势：<br><br>1． 资源重用<br><br>由于数据库连接得到重用，避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上，另一方面也增进了系统运行环境的平稳性（减少内存碎片以及数据库临时进程/线程的数量）。<br><br>2． 更快的系统响应速度<br><br>数据库连接池在初始化过程中，往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言，直接利用现有可用连接，避免了数据库连接初始化和释放过程的时间开销，从而缩减了系统整体响应时间。<br><br>3． 新的资源分配手段<br><br>对于多应用共享同一数据库的系统而言，可在应用层通过数据库连接的配置，实现数据库连接池技术，几年钱也许还是个新鲜话题，对于目前的业务系统而言，如果设计中还没有考虑到连接池的应用，那么&#8230;&#8230;.快在设计文档中加上这部分的内容吧。某一应用最大可用数据库连接数的限制，避免某一应用独占所有数据库资源。<br><br>4． 统一的连接管理，避免数据库连接泄漏<br><br>在较为完备的数据库连接池实现中，可根据预先的连接占用超时设定，强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。一个最小化的数据库连接池实现：<br><br>public class DBConnectionPool implements ConnectionPool<br><br>{<br><br>private static Vector pool;<br><br>private final int POOL_MAX_SIZE = 20;<br><br>/*<br><br>* 获取数据库连接<br><br>* 如果当前池中有可用连接，则将池中最后一个返回，如果没有，则新建一个返回<br><br>*/<br><br>public synchronized Connection getConnection() throws DBException<br><br>{<br><br>if (pool == null)<br><br>{<br><br>pool = new Vector();<br><br>}<br><br>Connection conn;<br><br>if (pool.isEmpty())<br><br>{<br><br>conn = createConnection();<br><br>}<br><br>else<br><br>{<br><br>int last_idx = pool.size() - 1;<br><br>conn = (Connection) pool.get(last_idx);<br><br>pool.remove(pool.get(last_idx));<br><br>}<br><br>return conn;<br><br>}<br><br>/*<br><br>* 将使用完毕的数据库连接放回备用池。<br><br>*<br><br>* 判断当前池中连接数是否已经超过阀值（POOL_MAX_SIZE），<br><br>* 如果超过，则关闭该连接。<br><br>* 否则放回池中以备下次重用。<br><br>*/<br><br>public synchronized void releaseConnection(Connection conn)<br><br>{<br><br>if (pool.size() &gt; POOL_MAX_SIZE)<br><br>{<br><br>try<br><br>{<br><br>conn.close();<br><br>}<br><br>catch (SQLException e)<br><br>{<br><br>e.printStackTrace();<br><br>}<br><br>}<br><br>else<br><br>{<br><br>pool.add(conn);<br><br>}<br><br>}<br><br>/**<br><br>* 读取数据库配置信息,并从数据库连接池中获得数据库连接<br><br>*<br><br>* @return<br><br>* @throws DBException<br><br>*/<br><br>private static Connection createConnection() throws DBException<br><br>{<br><br>Connection conn;<br><br>try<br><br>{<br><br>Class.forName("oracle.jdbc.driver.OracleDriver");<br><br>conn = DriverManager.getConnection(<br><br>"jdbc:oracle:thin:@localhost:1521:oracle",<br><br>"personal", "personal");<br><br>return conn;<br><br>}<br><br>catch (ClassNotFoundException e)<br><br>{<br><br>throw new DBException("ClassNotFoundException when loading JDBC Driver");<br><br>}<br><br>catch (SQLException e)<br><br>{<br><br>throw new DBException("SQLException when loading JDBC Driver");<br><br>}<br><br>}<br><br>}<br><br>上面的代码实现了一个最简单的连接池，因为简单，所以明了，目的只是展示数据库连接池实现的一般原理。完备的连接池实现固然错综复杂，但就其根本而言，还是源自同样的思想。<br><br>先脱离连接池本身的具体实现，我们看看这段代码在实际应用中可能产生的问题，注意到，由于getConnection 方法返回的是一个标准的JDBC Connection，程序员由于编程习惯，可能会习惯性的调用其close 方法关闭连接。如此一来，连接无法得到重用，数据库连接池机制形同虚设。为了解决这个问题，比较好的途径有：<br><br>1． Decorator 模式<br><br>2． Dynamic Proxy 模式<br><br>下面我们就这两种模式进行一些探讨<br><br>Decorator模式<br><br>&#8220;Decorator 模式的主要目的是利用一个对象，透明的为另一个对象添加新的功能&#8221;。这句话是从GOF 关于设计模式的经典著作《设计模式-可复用面向对象软件的基础》一书中关于Decorator 模式的描述直译而来，可能比较难以理解。简单来讲，就是通过一个Decorator 对原有对象进行封装，同时实现与原有对象相同的接口，从而得到一个基于原有对象的，对既有接口的增强性实现。其UML 描述如下：对于前面所讨论的Connection 释放的问题，理所当然，我们首先想到的是，如果能让JDBC Connection 在执行close 操作时自动将自己返回到数据库连接池中，那么所有问题都将迎刃而解，但是，JDBC Connection 自己显然无法根据实际情况判断何去何从。此时，引入Decorator 模式来解决我们所面对的问题就非常合适。<br>首先，我们引入一个ConnectionDecorator 类:<br>public class ConnectionDecorator implements Connection<br>{<br>Connection dbconn;<br>public ConnectionDecorator(Connection conn)<br>{<br>this.dbconn = conn; //实际从数据库获得的Connection引用<br>}<br>/* 此方法将被子类覆盖，以实现数据库连接池的连接返回操作<br>* @see java.sql.Connection#close()<br>*/<br>public void close() throws SQLException<br>{<br>this.dbconn.close();<br>}<br>/* (non-Javadoc)<br>* @see java.sql.Connection#commit()<br>*/<br>public void commit() throws SQLException<br>{<br>this.dbconn.commit();//调用实际连接的commit方法<br>}<br>&#8230;&#8230;<br>//以下各个方法类似,均调用dbconn.xxx方法作为Connection接口定义的功能。<br>&#8230;&#8230;<br>}<br>可以看到，ConnectionDecorator 类实际上是对传入的数据库连接加上了一个外壳，它实现了java.sql.Connection接口，不过本身并没有实现任何实际内容，只是简单的把方法的实现委托给运行期实际获得的Connection 实例，而从外部来看，ConnectionDecorator与普通的Connection 实例并没有什么区别，因为它们实现了同样的接口，对外提供了同样的功能调用。目标很清楚，通过这样的封装，我们的ConnectionDecorator 对于外部的程序员而言，调用方法与普通的JDBC Connection 完全相同，而在内部通过对ConnectionDecorator 的修改，我们就可以透明的改变现有实现，为之增加新的特性：<br>public class PooledConnection<br>extends ConnectionDecorator<br>implements Connection /????还需要实现这个接口吗？<br>{<br>private ConnectionPool connPool;<br>public PooledConnection(ConnectionPool pool, Connection conn)<br>{<br>super(conn);<br>connPool = pool;<br>}<br>/**<br>* 覆盖close方法，将数据库连接返回连接池，而不是直接关闭连接<br>*/<br>public void close() throws SQLException<br>{<br>connPool.releaseConnection(this.dbconn);<br>}<br>&#8230;&#8230;<br>}<br>为了应用新的PooledConnection，我们需要对原本的DBConnectionPool.getConnection 和releaseConnection 方法稍做改造:<br>public synchronized Connection getConnection() throws DBException<br>{<br>if (pool == null)<br>{<br>pool = new Vector();<br>}<br>Connection conn;<br>if (pool.isEmpty())<br>{<br>conn = createConnection();<br>}<br>else<br>{<br>int last_idx = pool.size() - 1;<br>conn = (Connection) pool.get(last_idx);<br>pool.remove(pool.get(last_idx));<br>}<br>return new PooledConnection(this,conn);<br>}<br>public synchronized void releaseConnection(Connection conn)<br>{<br>if (conn instanceof PooledConnection || pool.size() &gt; POOL_MAX_SIZE)<br>{<br>try {<br>conn.close();<br>}<br>catch (SQLException e)<br>{<br>e.printStackTrace();<br>}<br>}<br>else{<br>&nbsp;&nbsp; pool.add(conn);<br>}<br><br>}<br><br>此时，获取数据库连接后，调用者只需要按照JDBC Connection 的标准用法进行调用即可，从而实现了数据库连接池的透明化。同样的道理，我们甚至可以利用Decorator模式对DriverManager 类进行同样的改造，从而最小化数据库连接池实现对传统JDBC编码方式的影响。有兴趣的可以基于同样的原理尝试自行实现。</div>
 <img src ="http://www.blogjava.net/gm_jing/aggbug/128119.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2007-07-04 15:29 <a href="http://www.blogjava.net/gm_jing/articles/128119.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ejb中激活，失活机制</title><link>http://www.blogjava.net/gm_jing/articles/126040.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Mon, 25 Jun 2007 02:22:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/126040.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/126040.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/126040.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/126040.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/126040.html</trackback:ping><description><![CDATA[<p>Entity bean<br />
为了复用Bean，会采用一级Cache加上一级InstancePool(StatelessSessionBean是不需要Cache的)，<br />
从而支持将StatefulSessionBean持久化到磁盘，支持EntityBean的Bean Instance<br />
(注意这个Bean Instance和client得到的EntityBean是不同的，它没有和任何的DB Record关联)的复用，<br />
这就导致了ejbactivate、ejbPassivate等的引入。</p>
<p>ejbPassivate:当一个bean实例被客户引用，并执行一个业务方法后，容器会自动读取bean的实例字段，<br />
然后通过容器与数据库发生关系，保存改变的数据，执行完毕后bean被钝化，并调用ejbpassviate()方法通知bean。</p>
<p>ejbactivate：当客户过一端时间又调用这个bean的某业务方法时，被钝化的bean又重新的激活，但是并不是马上执行这个业务方法，<br />
而是由ejb对象首先调用ejbactivate()方法通知bean，bean实例要激活，然后从数据库中提取数据，并自动将数据值映射到bean实例，<br />
然后调用ejbload()方法，实例被再一次初始化，最后才开始执行要执行的业务方法。<br />
<br />
<span lang="EN-US">Stateful&nbsp;Session&nbsp;Bean&nbsp;</span> <br />
<span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">以</span> <span lang="EN-US">Stateful&nbsp;Session&nbsp;Bean&nbsp;</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">为例：其</span> <span lang="EN-US">Cache</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">大小决定了内存中可以同时存在的</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实例的数量，根据</span> <span lang="EN-US">MRU</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">或</span> <span lang="EN-US">NRU</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">算法，实例在激活和去激活状态之间迁移，激活机制是当客户端调用某个</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实例业务方法时，如果对应</span> <span lang="EN-US">EJB&nbsp;Object</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">发现自己没有绑定对应的</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实例则从其去激活</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">存储中（通过序列化机制存储实例）回复（激活）此实例。状态变迁前会调用对应的</span> <span lang="EN-US">ejbActive</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和</span> <span lang="EN-US">ejbPassivate</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">方法。</span></p>
<img src ="http://www.blogjava.net/gm_jing/aggbug/126040.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2007-06-25 10:22 <a href="http://www.blogjava.net/gm_jing/articles/126040.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>EJB相关</title><link>http://www.blogjava.net/gm_jing/articles/124910.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Mon, 18 Jun 2007 04:17:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/124910.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/124910.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/124910.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/124910.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/124910.html</trackback:ping><description><![CDATA[<strong>EJB是基于哪些技术实现的？并说出<font face="Times New Roman, serif">SessionBean</font>和<font face="Times New Roman, serif">EntityBean</font>的区别，<font face="Times New Roman, serif">StatefulBean</font>和<font face="Times New Roman, serif">StatelessBean</font>的区别。</strong><font face="Times New Roman, serif"><br />
EJB</font>包括<font face="Times New Roman, serif">Session Bean</font>、<font face="Times New Roman, serif">Entity Bean</font>、<font face="Times New Roman, serif">Message Driven Bean</font>，基于<font face="Times New Roman, serif">JNDI</font>、<font face="Times New Roman, serif">RMI</font>、<font face="Times New Roman, serif">JAT</font>等技术实现。<font face="Times New Roman, serif"><br />
SessionBean</font>在<font face="Times New Roman, serif">J2EE</font>应用程序中被用来完成一些服务器端的业务操作，例如访问数据库、调用其他<font face="Times New Roman, serif">EJB</font>组件。<font face="Times New Roman, serif">EntityBean</font>被用来代表应用系统中用到的数据。<font face="Times New Roman, serif"><br />
</font>对于客户机，<font face="Times New Roman, serif">SessionBean</font>是一种非持久性对象，它实现某些在服务器上运行的业务逻辑。<font face="Times New Roman, serif"><br />
</font>对于客户机，<font face="Times New Roman, serif">EntityBean</font>是一种持久性对象，它代表一个存储在持久性存储器中的实体的对象视图，或是一个由现有企业应用程序实现的实体。<font face="Times New Roman, serif"><br />
Session Bean </font>还可以再细分为 <font face="Times New Roman, serif">Stateful Session Bean </font>与 <font face="Times New Roman, serif">Stateless Session Bean </font>，这两种的 <font face="Times New Roman, serif">Session Bean</font>都可以将系统逻辑放在 <font face="Times New Roman, serif">method</font>之中执行，不同的是 <font face="Times New Roman, serif">Stateful Session Bean </font>可以记录呼叫者的状态，因此通常来说，一个使用者会有一个相对应的 <font face="Times New Roman, serif">Stateful Session Bean </font>的实体。<font face="Times New Roman, serif">Stateless Session Bean </font>虽然也是逻辑组件，但是他却不负责记录使用者状态，也就是说当使用者呼叫 <font face="Times New Roman, serif">Stateless Session Bean </font>的时候，<font face="Times New Roman, serif">EJB Container </font>并不会找寻特定的 <font face="Times New Roman, serif">Stateless Session Bean </font>的实体来执行这个 <font face="Times New Roman, serif">method</font>。换言之，很可能数个使用者在执行某个 <font face="Times New Roman, serif">Stateless Session Bean </font>的 <font face="Times New Roman, serif">methods </font>时，会是同一个 <font face="Times New Roman, serif">Bean </font>的 <font face="Times New Roman, serif">Instance </font>在执行。从内存方面来看， <font face="Times New Roman, serif">Stateful Session Bean </font>与 <font face="Times New Roman, serif">Stateless Session Bean </font>比较， <font face="Times New Roman, serif">Stateful Session Bean </font>会消耗 <font face="Times New Roman, serif">J2EE Server </font>较多的内存，然而 <font face="Times New Roman, serif">Stateful Session Bean </font>的优势却在于他可以维持使用者的状态。 <br />
---&gt;&gt;如果一个业务流程需要多次方法调用才能完成服务，适合<font face="Times New Roman, serif">Stateful Session Bean；如果一个业务流程只需要一次方法调用就能完成服务，适合<font face="Times New Roman, serif">Stateless Session Bean</font></font><br />
<br />
<br />
<span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">与</span> <span lang="EN-US">JAVA&nbsp;BEAN</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的区别？</span> <span lang="EN-US"><br />
Java&nbsp;Bean&nbsp;</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是可复用的组件，对</span> <span lang="EN-US">Java&nbsp;Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">并没有严格的规范，理论上讲，任何一个</span> <span lang="EN-US">Java</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类都可以是一个</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。但通常情况下，由于</span> <span lang="EN-US">Java&nbsp;Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是被容器所创建（如</span> <span lang="EN-US">Tomcat</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）的，所以</span> <span lang="EN-US">Java&nbsp;Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">应具有一个无参的构造器，另外，通常</span> <span lang="EN-US">Java&nbsp;Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">还要实现</span> <span lang="EN-US">Serializable</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口用于实现</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的持久性。</span> <span lang="EN-US">Java&nbsp;Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实际上相当于微软</span> <span lang="EN-US">COM</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">模型中的本地进程内</span> <span lang="EN-US">COM</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">组件，它是不能被跨进程访问的。</span> <span lang="EN-US">Enterprise&nbsp;Java&nbsp;Bean&nbsp;</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">相当于</span> <span lang="EN-US">DCOM</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，即分布式组件。它是基于</span> <span lang="EN-US">Java</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的远程方法调用（</span> <span lang="EN-US">RMI</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）技术的，所以</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">可以被远程访问（跨进程、跨计算机）。但</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">必须被布署在诸如</span> <span lang="EN-US">Webspere</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、</span> <span lang="EN-US">WebLogic</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这样的容器中，</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">客户从不直接访问真正的</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">组件，而是通过其容器访问。</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">容器是</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">组件的代理，</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">组件由容器所创建和管理。客户通过容器来访问真正的</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">组件。</span> <span lang="EN-US"><br />
<br />
<br />
<br />
<span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">包括（</span> <span lang="EN-US">SessionBean,EntityBean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）说出他们的生命周期，及如何管理事务的？</span> <span lang="EN-US"><br />
SessionBean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">：</span> <span lang="EN-US">Stateless&nbsp;Session&nbsp;Bean&nbsp;</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的生命周期是由容器决定的，当客户机发出请求要建立一个</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的实例时，</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">容器不一定要创建一个新的</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的实例供客户机调用，而是随便找一个现有的实例提供给客户机。当客户机第一次调用一个</span> <span lang="EN-US">Stateful&nbsp;Session&nbsp;Bean&nbsp;</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">时，容器必须立即在服务器中创建一个新的</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实例，并关联到客户机上，以后此客户机调用</span> <span lang="EN-US">Stateful&nbsp;Session&nbsp;Bean&nbsp;</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的方法时容器会把调用分派到与此客户机相关联的</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实例。</span> <span lang="EN-US"><br />
EntityBean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">：</span> <span lang="EN-US">Entity&nbsp;Beans</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">能存活相对较长的时间，并且状态是持续的。只要数据库中的数据存在，</span> <span lang="EN-US">Entity&nbsp;beans</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">就一直存活。而不是按照应用程序或者服务进程来说的。即使</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">容器崩溃了，</span> <span lang="EN-US">Entity&nbsp;beans</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">也是存活的。</span> <span lang="EN-US">Entity&nbsp;Beans</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">生命周期能够被容器或者</span> <span lang="EN-US">&nbsp;Beans</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">自己管理。</span> <span lang="EN-US"><br />
EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">通过以下技术管理实务：对象管理组织（</span> <span lang="EN-US">OMG</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）的对象实务服务（</span> <span lang="EN-US">OTS</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">），</span> <span lang="EN-US">Sun&nbsp;Microsystems</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的</span> <span lang="EN-US">Transaction&nbsp;Service</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（</span> <span lang="EN-US">JTS</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）、</span> <span lang="EN-US">Java&nbsp;Transaction&nbsp;API</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（</span> <span lang="EN-US">JTA</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">），开发组（</span> <span lang="EN-US">X/Open</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）的</span> <span lang="EN-US">XA</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口。<br />
</span><br />
<br />
&nbsp;<span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">容器提供的服务</span> <span lang="EN-US"><br />
</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">主要提供声明周期管理、代码产生、持续性管理、安全、事务管理、锁和并发行管理等服务。<br />
<br />
<br />
<br />
<br />
<span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">规范规定</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中禁止的操作有哪些？</span> <span lang="EN-US">&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;1.</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">不能操作线程和线程</span> <span lang="EN-US">API(</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">线程</span> <span lang="EN-US">API</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">指非线程对象的方法如</span> <span lang="EN-US">notify,wait</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">等</span> <span lang="EN-US">)</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，</span> <span lang="EN-US">2.</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">不能操作</span> <span lang="EN-US">awt</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，</span> <span lang="EN-US">3.</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">不能实现服务器功能，</span> <span lang="EN-US">4.</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">不能对静态属生存取，</span> <span lang="EN-US">5.</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">不能使用</span> <span lang="EN-US">IO</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">操作直接存取文件系统，</span> <span lang="EN-US">6.</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">不能加载本地库</span> <span lang="EN-US">.</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，</span> <span lang="EN-US">7.</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">不能将</span> <span lang="EN-US">this</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">作为变量和返回，</span> <span lang="EN-US">8.</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">不能循环调用。<br />
<br />
<br />
<br />
&nbsp;<span lang="EN-US">remote</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口和</span> <span lang="EN-US">home</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口主要作用</span> <span lang="EN-US"><br />
remote</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口定义了业务方法，用于</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">客户端调用业务方法。</span> <span lang="EN-US"><br />
home</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口是</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">工厂用于创建和移除查找</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实例</span> <span lang="EN-US"><br />
</span><br />
<br />
<span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的激活机制</span> <span lang="EN-US"><br />
</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">以</span> <span lang="EN-US">Stateful&nbsp;Session&nbsp;Bean&nbsp;</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">为例：其</span> <span lang="EN-US">Cache</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">大小决定了内存中可以同时存在的</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实例的数量，根据</span> <span lang="EN-US">MRU</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">或</span> <span lang="EN-US">NRU</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">算法，实例在激活和去激活状态之间迁移，激活机制是当客户端调用某个</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实例业务方法时，如果对应</span> <span lang="EN-US">EJB&nbsp;Object</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">发现自己没有绑定对应的</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实例则从其去激活</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">存储中（通过序列化机制存储实例）回复（激活）此实例。状态变迁前会调用对应的</span> <span lang="EN-US">ejbActive</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和</span> <span lang="EN-US">ejbPassivate</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">方法。</span>&nbsp;<span lang="EN-US"><br />
<br />
</span><br />
<span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的几种类型</span> <span lang="EN-US"><br />
</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">会话（</span> <span lang="EN-US">Session</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</span> <span lang="EN-US">Bean&nbsp;</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，实体（</span> <span lang="EN-US">Entity</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</span> <span lang="EN-US">Bean&nbsp;</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">消息驱动的（</span> <span lang="EN-US">Message&nbsp;Driven</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</span> <span lang="EN-US">Bean<br />
</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">会话</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">又可分为有状态（</span> <span lang="EN-US">Stateful</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）和无状态（</span> <span lang="EN-US">Stateless</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）两种</span> <span lang="EN-US"><br />
</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实体</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">可分为</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">管理的持续性（</span> <span lang="EN-US">BMP</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）和容器管理的持续性（</span> <span lang="EN-US">CMP</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）两种</span> <br />
</span><br />
<br />
</span><font face="宋体">客服端调用</font> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象的几个基本步骤</span> <span lang="EN-US"><br />
</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">设置</span> <span lang="EN-US">JNDI</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">服务工厂以及</span> <span lang="EN-US">JNDI</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">服务地址系统属性，查找</span> <span lang="EN-US">Home</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口，从</span> <span lang="EN-US">Home</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口调用</span> <span lang="EN-US">Create</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">方法创建</span> <span lang="EN-US">Remote</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口，通过</span> <span lang="EN-US">Remote</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口调用其业务方法。<br />
<br />
<br />
</span>说说你所熟悉或听说过的 <span lang="EN-US">j2ee</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中的几种常用模式</span> <span lang="EN-US">?</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">及对设计模式的一些看法</span> <span lang="EN-US"><br />
&nbsp;&nbsp;Session&nbsp;Facade&nbsp;Pattern</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">：使用</span> <span lang="EN-US">SessionBean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">访问</span> <span lang="EN-US">EntityBean<br />
Message&nbsp;Facade&nbsp;Pattern</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">：实现异步调用</span> <span lang="EN-US"><br />
EJB&nbsp;Command&nbsp;Pattern</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">：使用</span> <span lang="EN-US">Command&nbsp;JavaBeans</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">取代</span> <span lang="EN-US">SessionBean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，实现轻量级访问</span> <span lang="EN-US"><br />
Data&nbsp;Transfer&nbsp;Object&nbsp;Factory</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">：通过</span> <span lang="EN-US">DTO&nbsp;Factory</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">简化</span> <span lang="EN-US">EntityBean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">数据提供特性</span> <span lang="EN-US"><br />
Generic&nbsp;Attribute&nbsp;Access</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">：通过</span> <span lang="EN-US">AttibuteAccess</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口简化</span> <span lang="EN-US">EntityBean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">数据提供特性</span> <span lang="EN-US"><br />
Business&nbsp;Interface</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">：通过远程（本地）接口和</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类实现相同接口规范业务逻辑一致性<br />
<br />
</span><font face="宋体">说说在</font> <span lang="EN-US">weblogic</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中开发消息</span> <span lang="EN-US">Bean</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">时的</span> <span lang="EN-US">persistent</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">与</span> <span lang="EN-US">non-persisten</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的差别</span> <span lang="EN-US"><br />
persistent</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">方式的</span> <span lang="EN-US">MDB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">可以保证消息传递的可靠性</span> <span lang="EN-US">,</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">也就是如果</span> <span lang="EN-US">EJB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">容器出现问题而</span> <span lang="EN-US">JMS</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">服务器依然会将消息在此</span> <span lang="EN-US">MDB</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">可用的时候发送过来，而</span> <span lang="EN-US">non</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">－</span> <span lang="EN-US">persistent</span> <span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">方式的消息将被丢弃。</span><br />
</span>
<img src ="http://www.blogjava.net/gm_jing/aggbug/124910.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2007-06-18 12:17 <a href="http://www.blogjava.net/gm_jing/articles/124910.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]一个网上简单的EJB入门例子 </title><link>http://www.blogjava.net/gm_jing/articles/124907.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Mon, 18 Jun 2007 03:43:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/124907.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/124907.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/124907.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/124907.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/124907.html</trackback:ping><description><![CDATA[<div>一个简单的HELLO例子</div>
<div>1、安装Weblogic：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用的EJB服务是BEA的weblogic8.1，下载BEA的weblogic8.1，然后安装。安装步骤省略。　　</div>
<div>2、定义EJB远程接口（Remote Interface）：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 任何一个EJB都是通过Remote Interface被调用，EJB开发者首先要在Remote Interface中定义这个EJB可以被外界调用的所有方法。执行Remote Interface的类由EJB生成工具生成。<br>　　以下是HelloBean的Remote Inteface程序：</div>
<div>package com.leo；<br>import java.rmi.RemoteException；<br>import java.rmi.Remote；<br>import javax.ejb.*；</div>
<div>public interface Hello extends EJBObject, Remote {<br>//this method just get "Hello EJB" from HelloEJB.<br>public String getHello（） throws RemoteException；</div>
<div>}</div>
<div>3、定义Home Interface</div>
<div>EJB容器通过EJB的Home Interface来创建EJB实例，和Remote Interface一样，执行Home Interface的类由EJB生成工具生成。以下是HelloEJB 的Home Interface程序：</div>
<div>package com.leo；</div>
<div>import javax.ejb.*；<br>import java.rmi.Remote；<br>import java.rmi.RemoteException；<br>import java.util.*；</div>
<div>/**<br>* This interface is extremely simple it declares only<br>* one create method.<br>*/<br>public interface HelloHome extends EJBHome {</div>
<div>public Hello create（） throws CreateException,<br>RemoteException；</div>
<div>}</div>
<div>4、写EJB类</div>
<div>在EJB类中，编程者必须给出在Remote Interface中定义的远程方法的具体实现。EJB类中还包括一些 EJB规范中定义的必须实现的方法，这些方法都有比较统一的实现模版，编程者只需花费精力在具体业务方法的实现上。</div>
<div>以下是HelloEJB的代码：</div>
<div>package com.leo;<br>import javax.ejb.*;<br>public class HelloEJB implements SessionBean{<br>&nbsp;&nbsp;&nbsp; public void ejbCreate(){}<br>&nbsp;&nbsp;&nbsp; public void ejbRemove(){}<br>&nbsp;&nbsp;&nbsp; public void ejbActivate(){}<br>&nbsp;&nbsp;&nbsp; public void ejbPassivate(){}<br>&nbsp;&nbsp;&nbsp; public void setSessionContext(SessionContext ctx){}<br>&nbsp;&nbsp;&nbsp; public String getHello() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new String("Hello,EJB");<br>&nbsp;&nbsp;&nbsp; }<br>}</div>
<div>5、创建ejb-jar.xml文件</div>
<div>　　ejb-jar.xml文件是EJB的部署描述文件，包含EJB的各种配置信息，如是有状态Bean（Stateful Bean） 还是无状态Bean（Stateless Bean），交易类型等。ejb-jar.xml文件的详细信息请参阅EJB规范。以下是HelloBean的配置文件：</div>
<div>＜?xml version="1.0"?＞<br>＜!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems Inc.//DTD Enterprise JavaBeans 1.2//EN" "<a href="http://java.sun.com/j2ee/dtds/ejb-jar_1_2.dtd"><font color=#000033><u>http://java.sun.com/j2ee/dtds/ejb-jar_1_2.dtd</u></font></a>"＞<br>＜ejb-jar＞<br>＜enterprise-beans＞<br>＜session＞<br>＜ejb-name＞Hello＜/ejb-name＞<br>＜home＞com.leo.HelloHome＜/home＞<br>＜remote＞com.leo.Hello＜/remote＞<br>＜ejb-class＞com.leo.HelloEJB＜/ejb-class＞<br>＜session-type＞Stateless＜/session-type＞<br>＜transaction-type＞Container＜/transaction-type＞<br>＜/session＞<br>＜/enterprise-beans＞<br>＜/ejb-jar＞<br>&nbsp;</div>
<div><br>6、创建weblogic-ejb-jar.xml</div>
<div>&nbsp;&lt;?xml version="1.0" encoding="UTF-8"?&gt;</div>
<div>&lt;!DOCTYPE weblogic-ejb-jar PUBLIC "-//BEA Systems, Inc.//DTD WebLogic 8.1.0 EJB//EN" "<a href="http://www.bea.com/servers/wls810/dtd/weblogic-ejb-jar.dtd"><font color=#000033><u>http://www.bea.com/servers/wls810/dtd/weblogic-ejb-jar.dtd</u></font></a>"&gt;</div>
<div>&lt;weblogic-ejb-jar&gt;<br>&nbsp;&lt;description&gt;&lt;![CDATA[Generated by XDoclet]]&gt;&lt;/description&gt;<br>&nbsp;&nbsp; &lt;weblogic-enterprise-bean&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ejb-name&gt;Hello&lt;/ejb-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;stateless-session-descriptor&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/stateless-session-descriptor&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;reference-descriptor&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/reference-descriptor&gt;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;jndi-name&gt;Hello&lt;/jndi-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;local-jndi-name&gt;HelloLocal&lt;/local-jndi-name&gt;<br>&nbsp;&nbsp; &lt;/weblogic-enterprise-bean&gt;</div>
<div>&lt;/weblogic-ejb-jar&gt;</div>
<div>7、部署和编译</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EJB的jar包是由class文件和描述文件组成，对于weblogic服务器来说还要增加weblogic-ejb-jar.xml描述文件。编译Java源文并将编译后class和ejb-jar.xml、weblogic-ejb-jar.xml打包到Hello.jar</div>
<div>7.1：创建目录build。</div>
<div>7.2：在build下新建目录META-INF，把文件ejb-jar.xml、weblogic-ejb-jar.xml拷到META-INF下。</div>
<div>7.3：把编译好的class文件拷到build目录下（此时为com/leo/Hello.class,HelloEJB.class,HelloHome.class）。</div>
<div>7.4：打包成jar文件: jar -cvf hello.jar *.* 。</div>
<div>7.5：再将hello.jar文件部署到weblogic服务器中。</div>
<div>8、写客户端调用程序</div>
<div>您可以从Java Client，JSP，Servlet或别的EJB调用HelloBean。<br>调用EJB有以下几个步骤：<br>通过JNDI（Java Naming Directory Interface）得到EJB Home Interface<br>通过EJB Home Interface 创建EJB对象，并得到其Remote Interface<br>通过Remote Interface调用EJB方法</div>
<div>以下是一个从Java Client中调用HelloBean的例子：</div>
<div>package ejb.hello；<br>import javax.naming.Context；<br>import javax.naming.InitialContext；<br>import java.util.Hashtable；<br>import javax.ejb.*；<br>import java.rmi.RmoteException；</div>
<div>/**<br>* @author Copyright （c） 2000 by Apusic, Inc. All Rights Reserved.<br>*/<br>public class HelloClient {<br>&nbsp;&nbsp;&nbsp; public static void main（String args[]）{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String url = "rmi://localhost:7100"；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Context initCtx = null；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HelloHome hellohome = null；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Hashtable env = new Hashtable（）；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; env.put（Context.INITIAL_CONTEXT_FACTORY,"com.apusic.jndi.InitialContextFactory"）；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; env.put（Context.PROVIDER_URL, url）；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; initCtx = new InitialContext（env）；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch（Exception e）{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println（"Cannot get initial context: " + e.getMessage（））；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.exit（1）；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object obj=ctx.lookup("Hello");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HelloHome home=(HelloHome)PortableRemoteObject.narrow(obj,HelloHome.class);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Hello hello = hellohome.create（）；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String s = hello.getHello（）；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println（s）；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }catch（Exception e）{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println（e.getMessage（））；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.exit（1）；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>}</div>
<div>　　运行HelloClient，可得到以下输出：<br>　　Hello EJB</div>
&nbsp;<br>
<img src ="http://www.blogjava.net/gm_jing/aggbug/124907.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2007-06-18 11:43 <a href="http://www.blogjava.net/gm_jing/articles/124907.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ejb事务</title><link>http://www.blogjava.net/gm_jing/articles/108006.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Mon, 02 Apr 2007 08:38:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/108006.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/108006.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/108006.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/108006.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/108006.html</trackback:ping><description><![CDATA[<p><a name=N1012B><span class=atitle>会话 bean 和实体 bean 的事务属性</span></a></p>
<p>会话 bean 和实体 bean 可能的属性值包括：</p>
<ul>
    <li><code>Required</code> —— bean 必须始终运行在事务中。如果客户端已经启动一个事务，则 bean 将加入到事务中。如果客户端还没有启动事务，那么 EJB 容器将启动一个新事务。当需要 bean 始终运行在事务中时，请使用该属性。
    <li><code>RequiresNew</code> —— bean 始终启动一个新的事务。如果客户端已经启动一个事务，则挂起现有事务，直到新事务已提交或中止。在新事务完成之后，现有事务将继续。当需要 bean 作为一个单独的工作单元运行并展示所有的 ACID 属性时，请使用该属性。
    <li><code>Supports</code> —— 如果客户端启动一个事务，则 bean 将加入到事务中。但是，如果事务不存在，EJB 容器不会启动一个新事务。要在企业 bean 上执行非任务关键型操作时，请使用该属性。
    <li><code>Mandatory</code> —— 在调用 bean 时客户端必须启动一个事务。这不会创建一个新的事务。在调用 bean 时，如果没有事务已经启动，则将抛出一个异常。当 bean 是某一较大系统的一部分时，请使用该属性。通常可能由第三方负责启动事务。对用户而言，这是一个安全选项，因为它可以确保 bean 将成为事务的一部分。
    <li><code>NotSupported</code> —— 在事务中不能调用 bean。如果客户端已经启动一个事务，则挂起现有事务，直到 bean 的方法完成。在完成上述方法之后，现有事务将继续。如果客户端没有启动事务，则不会创建一个新事务。在不需要 bean 展示任何 ACID 属性（比如类似报表的非系统关键型操作）时，请使用该属性。
    <li><code>Never</code> —— 如果客户端启动一个事务，则 bean 将抛出一个异常。在您可能永远都不想让您的 bean 参与到事务中的情况下，请使用该属性。 </li>
</ul>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/opensource/os-ag-ejbtrans1/index.html#main"><strong><font color=#996699>回页首</font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N1015B><span class=atitle>消息驱动 bean 的事务属性</span></a></p>
<p>只有两种消息驱动 bean 消息监听器方法使用的事务属性：</p>
<ul>
    <li><code>NotSupported</code> —— bean 不能参与到事务中。如果客户端启动一个事务，那么现有事务将挂起，直到 bean 的方法完成为止。在完成上述方法之后，现有事务将继续。如果客户端没有启动事务，则不会创建一个新的事务。
    <li><code>Required</code> —— bean 必须始终运行在事务中。如果客户端已经启动事务，则 bean 将加入到事务中。如果客户端没有启动事务，则 EJB 容器将启动一个新事务。 </li>
</ul>
<p>在为企业 bean 方法确定正确事务属性之后，就可以配置 EJB 部署描述符了。</p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/opensource/os-ag-ejbtrans1/index.html#main"><strong><font color=#996699>回页首</font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=N10176><span class=atitle>配置 EJB 部署描述符</span></a></p>
<p>对于每个企业 bean，都要在部署描述符中配置事务的下列两个部分：</p>
<ul>
    <li>在 EJB 部署描述符中使用 <code>&lt;transaction-type&gt;</code> 元素指定 bean 使用的是容器管理的事务还是 bean 管理的事务。可能的值是 <code>container</code> 或 <code>bean</code>。由于实体 bean 必须使用容器管理的事务，这只对会话 bean 和消息驱动 bean 是必需的。
    <li>对于容器管理的事务，您可以为企业 bean 的方法随意指定事务属性。在 EJB 部署描述符中的 <code>&lt;container-transaction&gt;</code> 部分指定它。<a href="http://www.ibm.com/developerworks/cn/opensource/os-ag-ejbtrans1/index.html#list1"><u><font color=#996699>清单 1</font></u></a> 中显示了每种方法的通用格式。 </li>
</ul>
<br><a name=list1><strong>清单 1. 每种方法的通用格式</strong></a><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>&lt;method&gt;
            &lt;ejb-name&gt;EJBNAME&lt;/ejb-name&gt;
            &lt;method-name&gt;METHODNAME&lt;/method-name&gt;
            &lt;trans-attribute&gt;TRANSATTRIBUTE&lt;/trans-attribute&gt;
            &lt;/method&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p><code>TRANSATTRIBUTE</code> 可能的值有：</p>
<ul>
    <li><code>NotSupported</code>
    <li><code>Required</code>
    <li><code>Supports</code>
    <li><code>RequiresNew</code>
    <li><code>Mandatory</code>
    <li><code>Never</code> </li>
</ul>
<p>也可以对企业 bean 的所有方法指定事务属性。对 <code>&lt;method-name&gt;</code> 属性使用 <code>*</code>。</p>
<p><a href="http://www.ibm.com/developerworks/cn/opensource/os-ag-ejbtrans1/index.html#list2"><u><font color=#996699>清单 2</font></u></a> 显示了为容器管理的企业 bean 指定事务属性的示例。除了为 <code>updateClaimNumber</code> 方法分配 <code>Mandatory</code> 属性以外，<code>ClaimRecord</code>企业 bean 为所有方法都分配了 <code>Required</code> 属性。<code>Coverage</code> bean 对所有方法指派 <code>RequiresNew</code> 属性。</p>
<br><a name=list2><strong>清单 2. ejb 部署描述符文件中的事务属性</strong></a><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td class=code-outline>
            <pre class=displaycode>&lt;ejb-jar&gt;
            ...
            &lt;assembly-descriptor&gt;
            ...
            &lt;container-transaction&gt;
            &lt;method&gt;
            &lt;ejb-name&gt;ClaimRecord&lt;/ejb-name&gt;
            &lt;method-name&gt;*&lt;/method-name&gt;
            &lt;/method&gt;
            &lt;trans-attribute&gt;<span class=boldcode><strong>Required</strong></span>&lt;/trans-attribute&gt;
            &lt;/container-transaction&gt;
            &lt;container-transaction&gt;
            &lt;method&gt;
            &lt;ejb-name&gt;ClaimRecord&lt;/ejb-name&gt;
            &lt;method-name&gt;updateClaimNumber&lt;/methodname&gt;
            &lt;/method&gt;
            &lt;trans-attribute&gt;<span class=boldcode><strong>Mandatory</strong></span>&lt;/trans-attribute&gt;
            &lt;/container-transaction&gt;
            &lt;container-transaction&gt;
            &lt;method&gt;
            &lt;ejb-name&gt;Coverage&lt;/ejb-name&gt;
            &lt;method-name&gt;*&lt;/method-name&gt;
            &lt;/method&gt;
            &lt;trans-attribute&gt;<span class=boldcode><strong>RequiresNew</strong></span>&lt;/trans-attribute&gt;
            &lt;/container-transaction&gt;
            ...
            &lt;/assembly-descriptor&gt;
            ...
            &lt;/ejb-jar&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<img src ="http://www.blogjava.net/gm_jing/aggbug/108006.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2007-04-02 16:38 <a href="http://www.blogjava.net/gm_jing/articles/108006.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>异常处理</title><link>http://www.blogjava.net/gm_jing/articles/80392.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Fri, 10 Nov 2006 06:37:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/80392.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/80392.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/80392.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/80392.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/80392.html</trackback:ping><description><![CDATA[<p>异常处理</p>
<p>1、web.xml<br />
&nbsp; i.error-code：错误码&nbsp;<br />
&nbsp; &lt;error-page&gt;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;error-code&gt;404&lt;/error-code&gt;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;location&gt;/404.jsp&lt;/location&gt;<br />
&nbsp; &lt;/error-page&gt;<br />
&nbsp;&nbsp;<br />
&nbsp; ii.exception-type<br />
当servlet执行时，可能产生许多异常。而当异常产生时，Web容器将产生一个包含A Servlet Has Occurred消息的缺省页。但是，用户也可返回一个容器，该容器应包含为给定异常指定的错误页。<br />
&nbsp;&nbsp;&lt;error-page&gt;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;exception-type&gt;org.sprngside.bookstore.UserNotFound&lt;/exception-type&gt;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;location&gt;/userNotFound.jsp&lt;/location&gt;<br />
&nbsp;&nbsp;&lt;/error-page&gt;<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;<br />
2、jsp<br />
&nbsp;&lt;@ errorPage="error.jsp"&gt;</p>
<p><br />
3、Spring MVC里的异常控制<br />
&nbsp;&nbsp; spring-mvc可在xxx-serverlet.xml里定义default和 按Excepiton类型影射的错误页面, <br />
&nbsp;&nbsp; 和Servlet规范比,主要作了Spring特色的JSP路径转向和日志记录.参见bookstore-servlet.xml<br />
&nbsp;&nbsp; <br />
&nbsp;&lt;bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="defaultErrorView" value="/error.jsp"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="exceptionMappings"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;props&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="org.springside.framework.base.BusinessException"&gt;/businessError.jsp&lt;/prop&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/props&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&lt;/bean&gt;</p>
<p>&nbsp;</p>
<p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝<br />
&nbsp;&nbsp;error.jsp会同时处理jsp,servlet,和spring抛过来的异常<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;jsp的异常在exception 变量中.（要指定&lt;%@ page isErrorPage="true" %&gt;）<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;spring的异常在(Exception) request.getAttribute("exception")<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;servlet的异常在(Exception)request.getAttribute("javax.servlet.error.exception")<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;使用 (String) request.getAttribute("javax.servlet.error.request_uri")获得 request_uri<br />
&nbsp;&nbsp;使用 logger.error(exception.getMessage(), exception); 记录整个异常栈</p>
<p>&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&#183; javax.servlet.error.exception：实际的异常掷出的Throwable对象<br />
&nbsp;&nbsp;&#183; javax.servlet.error.request_uri：导致问题产生的资源的URI字符串对象<br />
&nbsp;&nbsp;&#183; javax.servlet.error.status_code：错误类型的整形值<br />
&nbsp;&nbsp;&#183; javax.servlet.error.exception_type：产生错误的异常类的实例<br />
&nbsp;&nbsp;&#183; javax.servlet.error.message：异常信息字符串<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;</p>
<p><br />
error.jsp 完整的代码</p>
<p>&lt;%@ page contentType="text/html;charset=UTF-8" isErrorPage="true" %&gt;<br />
&lt;%@ page import="org.apache.commons.logging.LogFactory" %&gt;<br />
&lt;%@ taglib prefix="c" uri="<a href="http://java.sun.com/jsp/jstl/core">http://java.sun.com/jsp/jstl/core</a>" %&gt;<br />
&lt;html&gt;<br />
&lt;head&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;title&gt;Error Page&lt;/title&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;script src="&lt;c:url value="/scripts/prototype.js"/&gt;" type="text/javascript"&gt;&lt;/script&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;script language="javascript"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function showDetail()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $('detail_error_msg').toggle();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; &lt;/script&gt;<br />
&lt;/head&gt;</p>
<p>&lt;body&gt;</p>
<p>&lt;div id="content"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;%<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Exception from JSP didn't log yet ,should log it here.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String requestUri = (String) request.getAttribute("javax.servlet.error.request_uri");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogFactory.getLog(requestUri).error(exception.getMessage(), exception);<br />
&nbsp;&nbsp;&nbsp; %&gt;</p>
<p>&nbsp;&nbsp;&nbsp; &lt;h3&gt;System Runtime Error: &lt;br&gt;&lt;%=exception.getMessage()%&gt;&lt;/h3&gt;&lt;br&gt;</p>
<p>&nbsp;&nbsp;&nbsp; &lt;button onclick="history.back();"&gt;Back&lt;/button&gt;&lt;br&gt;</p>
<p>&nbsp;&nbsp;&nbsp; &lt;p&gt;&lt;a href="#" onclick="showDetail();"&gt;Administrator click here to get the detail.&lt;/a&gt;&lt;/p&gt;</p>
<p>&nbsp;&nbsp;&nbsp; &lt;div id="detail_error_msg" style="display:none"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;pre&gt;&lt;%exception.printStackTrace(new java.io.PrintWriter(out));%&gt;&lt;/pre&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/div&gt;<br />
&lt;/div&gt;<br />
&lt;/body&gt;<br />
&lt;/html&gt;&nbsp;</p>
<img src ="http://www.blogjava.net/gm_jing/aggbug/80392.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2006-11-10 14:37 <a href="http://www.blogjava.net/gm_jing/articles/80392.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>返回上级页面方法</title><link>http://www.blogjava.net/gm_jing/articles/80384.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Fri, 10 Nov 2006 06:22:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/80384.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/80384.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/80384.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/80384.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/80384.html</trackback:ping><description><![CDATA[
		<p>返回上级页面方法</p>
		<p>
				<br />referer是浏览器在用户提交请求当前页面中的一个链接时,将当前页面的URL放在头域中提交给服务端的,<br />如当前页面为a.html,它里面有一个b.html的链接,当用户要访问b.html时浏览器就会把a.html作为referer发给服务端.</p>
		<p>errorpage 根本不是用户去访问的,绝对大多数errorPage是forward,由服务端直接调用http连接访问errorpage将结果发给用户.<br />用户根本没有访问errorPage.</p>
		<p>对于location="aaa.jsp"这样用js访问没有referer,是因为当执行location="aaa.jsp"时,当前页面的URL已经是aaa.jsp,<br />真的要取上一级当然可以从history中取到发给服务端,但那已经不是Referer的本意了..可以以如下代码来代替:<br />function jump(url){<br />    var e = document.createElement("a");<br />    e.href = url;<br />    document.body.appendChild(e);<br />    e.click();<br />}</p>
		<p> </p>
		<p>//JAVA 中<br />String currentURL = request.getRequestURL().toString();<br />String preURL = request.getHeader("referer");</p>
		<p>if (currentURL.equals(preURL) ) {<br /> return new RedirectingActionForward(preURL);<br />} else {<br /> return new RedirectingActionForward(currentURL)<br />}</p>
<img src ="http://www.blogjava.net/gm_jing/aggbug/80384.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2006-11-10 14:22 <a href="http://www.blogjava.net/gm_jing/articles/80384.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于路径</title><link>http://www.blogjava.net/gm_jing/articles/68297.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Thu, 07 Sep 2006 07:16:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/68297.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/68297.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/68297.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/68297.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/68297.html</trackback:ping><description><![CDATA[<p>关于路径</p>
<p>（1）工程路径</p>
<p>（2）类路径<br />
相对于WEB-INF/classes而言的</p>
<p>&nbsp;</p>
<p>---&gt;&gt;&gt;<strong>jsp中的路径</strong><br />
/egl/pages/logistics/trans_provide_info.jsp</p>
<p>egl是工程名，在tomcat的config中配置的<br />
&lt;Context path="/egl" docBase ="E:/work/EGL/public/dev/web" reloadable="true"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;Logger className="org.apache.catalina.logger.FileLogger"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prefix="egl_log." suffix=".txt" timestamp="true"/&gt;<br />
&lt;/Context&gt;<br />
后面的pages/logistics/trans_provide_info.jsp就是E:/work/EGL/public/dev/web下面的文件</p>
<p><br />
---&gt;&gt;&gt;<strong>action的路径</strong><br />
/egl/logistics/trans_provide_info.do</p>
<p>egl是工程名,它查找WEB-INF/classes下的trans_provide_info对应的class，中间<span style="color: red">/logistics</span>是namespace<br />
&lt;package name="logistics" extends="webwork-default" namespace="<span style="color: red">/</span><span style="color: red">logistics</span>"&gt;<br />
&nbsp;&nbsp;&nbsp;&lt;default-interceptor-ref name="myDefaultWebStack"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&lt;action name="trans_provide_info" class="j1.logistics.tp.action.TransProvideAction"&gt;<br />
&nbsp;&nbsp;&nbsp;&lt;result name="success" type="chain"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;param name="actionName"&gt;trans_provide_list&lt;/param&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;param name="namespace"&gt;/logistics&lt;/param&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/result&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; &lt;/action&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ......<br />
/egl/logistics/trans_provide_info.do ＝/egl/WEB-INF/classes/logistics/trans_provide_info.do&nbsp; <br />
</p>
<img src ="http://www.blogjava.net/gm_jing/aggbug/68297.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2006-09-07 15:16 <a href="http://www.blogjava.net/gm_jing/articles/68297.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于taglib</title><link>http://www.blogjava.net/gm_jing/articles/68262.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Thu, 07 Sep 2006 05:53:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/68262.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/68262.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/68262.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/68262.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/68262.html</trackback:ping><description><![CDATA[
		<p>&lt;%@ taglib prefix="****" uri="********"%&gt;</p>
		<p>
				<br />关于taglib<br />（1）在web.xml中定义一个taglib</p>
		<p>&lt;%@ taglib prefix="ww" uri="webwork"%&gt;//uri不符合*.tld里面的uri，但是在web.xml定义了<br />&lt;jsp-config&gt;<br />    &lt;taglib&gt;<br />        &lt;taglib-uri&gt;webwork&lt;/taglib-uri&gt;<br />        &lt;taglib-location&gt;/WEB-INF/taglib.tld&lt;/taglib-location&gt;<br />    &lt;/taglib&gt;<br />    &lt;jsp-property-group&gt;<br />        &lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt;<br />        &lt;page-encoding&gt;gb2312&lt;/page-encoding&gt;<br />    &lt;/jsp-property-group&gt;<br />&lt;/jsp-config&gt;</p>
		<p> </p>
		<p>
				<br />（2）web.xml中没有定义，如果uri符合*.tld里面的uri，默认读取jar/META-INF</p>
		<p>&lt;%@ taglib prefix="c" uri="<a href="http://java.sun.com/jsp/jstl/core">http://java.sun.com/jsp/jstl/core</a>" %&gt;<br />读取standard.jar/META-INF/c.tld</p>
		<p>【注】prefix可以随便定义，只是一个前缀。</p>
<img src ="http://www.blogjava.net/gm_jing/aggbug/68262.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2006-09-07 13:53 <a href="http://www.blogjava.net/gm_jing/articles/68262.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jsp2.0 学习-jstl</title><link>http://www.blogjava.net/gm_jing/articles/65642.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Thu, 24 Aug 2006 15:07:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/65642.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/65642.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/65642.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/65642.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/65642.html</trackback:ping><description><![CDATA[
		<p>jstl准备<br />1、jstl.jar和standard.jar<br />2、对应的tld文件<br />3、修改web.xml</p>
		<p>&lt;jsp-config&gt;<br />        &lt;taglib&gt;<br />            &lt;taglib-uri&gt;/WEB-INF/c.tld&lt;/taglib-uri&gt;<br />            &lt;taglib-location&gt;/WEB-INF/c.tld&lt;/taglib-location&gt;<br />        &lt;/taglib&gt;<br />        &lt;jsp-property-group&gt;<br />            &lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt;<br />            &lt;page-encoding&gt;UTF-8&lt;/page-encoding&gt;<br />        &lt;/jsp-property-group&gt;<br />    &lt;/jsp-config&gt;<br /><br /><br />&lt;%@ taglib prefix="c" uri="<a href="http://java.sun.com/jsp/jstl/core">http://java.sun.com/jsp/jstl/core</a>" %&gt;<br /></p>
		<hr />
		<br />(1)表达式操作<br />● c:out<br />&lt;c:out value="value" [escapeXml="{true|false}"] [default="defaultValue"] /&gt; 
<p></p><p><br />● c:set<br />语法1：将 value的值储存至范围为scope的 varName 变量之中<br />&lt;c:set value="value" var="varName" [scope="{ page|request|session|application }"]/&gt;<br />语法2：将本体内容的数据储存至范围为scope的 varName 变量之中<br />&lt;c:set var="varName" [scope="{ page|request|session|application }"]&gt;… 本体内容&lt;/c:set&gt;<br />语法3：将 value的值储存至 target 对象的属性中<br />&lt;c:set value="value" target="target" property="propertyName" /&gt;<br />语法4：将本体内容的数据储存至 target 对象的属性中<br />&lt;c:set target="target" property="propertyName"&gt;… 本体内容&lt;/c:set&gt;</p><p><br />● &lt;c:remove&gt;<br />&lt;c:remove var="varName" [scope="{ page|request|session|application }"] /&gt;</p><p> </p><p>● &lt;c:catch&gt;<br />&lt;c:catch [var="varName"] &gt;… 欲抓取错误的部分&lt;/c:catch&gt;</p><p> </p><p>● &lt;c:if&gt;<br />语法1：没有本体内容(body)<br />&lt;c:if test="testCondition" var="varName" [scope="{page|request|session|application}"]/&gt;<br />语法2：有本体内容<br />&lt;c:if test="testCondition" [var="varName"] [scope="{page|request|session|application}"]&gt;本体内容&lt;/c:if&gt;</p><p> </p><p>● &lt;c:choose&gt;<br />&lt;c:choose&gt;<br /> ：<br /> &lt;c:when&gt;<br /> &lt;/c:when&gt;<br /> ：<br /> &lt;c:otherwise&gt;<br /> &lt;/c:otherwise&gt;<br /> ：<br />&lt;/c:choose&gt;</p><p> </p><p>● &lt;c:when&gt;<br />&lt;c:when test="testCondition" &gt;本体内容&lt;/c:when&gt;</p><p> </p><p>● &lt;c:otherwise&gt;<br />&lt;c:otherwise&gt;本体内容&lt;/c:otherwise&gt;</p><p><br />(2)迭代操作<br />● &lt;c:forEach&gt;<br />语法1：迭代一集合对象之所有成员<br />&lt;c:forEach [var="varName"] items="collection" [varStatus="varStatusName"]<br />[begin="begin"] [end="end"] [step="step"]&gt;<br />本体内容<br />&lt;<br />/c:forEach&gt;<br />语法2：迭代指定的次数<br />&lt;c:forEach [var="varName"] [varStatus="varStatusName"] begin="begin" end="end" [step="step"]&gt;<br />本体内容<br />&lt;/c:forEach&gt;</p><p> </p><p>(3)URL操作<br /><br /></p><img src ="http://www.blogjava.net/gm_jing/aggbug/65642.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2006-08-24 23:07 <a href="http://www.blogjava.net/gm_jing/articles/65642.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jsp2.0 学习-Jsp 2.0 的新功能</title><link>http://www.blogjava.net/gm_jing/articles/65635.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Thu, 24 Aug 2006 13:53:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/65635.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/65635.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/65635.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/65635.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/65635.html</trackback:ping><description><![CDATA[
		<p>
				<font color="#ff1493" size="5">
						<strong>Jsp 2.0 的新功能</strong>
				</font>
		</p>
		<p>
				<font color="#0000ff" size="4">(1) Expression Language</font>
				<br />JSP 2.0 之后，正式将EL 纳入JSP 的标准语法。EL 主要的功用在于简化JSP 的语法，方便Web<br />开发人员的使用。例如:count：${param.count + 5}</p>
		<p>
				<font color="#0000ff" size="4">(2) 新增Simple Tag 和Tag File</font>
		</p>
		<p>
				<font color="#0000ff">(</font>
				<font color="#0000ff" size="4">
						<font color="#0000ff">3</font>) web.xml 新增&lt;jsp-config&gt;元素</font>
		</p>
		<p>
				<br />
				<br />
		</p>
		<hr />
		<strong>
				<font color="#800080">EL 语法</font>
				<br />
		</strong>1 .与[ ] 运算符<br />${sessionScope.user.sex}=${sessionScope.user["sex"]}<br />${sessionScope.shoppingCart[0].price}<br />注意：${expr-a[expr-b]} 
<p></p><p>2  EL 变量<br />${pageScope.username}...<br /><br /><br />3 自动转变类型<br />${param.count + 20}<br /></p><p>4 EL 隐含对象<br />         隐含对象                   类 型                                              说 明<br />     PageContext       javax.servlet.ServletContext       表示此JSP 的PageContext<br />     PageScope          java.util.Map                       取得Page 范围的属性名称所对应的值<br />     RequestScope     java.util.Map                   取得Request 范围的属性名称所对应的值<br />     sessionScope      java.util.Map                    取得Session 范围的属性名称所对应的值<br />     applicationScope java.util.Map            取得Application 范围的属性名称所对应的值<br />     param                  java.util.Map        如同ServletRequest.getParameter(String name)。<br />                                                                回传String 类型的值</p><p>   paramValues         java.util.Map       如同ServletRequest.getParameterValues(String<br />                                                               name)。回传String []类型的值<br />   header                    java.util.Map      如同ServletRequest.getHeader(String name)。<br />                                                              回传String 类型的值<br />   headerValues        java.util.Map       如同ServletRequest.getHeaders(String name)。<br />                                                              回传String []类型的值<br />  cookie                    java.util.Map       如同HttpServletRequest.getCookies( )<br />  initParam               java.util.Map       如同ServletContext.getInitParameter(String<br />                                                               name)。回传String 类型的值<br /><br /><br /><br />5 EL 算术运算符/关系运算符/逻辑运算符</p><p><br />6 其他运算符<br />(1) Empty 运算符<br />(2) 条件运算符<br />(3) ( ) 括号运算符<br /></p><img src ="http://www.blogjava.net/gm_jing/aggbug/65635.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2006-08-24 21:53 <a href="http://www.blogjava.net/gm_jing/articles/65635.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jms学习</title><link>http://www.blogjava.net/gm_jing/articles/64101.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Thu, 17 Aug 2006 05:44:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/64101.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/64101.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/64101.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/64101.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/64101.html</trackback:ping><description><![CDATA[<p>1、一个Queue时候,Sender和Receiver使用同一个Queue<br />
[Sender]<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; QueueSession queueSession = queueConnection.createQueueSession(transacted, acknowledgementMode);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; QueueSender queueSender = queueSession.createSender(queue);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TextMessage textMessage = queueSession.createTextMessage();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; textMessage.clearBody();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; textMessage.setText(message);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;queueSender.send(textMessage);</p>
<p>[Receiver]<br />
&nbsp;&nbsp;&nbsp;QueueSession queueSession = queueConnection.createQueueSession(transacted, acknowledgementMode);<br />
&nbsp;&nbsp;&nbsp;QueueReceiver queueReceiver = queueSession.createReceiver(queue);&nbsp;&nbsp;&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;queueReceiver.setMessageListener(msgRcvr);<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp; //msgRcvr--&gt;MessageListener的onMessage方法<br />
&nbsp;&nbsp;&nbsp;public synchronized void&nbsp; onMessage(Message message) {<br />
&nbsp;&nbsp;&nbsp;if (message instanceof TextMessage) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TextMessage textMessage = (TextMessage) message;<br />
&nbsp;&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("Message content is:" + textMessage.getText());<br />
&nbsp;&nbsp;&nbsp;&nbsp;}catch (JMSException e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}</p>
<p>&nbsp;</p>
<hr />
<br />
2、两个Queue时候<br />
&nbsp;&nbsp; a.可以使用两个Queue<br />
&nbsp;&nbsp; b.可以是一个普通的Queue,一个TemporaryQueue<br />
&nbsp;&nbsp; <br />
[Sender]<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; QueueSession queueSession = queueConnection.createQueueSession(transacted, acknowledgementMode);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Queue tempQueue = queueSession.createTemporaryQueue();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String cID = (int)(Math.random()*10)+"";<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TextMessage textMessage = queueSession.createTextMessage();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#ff1493">textMessage.setJMSCorrelationID(cID);<br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; textMessage.setJMSReplyTo(tempQueue); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; textMessage.setText(message);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; QueueSender queueSender = queueSession.createSender(senderQueue);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; queueSender.send(textMessage);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#ff1493">QueueReceiver queuereceiver = queueSession.createReceiver(tempQueue,"JMSCorrelationID='" + cID +"'"); <br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Message msg = queuereceiver.receive(5000);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (msg instanceof TextMessage) {<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; System.out.println("RETURN Message:" + ((TextMessage)msg).getText());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
<p><br />
[Receiver]<br />
&nbsp;&nbsp;&nbsp;public synchronized void&nbsp; onMessage(Message message) {<br />
&nbsp;&nbsp;&nbsp;if (message instanceof TextMessage) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TextMessage oldMessage = (TextMessage) message;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Queue tempQueue = (Queue)oldMessage.getJMSReplyTo();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;queueSender = queueSession.createSender(tempQueue);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TextMessage newMessage = queueSession.createTextMessage();&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#ff1493">newMessage.setJMSCorrelationID(oldMessage.getJMSCorrelationID());<br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;newMessage.setText("test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;queueSender.send(newMessage);</p>
<p>&nbsp;&nbsp;}</p>
<p>&nbsp;</p>
<p><br />
【注】<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1)JMSCorrelationID 比较关键，只是一个身分标识，上面有3个地方使用<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2)Template&nbsp;用于临时template中，她的设置影响&nbsp;cID<br />
如果String cID = (Math.random()*10) + ""; <font color="#0000ff">抛出&#8220;Byte quota exceeded for...&#8221;于template 设置有关</font><br />
</p>
<img src ="http://www.blogjava.net/gm_jing/aggbug/64101.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2006-08-17 13:44 <a href="http://www.blogjava.net/gm_jing/articles/64101.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jsp 一些总结</title><link>http://www.blogjava.net/gm_jing/articles/59007.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Wed, 19 Jul 2006 07:59:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/59007.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/59007.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/59007.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/59007.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/59007.html</trackback:ping><description><![CDATA[
		<br />jsp:param <br />&lt;jsp:include page="{urlSpec | &lt;%= expression %&gt;}" flush="true | false" &gt;<br />   &lt;jsp:param name="PN" value="{PV | &lt;%= expression %&gt;}" /&gt; *<br />&lt;/jsp:include&gt;<br />include的页面用request.getParameter("PN");得到<br /><br /><br />exception 对象<br />exception 对象和session 对象一样，并不是在每一个JSP 网页中都能够使用。若要使用exception 对象时，必须在page 指令中设定。<br />&lt;%@ page isErrorPage="true" %&gt;<br />三个方法：getMessage( )、getLocalizedMessage( )、printStackTrace(new java.io.PrintWriter(out)) ，其中printStackTrace( )的参数要为PrintWriter 而不是JspWriter。<br /><img src ="http://www.blogjava.net/gm_jing/aggbug/59007.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2006-07-19 15:59 <a href="http://www.blogjava.net/gm_jing/articles/59007.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jsp2.0 学习-Servlet 2.4 的新功能</title><link>http://www.blogjava.net/gm_jing/articles/58861.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Tue, 18 Jul 2006 14:24:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/58861.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/58861.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/58861.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/58861.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/58861.html</trackback:ping><description><![CDATA[
		<div class="postcontent">
				<p align="left">
						<font size="4">
								<font color="#ff1493" size="5">
										<strong>Servlet 2.4 的新功能</strong>
								</font>
								<br />
								<br />
								<font color="#0000ff">1.</font>
								<font color="#0000ff">web.xml DTD 改用XML Schema,</font>
								<font color="#0000ff">主要加强两项功能：</font>
						</font>
						<br />
						<font size="2">
								<font color="#000000">(1) 元素可不依照顺序设定；<br />(2) 更强大的验证机制。<br /></font>   a. 可检查元素的值是否为合法的值。例如：&lt;filter-mapping&gt;的&lt;dispatcher&gt;元素，其值只<br />能为REQUEST、FORWARD、INCLUDE 和ERROR</font>
						<br />
						<font size="2">   b.可检查如Servlet、Filter 或EJB-ref 等等元素的名称是否惟一<br />   c.可检查元素值是否为合法文字字符或数字字符</font>
						<br />
				</p>
				<p align="left">
						<font size="2">
								<font color="#0000ff" size="4">2.新增Filter 四种设定；</font>
								<br />filter-mapping--&gt;dispatcher--&gt;<br /> 四种设定为：REQUEST、FORWARD、INCLUDE 和ERROR。<br />FORWARD: RequestDispatcher.forward()   REQUEST: 由客户端发出请求 <br />INCLUDE: RequestDispatcher.include()   ERROR:</font>
				</p>
				<p align="left">
						<font color="#0000ff">
								<font size="4">3. 新增Request Listener、Event 和Request Attribute Listener、Event</font>
								<br />
						</font> <br /><font color="#0000ff"><font size="4">4. Servlet 2.4 其他较显著的变更如</font>：</font><br /> a. <br />    取消SingleThreadModel接口。当Servlet实现SingleThreadModel 接口时，它能确保同时间内，只能有一个thread 执行此Servlet。<br /> b. <br />&lt;welcome-file-list&gt;可以为Servlet。<br /> &lt;servlet&gt;<br />  &lt;servlet-name&gt;Index&lt;/servlet-name&gt;<br />  &lt;servlet-class&gt;tw.com.javaworld.IndexServlet&lt;/servlet-class&gt;<br /> &lt;/servlet&gt;<br /> ......<br /> &lt;welcome-file-list&gt;<br />  &lt;welcome-file&gt;Index&lt;/welcome-file&gt;<br /> &lt;/welcome-file-list&gt;<br /> c.<br />   ServletRequest 接口新增一些方法，如：<br />  public String getLocalName( );<br />  public String getLocalAddr( );<br />  public int getLocalPort( );<br />  public int getRemotePort( );<br /><br /></p>
		</div>
<img src ="http://www.blogjava.net/gm_jing/aggbug/58861.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2006-07-18 22:24 <a href="http://www.blogjava.net/gm_jing/articles/58861.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>客户层的一些技巧</title><link>http://www.blogjava.net/gm_jing/articles/58860.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Tue, 18 Jul 2006 14:23:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/58860.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/58860.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/58860.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/58860.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/58860.html</trackback:ping><description><![CDATA[<p><font color="#0000ff" size="4">1.如何得到servletContext</font> <br />
<br />
a.HttpServlet<br />
this.getServletConfig().<font color="#c60a00">getServletContext</font>();<br />
b.Jsp<br />
<font size="2"><font size="3">pageContext.<font color="#c60a00">getServletConfig</font>(); <br />
<br />
c.Action<br />
覆盖setServlet<br />
public void setServlet(ActionServlet actionServlet) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super.setServlet(actionServlet);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServletContext servletContext = actionServlet.getServletContext();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//WebApplicationContext wac = WebApplicationContextUtils.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getRequiredWebApplicationContext(servletContext);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//this.orderService = (IOrderService) wac.getBean("orderService");<br />
&nbsp;}<br />
<br />
</font><br />
<br />
<font color="#0000ff" size="4">2.RequestDispatcher 出现的问题</font><br />
<br />
</font>hello.jsp 中<br />
&nbsp;&nbsp; &lt;form name="theForm" action="/myWebApp/hello.jsp" method="get"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;input type="text" name="userName" value="test"&gt; &lt;br /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;input type="test" nam="password" value="123"&gt; &lt;br /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;input type="submit" value="SUBMIT"&gt;<br />
&nbsp;&nbsp;&nbsp;&lt;/form&gt;<br />
&nbsp;&nbsp;&nbsp;&lt;jsp:include page="/handleHttpServlet"/&gt;<br />
<br />
<br />
handleHttpServlet中<br />
&nbsp; request.setAttribute("carriers", carriers);<br />
&nbsp;&nbsp;RequestDispatcher rd = request.getRequestDispatcher("/info.jsp");<br />
&nbsp;&nbsp;rd.forward(request, response);<br />
&nbsp;&nbsp;//rd.include(request, response);<br />
<br />
<font style="background-color: #ffff00">上例中hello.jsp的内容,变成了info.jsp,本来应该include,因为servlet中采用了forward,<br />
改正应该变成rd.include(request, response);</font><br />
</p>
<img src ="http://www.blogjava.net/gm_jing/aggbug/58860.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2006-07-18 22:23 <a href="http://www.blogjava.net/gm_jing/articles/58860.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OSCache 学习</title><link>http://www.blogjava.net/gm_jing/articles/58859.html</link><dc:creator>黎夕</dc:creator><author>黎夕</author><pubDate>Tue, 18 Jul 2006 14:22:00 GMT</pubDate><guid>http://www.blogjava.net/gm_jing/articles/58859.html</guid><wfw:comment>http://www.blogjava.net/gm_jing/comments/58859.html</wfw:comment><comments>http://www.blogjava.net/gm_jing/articles/58859.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gm_jing/comments/commentRss/58859.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gm_jing/services/trackbacks/58859.html</trackback:ping><description><![CDATA[
		<div class="postcontent">
				<p>一、Cache介绍<br />Cache是一种用于提高系统响应速度、改善系统运行性能的技术。尤其是在Web应用中，通过缓存页面的输出结果，可以很显著的改善系统运行性能。<br /></p>
				<p>例如：电子商务中关于送货区域，统计报表<br />1.1 问题的共同点<br />1、被处理的内容短时间不变，所以短时间内可以作为静态内容进行处理<br />2、在一个不太长的时间内，被处理内容可能或者必定产生变化，所以必须将他们作为动态内容进行处理<br />3、在合理的时间区段内可以忽略被处理内容变化后带来的影响<br />4、对这些内容的处理动作比较消耗系统性能，影响系统响应时间</p>
				<p>1.2 解决方法<br />1、缓存信息<br />      当上述的基础数据或者统计报表第一次被访问时，被处理的内容被当作动态信息，基础数库从数据库中获得，统计报表也会被生成符合要求的图形、文件，然后这些信息都会被放入缓存信息中。</p>
				<p>2、响应信息由缓存提供<br />      当上述的基础数据或者统计报表继续被访问时，系统将会<font style="BACKGROUND-COLOR: #ffff00">首先检查缓存信息中是否有对应的内容和我们设定的缓存规则</font>，如果符合缓存信息存在而且符合缓存规则，给出的响应将来自于缓存信息，如果没有或者缓存信息已经不符合设定的要求，系统将重复上一步的动作。</p>
				<p>很显然，上面的步骤2中，多数情况下，当用户请求到达时，被处理的内容将来自于缓存，所以大大的减少了与数据库的交互，或者不再需要为每个请求都生成一次报表图形或者文件，这部分工作的减少对于降低系统性能消耗、提高系统稳定性和并发处理能力是非常有益的。</p>
				<p> </p>
				<p>二、OSCache介绍<br />OSCache中按照缓存范围的不同分为两种不同的方式：一种是缓存JSP页面中部分或者全部内容，一种是基于整个页面文件的缓存。<br /><br />2.1 Cache-OSCache提供的缓存JSP页面中部分或者全部内容标签<br /><br />这是OSCache提供的标签库中最重要的一个标签，包括在标签中的内容将应用缓存机制进行处理，处理的方式将取决于编程者对cache标签属性的设置。</p>
				<p>第一次请求到达时，标签中的内容被处理并且缓存起来，当下一个请求到达时，缓存系统会检查这部分内容的缓存是否已经失效，主要是以下几项：</p>
				<p>1. 缓存时间超过了cache标签设置的time或者duration属性规定的超时时间 <br />2. cron属性规定的时间比缓存信息的开始时间更晚 <br />3. 标签中缓存的内容在缓存后又被重新刷新过 <br />4. 其他缓存超期设定 <br />如果符合上面四项中的任何一项，被缓存的内容视为已经失效，这时被缓存的内容将被重新处理并且返回处理过后的信息，如果被缓存的内容没有失效，那么返回给用户的将是缓存中的信息。<br /><br /></p>
				<p>cache标签的属性说明:</p>
				<p>
						<font color="#000000">
								<b>key</b> - 标识缓存内容的关键词。在指定的作用范围内必须是唯一的。默认的key是被访问页面的URI和后面的请求字符串。</font> 你可以在同一个页面中使用很多cache标签而不指定他的key属性，这种情况下系统使用该页面的URI和后面的请求字符串，另外再自动给这些key增加一个索引值来区分这些缓存内容。但是不推荐采用这样的方式。</p>
				<p>
						<font color="#000000">
								<b>scope</b> - 缓存发生作用的范围，可以是application或者session<br /></font>
						<font color="#000000">
								<b>time</b> - 缓存内容的时间段，单位是秒，默认是3600秒，也就是一个小时，如果设定一个负值，那么这部分被缓存的内容将永远不过期。 </font>
						<font color="#000000">
								<b>
										<br />duration</b> - 指定缓存内容失效的时间，是相对time的另一个选择，可以使用简单日期格式或符合USO-8601的日期格式。如：duration='PT5M' duration='5s'等<br /></font>
						<font color="#000000">
								<b>refresh</b> - false 或者true。</font>如果refresh属性设置为true，不管其他的属性是否符合条件，这部分被缓存的内容都将被更新，这给编程者一种选择，决定什么时候必须刷新。 <br /><font color="#000000"><b>mode</b> - 如果编程者不希望被缓存的内容增加到给用户的响应中，可以设置mode属性为"silent"</font></p>
				<p>
						<strong>cron</strong>- Cron表达式包括5个字段分别为Minute,Hour,DOM(Day Of Month),Month,DOW(Day Of Week)。<br />     <font size="2">他们顺序地对应了5个位置。当某个位置上的值为*时,表示该位置上的任意时间。<br />       另外还提供了指定时间的操作符号"-"，","，"/"，他们分别表示一段时间范围，具体的时间，以及递增的时间段。<br />       下面是几个例子说明一下Cron表达式的基本应用，有兴趣的也可以查看下OSCache的文档。</font></p>
				<div>
						<font size="2">        "10/20 * * * *" :因是第一个位置，并且是一个递增的表达式,所以表达式指定的是每个小时的第10分钟，<br />         第30分钟,第50分钟缓存内容失效。</font>
				</div>
				<div>
						<font size="2">         "* 8-18/4 * * *" :指定每天早上8点到晚上6点之间，每4个小时缓存内容失效。等同于"* 8,12,16 * * *"</font>
				</div>
				<div>
						<font size="2">        "* * * * 1-5":表示每个星期一到星期五内容失效。<br /></font>
						<br />其它可用的属性还包括：groups、language、refreshpolicyclass、refreshpolicyparam。<br />上面的这些属性可以单独使用，也可以根据需要组合使用，下面的例子将讲解这些常用属性的使用方式。</div>
				<p>
						<br />2.2 用CashFilter实现页面级缓存<br /></p>
				<p>在OSCache组件中提供了一个CacheFilter用于实现页面级的缓存，主要用于对web应用中的某些动态页面进行缓存，尤其是那些需要生成pdf格式文件/报表、图片文件等的页面，不仅减少了数据库的交互、减少数据库服务器的压力，而且对于减少web服务器的性能消耗有很显著的效果。</p>
				<p>这种功能的实现是通过在web.xml中进行配置来决定缓存哪一个或者一组页面，而且还可以设置缓存的相关属性，这种基于配置文件的实现方式对于J2EE来说应该是一种标准的实现方式了。</p>
				<pre>&lt;filter&gt;
      &lt;filter-name&gt;CacheFilter&lt;/filter-name&gt;
      &lt;filter-class&gt;com.opensymphony.oscache.web.filter.CacheFilter&lt;/filter-class&gt;<br />      &lt;init-param&gt;
         &lt;param-name&gt;time&lt;/param-name&gt;
         &lt;param-value&gt;600&lt;/param-value&gt;
      &lt;/init-param&gt;
      &lt;init-param&gt;
         &lt;param-name&gt;scope&lt;/param-name&gt;
         &lt;param-value&gt;session&lt;/param-value&gt;
      &lt;/init-param&gt;
&lt;/filter&gt;
&lt;filter-mapping&gt;
&lt;filter-name&gt;CacheFilter&lt;/filter-name&gt;
&lt;!-对/testContent.jsp页面内容进行缓存--&gt;
      &lt;url-pattern&gt;/testContent.jsp&lt;/url-pattern&gt;<br />&lt;!-对所有jsp页面内容进行缓存--&gt;
      &lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt;
&lt;/filter-mapping&gt;
</pre>
				<p>
						<br />
						<br />参考：<br />1. <a href="http://www.zhanglihai.com/blog/c_67_java_log4j_ant_Hibernate_apache.html"><font color="#366900">应用OSCache提升J2EE系统运行性能</font></a><br />2、oscache源代码</p>
		</div>
<img src ="http://www.blogjava.net/gm_jing/aggbug/58859.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gm_jing/" target="_blank">黎夕</a> 2006-07-18 22:22 <a href="http://www.blogjava.net/gm_jing/articles/58859.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>