﻿<?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-ASONG-文章分类-JAVA</title><link>http://www.blogjava.net/ASONG/category/11502.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 15 Jan 2008 08:16:10 GMT</lastBuildDate><pubDate>Tue, 15 Jan 2008 08:16:10 GMT</pubDate><ttl>60</ttl><item><title>Session机制详解(jsp-servlet 技术)(转载)</title><link>http://www.blogjava.net/ASONG/articles/175485.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Tue, 15 Jan 2008 08:06:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/175485.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/175485.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/175485.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/175485.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/175485.html</trackback:ping><description><![CDATA[虽然session机制在web应用程序中被采用已经很长时间了，但是仍然有很多人不清楚session机制的本质，以至不能正确的应用这一技术。本文将详细讨论session的工作机制并且对在Java web application中应用session机制时常见的问题作出解答。<br />
<br />
目录：<br />
一、术语session<br />
二、HTTP协议与状态保持<br />
三、理解cookie机制<br />
四、理解session机制<br />
五、理解javax.servlet.http.HttpSession<br />
六、HttpSession常见问题<br />
七、跨应用程序的session共享<br />
八、总结<br />
参考文档<br />
<br />
一、术语session<br />
在我的经验里，session这个词被滥用的程度大概仅次于transaction，更加有趣的是transaction与session在某些语境下的含义是相同的。<br />
<br />
session，中文经常翻译为会话，其本来的含义是指有始有终的一系列动作/消息，比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个 session。有时候我们可以看到这样的话&#8220;在一个浏览器会话期间，...&#8221;，这里的会话一词用的就是其本义，是指从一个浏览器窗口打开到关闭这个期间 ①。最混乱的是&#8220;用户（客户端）在一次会话期间&#8221;这样一句话，它可能指用户的一系列动作（一般情况下是同某个具体目的相关的一系列动作，比如从登录到选购商品到结账登出这样一个网上购物的过程，有时候也被称为一个transaction），然而有时候也可能仅仅是指一次连接，也有可能是指含义①，其中的差别只能靠上下文来推断②。<br />
<br />
然而当session一词与网络协议相关联时，它又往往隐含了&#8220;面向连接&#8221;和/或&#8220;保持状态&#8221;这样两个含义， &#8220;面向连接&#8221;指的是在通信双方在通信之前要先建立一个通信的渠道，比如打电话，直到对方接了电话通信才能开始，与此相对的是写信，在你把信发出去的时候你并不能确认对方的地址是否正确，通信渠道不一定能建立，但对发信人来说，通信已经开始了。&#8220;保持状态&#8221;则是指通信的一方能够把一系列的消息关联起来，使得消息之间可以互相依赖，比如一个服务员能够认出再次光临的老顾客并且记得上次这个顾客还欠店里一块钱。这一类的例子有&#8220;一个TCP session&#8221;或者 &#8220;一个POP3 session&#8221;③。<br />
<br />
而到了web服务器蓬勃发展的时代，session在web开发语境下的语义又有了新的扩展，<font style="display: none">5vD的|[xHfQw&amp;O育r</font>它的含义是指一类用来在客户端与服务器之间保持状态的解决方案④。有时候session也用来指这种解决方案的存储结构，如&#8220;把xxx保存在session 里&#8221;⑤。由于各种用于web开发的语言在一定程度上都提供了对这种解决方案的支持，所以在某种特定语言的语境下，session也被用来指代该语言的解决方案，比如经常把Java里提供的javax.servlet.http.HttpSession简称为session⑥。<br />
<br />
鉴于这种混乱已不可改变，本文中session一词的运用也会根据上下文有不同的含义，请大家注意分辨。<br />
在本文中，使用中文&#8220;浏览器会话期间&#8221;来表达含义①，使用&#8220;session机制&#8221;来表达含义④，使用&#8220;session&#8221;表达含义⑤，使用具体的&#8220;HttpSession&#8221;来表达含义⑥<br />
<br />
二、HTTP协议与状态保持<br />
HTTP 协议本身是无状态的，这与HTTP协议本来的目的是相符的，客户端只需要简单的向服务器请求下载某些文件，无论是客户端还是服务器都没有必要纪录彼此过去的行为，每一次请求之间都是独立的，好比一个顾客和一个自动售货机或者一个普通的（非会员制）大卖场之间的关系一样。<br />
<br />
然而聪明（或者贪心？）的人们很快发现如果能够提供一些按需生成的动态信息会使web变得更加有用，就像给有线电视加上点播功能一样。这种需求一方面迫使HTML逐步添加了表单、脚本、DOM等客户端行为，另一方面在服务器端则出现了CGI规范以响应客户端的动态请求，作为传输载体的HTTP协议也添加了文件上载、 cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的session机制则是又一种在客户端与服务器之间保持状态的解决方案。<br />
<br />
让我们用几个例子来描述一下cookie和session机制之间的区别与联系。笔者曾经常去的一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠，<font style="display: none">wWA软Ai1ncuVYO中M软+b</font>然而一次性消费5杯咖啡的机会微乎其微，这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案：<br />
1、该店的店员很厉害，能记住每位顾客的消费数量，只要顾客一走进咖啡店，店员就知道该怎么对待了。这种做法就是协议本身支持状态。<br />
2、发给顾客一张卡片，上面记录着消费的数量，一般还有个有效期限。每次消费时，如果顾客出示这张卡片，则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。<br />
3、发给顾客一张会员卡，除了卡号之外什么信息也不纪录，每次消费时，如果顾客出示该卡片，则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。<br />
<br />
由于HTTP协议是无状态的，而出于种种考虑也不希望使之成为有状态的，因此，后面两种方案就成为现实的选择。具体来说cookie机制采用的是在客户端保持状态的方案，而session机制采用的是在服务器端保持状态的方案。同时我们也看到，由于采用服务器端保持状态的方案在客户端也需要保存一个标识，所以session机制可能需要借助于cookie机制来达到保存标识的目的，但实际上它还有其他选择。<br />
<br />
三、理解cookie机制 <br />
cookie机制的基本原理就如上面的例子一样简单，但是还有几个问题需要解决：&#8220;会员卡&#8221;如何分发；&#8220;会员卡&#8221;的内容；以及客户如何使用&#8220;会员卡&#8221;。<br />
<br />
正统的cookie分发是通过扩展HTTP协议来实现的，服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie。<br />
<br />
而cookie 的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie，如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置，则把该cookie附在请求资源的HTTP请求头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出示，如果某家分店还发行了自己的会员卡，那么进这家店的时候除了要出示麦当劳的会员卡，还要出示这家店的会员卡。<br />
<br />
cookie的内容主要包括：名字，值，过期时间，路径和域。<br />
其中域可以指定某一个域比如.google.com，相当于总店招牌，比如宝洁公司，也可以指定一个域下的具体某台机器比如www.google.com或者froogle.google.com，可以用飘柔来做比。<br />
路径就是跟在域名后面的URL路径，比如/或者/foo等等，可以用某飘柔专柜做比。<br />
路径与域合在一起就构成了cookie的作用范围。<br />
如果不设置过期时间，则表示这个cookie的生命期为浏览器会话期间，只要关闭浏览器窗口，cookie就消失了。这种生命期为浏览器会话期的 cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里，当然这种行为并不是规范规定的。如果设置了过期时间，浏览器就会把cookie保存到硬盘上，关闭后再次打开浏览器，这些cookie仍然有效直到超过设定的过期时间。<br />
<br />
存储在硬盘上的cookie 可以在不同的浏览器进程间共享，比如两个IE窗口。而对于保存在内存里的cookie，不同的浏览器有不同的处理方式。对于IE，在一个打开的窗口上按 Ctrl-N（或者从文件菜单）打开的窗口可以与原窗口共享，而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存cookie；对于 Mozilla Firefox0.8，所有的进程和标签页都可以共享同样的cookie。一般来说是用javascript的window.open打开的窗口会与原窗口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用session机制的web应用程序开发者造成很大的困扰。<br />
<br />
下面就是一个goolge设置cookie的响应头的例子<br />
HTTP/1.1 302 Found<br />
Location: <a href="http://www.google.com/intl/zh-CN/"></a><a href="http://www.google.com/intl/zh-CN/" target="_blank">http://www.google.com/intl/zh-CN/</a><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<br />
<br />
<br />
<br />
<br />
这是使用HTTPLook这个HTTP Sniffer软件来俘获的HTTP通讯纪录的一部分<br />
<br />
<br />
<br />
<br />
浏览器在再次访问goolge的资源时自动向外发送cookie<br />
<br />
<br />
<br />
<br />
使用Firefox可以很容易的观察现有的cookie的值<br />
使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。<br />
<br />
<br />
<br />
<br />
IE也可以设置在接受cookie前询问<br />
<br />
<br />
<br />
<br />
这是一个询问接受cookie的对话框。<br />
<br />
四、理解session机制<br />
session机制是一种服务器端的机制，服务器使用一种类似于散列表的结构（也可能就是使用散列表）来保存信息。<br />
<br />
当程序需要为某个客户端的请求创建一个session的时候，服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为 session id，如果已包含一个session id则说明以前已经为此客户端创建过session，服务器就按照session id把这个 session检索出来使用（如果检索不到，可能会新建一个），如果客户端请求不包含session id，则为此客户端创建一个session并且生成一个与此session相关联的session id，
<p style="display: none">M无垠专~IJ软育7@</p>
session id的值应该是一个既不会重复，又不容易被找到规律以仿造的字符串，这个 session id将被在本次响应中返回给客户端保存。<br />
<br />
保存这个session id的方式可以采用cookie，这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID，而。比如weblogic对于web应用程序生成的cookie，JSESSIONID= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764，它的名字就是 JSESSIONID。<br />
<br />
由于cookie可以被人为的禁止，必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写，就是把session id直接附加在URL路径的后面，附加方式也有两种，一种是作为URL路径的附加信息，表现形式为<a href="http://...../xxx;jsessionid="></a><a href="http://...../xxx;jsessionid=" target="_blank">http://...../xxx;jsessionid=</a> ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764<br />
另一种是作为查询字符串附加在URL后面，表现形式为<a href="http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764"></a><a href="http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764" target="_blank">http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764</a><br />
这两种方式对于用户来说是没有区别的，<span style="display: none">7的Fv:网育`xR</span>只是服务器在解析的时候处理的方式不同，采用第一种方式也有利于把session id的信息和正常程序参数区分开来。<br />
为了在整个交互过程中始终保持状态，就必须在每个客户端可能请求的路径后面都包含这个session id。<br />
<br />
另一种技术叫做表单隐藏字段。就是服务器会自动修改表单，添加一个隐藏字段，以便在表单提交时能够把session id传递回服务器。比如下面的表单<br />
&lt;form name="testform" action="/xxx"&gt;<br />
&lt;input type="text"&gt;<br />
&lt;/form&gt;<br />
在被传递给客户端之前将被改写成<br />
&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;<br />
这种技术现在已较少应用，笔者接触过的很古老的iPlanet6(SunONE应用服务器的前身)就使用了这种技术。<br />
实际上这种技术可以简单的用对action应用URL重写来代替。<br />
<br />
在谈论session机制的时候，常常听到这样一种误解&#8220;只要关闭浏览器，session就消失了&#8221;。其实可以想象一下会员卡的例子，除非顾客主动对店家提出销卡，否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的，除非程序通知服务器删除一个session，否则服务器会一直保留，程序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭，因此服务器根本不会有机会知道浏览器已经关闭，之所以会有这种错觉，是大部分session机制都使用会话cookie来保存session id，而关闭浏览器后这个 session id就消失了，再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上，或者使用某种手段改写浏览器发出的HTTP请求头，把原来的session id发送给服务器，则再次打开浏览器仍然能够找到原来的session。<br />
<br />
恰恰是由于关闭浏览器不会导致session被删除，迫使服务器为seesion设置了一个失效时间，当距离客户端上一次使用session的时间超过这个失效时间时，服务器就可以认为客户端已经停止了活动，才会把session删除以节省存储空间。<br />
<br />
五、理解javax.servlet.http.HttpSession<br />
HttpSession是Java平台对session机制的实现规范，因为它仅仅是个接口，具体到每个web应用服务器的提供商，除了对规范支持之外，仍然会有一些规范里没有规定的细微差异。这里我们以BEA的Weblogic Server8.1作为例子来演示。<br />
<br />
首先，Weblogic Server提供了一系列的参数来控制它的HttpSession的实现，包括使用cookie的开关选项，使用URL重写的开关选项，session持久化的设置，session失效时间的设置，以及针对cookie的各种设置，比如设置cookie的名字、路径、域， cookie的生存时间等。<br />
<br />
一般情况下，session都是存储在内存里，当服务器进程被停止或者重启的时候，内存里的session也会被清空，如果设置了session的持久化特性，服务器就会把session保存到硬盘上，当服务器进程重新启动或这些信息将能够被再次使用， Weblogic Server支持的持久性方式包括文件、数据库、客户端cookie保存和复制。<br />
<br />
复制严格说来不算持久化保存，因为session实际上还是保存在内存里，不过同样的信息被复制到各个cluster内的服务器进程中，这样即使某个服务器进程停止工作也仍然可以从其他进程中取得session。<br />
<br />
cookie生存时间的设置则会影响浏览器生成的cookie是否是一个会话cookie。默认是使用会话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解。<br />
<br />
cookie的路径对于web应用程序来说是一个非常重要的选项，Weblogic Server对这个选项的默认处理方式使得它与其他服务器有明显的区别。后面我们会专题讨论。<br />
<br />
关于session的设置参考[5] <a href="http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869"></a><a href="http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869" target="_blank">http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869</a><br />
<br />
六、HttpSession常见问题<br />
（在本小节中session的含义为⑤和⑥的混合）<br />
<br />
<br />
1、session在何时被创建<br />
一个常见的误解是以为session在有客户端访问时就被创建，然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建，注意如果JSP没有显示的使用 &lt;% @page session="false"%&gt; 关闭session，则JSP文件在编译成Servlet时将会自动加上这样一条语句 HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的 session对象的来历。<br />
<br />
由于session会消耗内存资源，因此，如果不打算使用session，应该在所有的JSP中关闭它。<br />
<br />
2、session何时被删除<br />
综合前面的讨论，session在下列情况下被删除a.程序调用HttpSession.invalidate();或b.距离上一次收到客户端发送的session id时间间隔超过了session的超时设置;或c.服务器进程被停止（非持久session）<br />
<br />
3、如何做到在浏览器关闭时删除session<br />
严格的讲，做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作，然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。<br />
<br />
4、有个HttpSessionListener是怎么回事<br />
你可以创建这样的listener去监控session的创建和销毁事件，使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销毁动作触发listener，而不是相反。类似的与HttpSession有关的listener还有 HttpSessionBindingListener，HttpSessionActivationListener和 HttpSessionAttributeListener。<br />
<br />
5、存放在session中的对象必须是可序列化的吗<br />
不是必需的。要求对象可序列化只是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。在 Weblogic Server的session中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本如果 session中有不可序列化的对象，在session销毁时会有一个Exception，很奇怪。<br />
<br />
6、如何才能正确的应付客户端禁止cookie的可能性<br />
对所有的URL使用URL重写，包括超链接，form的action，和重定向的URL，具体做法参见[6]<br />
<a href="http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770"></a><a href="http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770" target="_blank">http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770</a><br />
<br />
7、开两个浏览器窗口访问应用程序会使用同一个session还是不同的session<br />
参见第三小节对cookie的讨论，对session来说是只认id不认人，因此不同的浏览器，不同的窗口打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。<br />
<br />
8、如何防止用户打开两个浏览器窗口操作导致的session混乱<br />
这个问题与防止表单多次提交是类似的，可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端，同时保存在session里，客户端提交表单时必须把这个id也返回服务器，程序首先比较返回的id与保存在session里的值是否一致，如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript window.open打开的窗口，一般不设置这个id，或者使用单独的id，以防主窗口无法操作，建议不要再window.open打开的窗口里做修改操作，这样就可以不用设置。<br />
<br />
9、为什么在Weblogic Server中改变session的值后要重新调用一次session.setValue<br />
做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改变，需要向其他服务器进程复制新的session值。<br />
<br />
10、为什么session不见了<br />
排除session正常失效的因素之外，服务器本身的可能性应该是微乎其微的，虽然笔者在iPlanet6SP1加若干补丁的Solaris版本上倒也遇到过；浏览器插件的可能性次之，笔者也遇到过3721插件造成的问题；理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题。<br />
出现这一问题的大部分原因都是程序的错误，最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨论这个问题。<br />
<br />
七、跨应用程序的session共享<br />
<br />
常常有这样的情况，一个大项目被分割成若干小项目开发，为了能够互不干扰，要求每个小项目作为一个单独的web应用程序开发，可是到了最后突然发现某几个小项目之间需要共享一些信息，或者想使用session来实现SSO(single sign on)，在session中保存login的用户信息，最自然的要求是应用程序间能够访问彼此的session。<br />
<br />
然而按照Servlet规范，session的作用范围应该仅仅限于当前应用程序下，不同的应用程序之间是不能够互相访问对方的session的。各个应用服务器从实际效果上都遵守了这一规范，但是实现的细节却可能各有不同，因此解决跨应用程序session共享的方法也各不相同。<br />
<br />
首先来看一下Tomcat是如何实现web应用程序之间session的隔离的，从 Tomcat设置的cookie路径来看，它对不同的应用程序设置的cookie路径是不同的，<span style="display: none">V]bD专b%软N7L教供,jR国Z</span>这样不同的应用程序所用的session id是不同的，因此即使在同一个浏览器窗口里访问不同的应用程序，发送给服务器的session id也可以是不同的。<br />
<br />
<br />
&nbsp;&nbsp;<br />
<br />
根据这个特性，我们可以推测Tomcat中session的内存结构大致如下。<br />
<br />
<br />
<br />
<br />
笔者以前用过的iPlanet也采用的是同样的方式，估计SunONE与iPlanet之间不会有太大的差别。对于这种方式的服务器，解决的思路很简单，实际实行起来也不难。要么让所有的应用程序共享一个session id，要么让应用程序能够获得其他应用程序的session id。<br />
<br />
iPlanet中有一种很简单的方法来实现共享一个session id，那就是把各个应用程序的cookie路径都设为/（实际上应该是/NASApp，对于应用程序来讲它的作用相当于根）。<br />
&lt;session-info&gt;<br />
&lt;path&gt;/NASApp&lt;/path&gt;<br />
&lt;/session-info&gt;<br />
<br />
需要注意的是，操作共享的session应该遵循一些编程约定，比如在session attribute名字的前面加上应用程序的前缀，使得 setAttribute("name", "neo")变成setAttribute("app1.name", "neo")，以防止命名空间冲突，导致互相覆盖。<br />
<br />
<br />
在Tomcat中则没有这么方便的选择。在Tomcat版本3上，我们还可以有一些手段来共享session。对于版本4以上的Tomcat，目前笔者尚未发现简单的办法。只能借助于第三方的力量，比如使用文件、数据库、JMS或者客户端cookie，URL参数或者隐藏字段等手段。<br />
<br />
我们再看一下Weblogic Server是如何处理session的。<br />
<br />
<br />
&nbsp;&nbsp;<br />
<br />
从截屏画面上可以看到Weblogic Server对所有的应用程序设置的cookie的路径都是/，<span style="display: none">}的U(Cx中s中L3网提4NinY</span>这是不是意味着在Weblogic Server中默认的就可以共享session了呢？然而一个小实验即可证明即使不同的应用程序使用的是同一个session，各个应用程序仍然只能访问自己所设置的那些属性。这说明Weblogic Server中的session的内存结构可能如下<br />
<br />
<br />
<br />
<br />
对于这样一种结构，在 session机制本身上来解决session共享的问题应该是不可能的了。除了借助于第三方的力量，比如使用文件、数据库、JMS或者客户端 cookie，URL参数或者隐藏字段等手段，还有一种较为方便的做法，就是把一个应用程序的session放到ServletContext中，这样另外一个应用程序就可以从ServletContext中取得前一个应用程序的引用。示例代码如下，<br />
<br />
应用程序A<br />
context.setAttribute("appA", session); <br />
<br />
应用程序B<br />
contextA = context.getContext("/appA");<br />
HttpSession sessionA = (HttpSession)contextA.getAttribute("appA"); <br />
<br />
值得注意的是这种用法不可移植，因为根据ServletContext的JavaDoc，应用服务器可以处于安全的原因对于context.getContext("/appA");返回空值，以上做法在Weblogic Server 8.1中通过。<br />
<br />
那么Weblogic Server为什么要把所有的应用程序的cookie路径都设为/呢？原来是为了SSO，凡是共享这个session的应用程序都可以共享认证的信息。一个简单的实验就可以证明这一点，修改首先登录的那个应用程序的描述符weblogic.xml，把cookie路径修改为/appA 访问另外一个应用程序会重新要求登录，即使是反过来，先访问cookie路径为/的应用程序，再访问修改过路径的这个，虽然不再提示登录，但是登录的用户信息也会丢失。注意做这个实验时认证方式应该使用FORM，因为浏览器和web服务器对basic认证方式有其他的处理方式，第二次请求的认证不是通过 session来实现的。具体请参看[7] secion 14.8 Authorization，你可以修改所附的示例程序来做这些试验。<br />
<br />
八、总结<br />
session机制本身并不复杂，然而其实现和配置上的灵活性却使得具体情况复杂多变。这也要求我们不能把仅仅某一次的经验或者某一个浏览器，服务器的经验当作普遍适用的经验，而是始终需要具体情况具体分析。<br />
摘要：虽然session机制在web应用程序中被采用已经很长时间了，但是仍然有很多人不清楚session机制的本质，以至不能正确的应用这一技术。本文将详细讨论session的工作机制并且对在Java web application中应用session机制时常见的问题作出解答。
<img src ="http://www.blogjava.net/ASONG/aggbug/175485.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2008-01-15 16:06 <a href="http://www.blogjava.net/ASONG/articles/175485.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入浅出代理模式 (转载)</title><link>http://www.blogjava.net/ASONG/articles/169583.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Sat, 22 Dec 2007 08:00:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/169583.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/169583.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/169583.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/169583.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/169583.html</trackback:ping><description><![CDATA[<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">关键字：代理模式</span> java</p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt; tab-stops: list 21.0pt">一、<span style="font-family: 宋体">引子</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">我们去科技市场为自己的机器添加点奢侈的配件，很多</span>DIYer<span style="font-family: 宋体">都喜欢去找代理商，因为在代理商那里拿到的东西不仅质量有保证，而且价格和售后服务上都会好很多。客户通过代理商得到了自己想要的东西，而且还享受到了代理商额外的服务；而生产厂商通过代理商将自己的产品推广出去，而且可以将一些销售服务的任务交给代理商来完成（当然代理商要和厂商来共同分担风险，分配利润），这样自己就可以花更多的心思在产品的设计和生产上了。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">在美国，任何企业的产品要想拿到市场上去卖就必须经过代理商这一个环节，否则就是非法的。看来代理商在商业运作中起着很关键的作用。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">不小心把话题扯远了，回过头来，那么在我们的面向对象的程序设计中，会不会有代理商这样的角色呢？来看这篇文章的人肯定不会说：没有！</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">那么就跟着这篇文章来看看代理模式的奇妙吧</span>~~~~</p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt; tab-stops: list 21.0pt">二、<span style="font-family: 宋体">定义和分类</span></p>
<p style="margin: 0cm 0cm 0pt 21pt">A<span style="font-family: 宋体">：</span>What&#8217;s <span style="font-family: 宋体">代理模式</span> in English ? </p>
<p style="margin: 0cm 0cm 0pt 21pt">B<span style="font-family: 宋体">：</span>It is Proxy or Surrogate !</p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">代理模式在设计模式中的定义就是：为其他对象提供一种代理以控制对这个对象的访问。说白了就是，在一些情况下客户不想或者不能直接引用一个对象，而代理对象可以在客户和目标对象之间起到中介作用，去掉客户不能看到的内容和服务或者增添客户需要的额外服务。<br />
&nbsp;&nbsp;&nbsp; 那么什么时候要使用代理模式呢？在对已有的方法进行使用的时候出现需要对原有方法进行改进或者修改，这时候有两种改进选择：修改</span><span style="font-family: 宋体">原有方法来适应现在的使用方式，或者使用一个&#8220;第三者&#8221;方法来调用原有的方法并且对方法产生的结果进行一定的控制。第一种方法是明显</span><span style="font-family: 宋体">违背了&#8220;对扩展开放、对修改关闭&#8221;（开闭原则），而且在原来方法中作修改可能使得原来类的功能变得模糊和多元化（就像现在企业多元化</span><span style="font-family: 宋体">一样），而使用第二种方式可以将功能划分的更加清晰，有助于后面的维护。所以在一定程度上第二种方式是一个比较好的选择！<br />
&nbsp;&nbsp;&nbsp; 当然，话又说回来了，如果是一个很小的系统，功能也不是很繁杂，那么使用代理模式可能就显得臃肿，不如第一种方式来的快捷。这就</span><span style="font-family: 宋体">像一个三口之家，家务活全由家庭主妇或者一个保姆来完成是比较合理的，根本不需要雇上好几个保姆层层代理:)</p>
</span>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">根据《</span><span style="font-family: Tahoma">Java</span><span style="font-family: 宋体">与模式》书中对代理模式的分类，代理模式分为</span><span style="font-family: Tahoma">8</span><span style="font-family: 宋体">种，这里将几种常见的、重要的列举如下：</span></p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt; tab-stops: list 42.0pt"><span style="font-family: Tahoma">1.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体">远程（</span><span style="font-family: Tahoma">Remote</span><span style="font-family: 宋体">）代理：为一个位于不同的地址空间的对象提供一个局域代表对象。比如：你可以将一个在世界某个角落一台机器通过代理假象成你局域网中的一部分。</span></p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt; tab-stops: list 42.0pt"><span style="font-family: Tahoma">2.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体">虚拟（</span><span style="font-family: Tahoma">Virtual</span><span style="font-family: 宋体">）代理：根据需要将一个资源消耗很大或者比较复杂的对象延迟的真正需要时才创建。比如：如果一个很大的图片，需要花费很长时间才能显示出来，那么当这个图片包含在文档中时，使用编辑器或浏览器打开这个文档，这个大图片可能就影响了文档的阅读，这时需要做个图片</span><span style="font-family: Tahoma">Proxy</span><span style="font-family: 宋体">来代替真正的图片。</span></p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt; tab-stops: list 42.0pt"><span style="font-family: Tahoma">3.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体">保护（</span><span style="font-family: Tahoma">Protect or Access</span><span style="font-family: 宋体">）代理：控制对一个对象的访问权限。比如：在论坛中，不同的身份登陆，拥有的权限是不同的，使用代理模式可以控制权限（当然，使用别的方式也可以实现）。</span></p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt; tab-stops: list 42.0pt"><span style="font-family: Tahoma">4.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体">智能引用（</span><span style="font-family: Tahoma">Smart Reference</span><span style="font-family: 宋体">）代理：提供比对目标对象额外的服务。比如：纪录访问的流量（这是个再简单不过的例子），提供一些友情提示等等。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">代理模式是一种比较有用的模式，从几个类的&#8220;小结构&#8221;到庞大系统的&#8220;大结构&#8221;都可以看到它的影子。</span></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt; tab-stops: list 21.0pt"><span style="font-family: Tahoma">三、</span><span style="font-family: 宋体">结构</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">代理模式中的&#8220;代理商&#8221;要想实现代理任务，就必须和被代理的&#8220;厂商&#8221;使用共同的接口（你可以想象为产品）。所以自然而然你会想到在</span><span style="font-family: Tahoma">java</span><span style="font-family: 宋体">中使用一个抽象类或者接口（推荐）来实现这个共同的接口。于是代理模式就有三个角色组成了：</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">1</span><span style="font-family: 宋体">．抽象主题角色：声明了真实主题和代理主题的共同接口。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">2</span><span style="font-family: 宋体">．代理主题角色：内部包含对真实主题的引用，并且提供和真实主题角色相同的接口。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">3</span><span style="font-family: 宋体">．真实主题角色：定义真实的对象。</span><span style="font-family: Tahoma"><br />
<br />
</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">使用类图来表示下三者间的关系如下：<br />
</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt" align="center"><span style="font-family: 宋体"><img alt="" hspace="0" src="http://blog.csdn.net/images/blog_csdn_net/ai92/76417/r_leitu.jpg" align="baseline" border="0" /></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;当然，图上所示的是代理模式中的一个具体情况。而代理模式可以非常灵活的使用其他方式来实现，这样就与图上所示有很大的区别。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">也许，现在你已经对代理模式已经有了一个宏观的认识了，下面我们来看看怎么实际的使用代理模式。</span></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt; tab-stops: list 21.0pt"><span style="font-family: Tahoma">四、</span><span style="font-family: 宋体">举例</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">以论坛中已注册用户和游客的权限不同来作为第一个例子：已注册的用户拥有发帖，修改自己的注册信息，修改自己的帖子等功能；而游客只能看到别人发的帖子，没有其他权限。为了简化代码，更好的显示出代理模式的骨架，我们这里只实现发帖权限的控制。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">首先我们先实现一个抽象主题角色</span><span style="font-family: Tahoma">MyForum</span><span style="font-family: 宋体">，里面定义了真实主题和代理主题的共同接口——发帖功能。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">代码如下：</span></p>
<p style="margin: 0cm 0cm 0pt 21pt"><span style="font-family: Tahoma">public interface MyForum</span></p>
<p style="margin: 0cm 0cm 0pt 21pt"><span style="font-family: Tahoma">{</span></p>
<p style="margin: 0cm 0cm 0pt 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void AddFile();</span></p>
<p style="margin: 0cm 0cm 0pt 21pt"><span style="font-family: Tahoma">}</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">这样，真实主题角色和代理主题角色都要实现这个接口。其中真实的主题角色基本就是将这个接口的方法内容填充进来。所以在这里就不再赘述它的实现。我们把主要的精力放到关键的代理主题角色上。代理主题角色代码大体如下：</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">public class MyForumProxy implements MyForum </span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">{</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private RealMyForum forum ;</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private int permission ;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span><span style="font-family: 宋体">权限值</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public MyForumProxy(int permission)</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;forum = new RealMyForum()&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.permission = permission ;</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span><span style="font-family: 宋体">实现的接口</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void AddFile()</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span><span style="font-family: 宋体">满足权限设置的时候才能够执行操作</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Constants</span><span style="font-family: 宋体">是一个常量类</span></p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: 21pt"><span style="font-family: Tahoma">if(Constants.ASSOCIATOR == permission)</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; forum.AddFile();</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else </span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("You are not a associator of MyForum ,please registe!");</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: Tahoma">}</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">这样就实现了代理模式的功能。当然你也可以在这个代理类上添加自己的方法来实现额外的服务，比如统计帖子的浏览次数，记录用户的登录</span><span style="font-family: 宋体">情况等等。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">还有一个很常见的代理模式的使用例子就是对大幅图片浏览的控制。在我们常见的网站上面浏览图文的信息时，不知道你有没有注意到</span><span style="font-family: 宋体">，图片位置放置的是经过缩小的，当有人要仔细的查看这个图片时，可以通过点击图片来激活一个链接，在一个新的网页打开要看的图片 。这</span><span style="font-family: 宋体">样对于提高浏览速度是很有好处的，因为不是每个人都要去看仔细图上的信息。这种情况就可以使用代理模式来全面实现。这里我将思路表述</span><span style="font-family: 宋体">出来，至于实现由于工作原因，就不表述了，至于这种方式在B/S模式下的真实可行性，我没有确认过，只是凭空的想象。如果不是可行的方式</span><span style="font-family: 宋体">，那这个例子可以放到一个C/S下来实现，这个是绝对没有问题的，而且在很多介绍设计模式的书和文章中使用。两种方式的实现有兴趣的可以</span><span style="font-family: 宋体">来尝试一下：）<br />
&nbsp;&nbsp;&nbsp;&nbsp;我们在浏览器中访问网页时是调用的不是真实的装载图片的方法，而是在代理对象中的方法，在这个对象中，先使用一个线程向浏览</span><span style="font-family: 宋体">器装载了一个缩小版的图片，而在后台使用另一个线程来调用真实的装载大图片的方法将图片加载到本地，当你要浏览这个图片的时候，将其</span><span style="font-family: 宋体">在新的网页中显示出来。当然如果在你想浏览的时候图片尚未加载成功，可以再启动一个线程来显示提示信息，直到加载成功。&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;这样代理模式的功能就在上面体现的淋漓尽致——通过代理来将真实图片的加载放到后台来操作，使其不影响前台的浏览。<br />
<br />
</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">五、总结</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 21pt"><span style="font-family: 宋体">代理模式能够协调调用者和被调用者，能够在一定程度上降低系统的耦合度。不过一定要记住前面讲的使用代理模式的条件，不然的话使用了代理模式不但不会有好的效果，说不定还会出问题的</span>~~</p>
<img src ="http://www.blogjava.net/ASONG/aggbug/169583.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2007-12-22 16:00 <a href="http://www.blogjava.net/ASONG/articles/169583.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JSP的九个隐含对象</title><link>http://www.blogjava.net/ASONG/articles/128037.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Wed, 04 Jul 2007 02:33:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/128037.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/128037.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/128037.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/128037.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/128037.html</trackback:ping><description><![CDATA[<font face=Verdana>&nbsp;一、page 对象<br>&nbsp;&nbsp;&nbsp; page对象代表JSP本身，更准确地说它代表JSP被转译后的Servlet，它可以调用Servlet类所定义的方法。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 二、config 对象<br>&nbsp;&nbsp;&nbsp; config 对象里存放着一些Servlet 初始的数据结构。<br>&nbsp;&nbsp;&nbsp; config 对象实现于javax.servlet.ServletConfig 接口，它共有下列四种方法：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public String getInitParameter(name)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public java.util.Enumeration getInitParameterNames( )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public ServletContext getServletContext( )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Sring getServletName( )</font>
<p><font face=Verdana>&nbsp;&nbsp;&nbsp; 三、request 对象<br>&nbsp;&nbsp;&nbsp; request 对象包含所有请求的信息，如：请求的来源、标头、cookies和请求相关的参数值等等。<br>&nbsp;&nbsp;&nbsp; request 对象实现javax.servlet.http.HttpServletRequest接口的，所提供的方法可以将它分为四大类：<br>&nbsp;&nbsp;&nbsp; 1.储存和取得属性方法；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void setAttribute(String name, Object value)&nbsp;&nbsp;&nbsp; 设定name属性的值为value<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Enumeration getAttributeNamesInScope(int scope)&nbsp;&nbsp;&nbsp; 取得所有scope 范围的属性<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object getAttribute(String name)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得name 属性的值<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void removeAttribute(String name)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 移除name 属性的值<br>&nbsp;&nbsp;&nbsp; 2.取得请求参数的方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getParameter(String name)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得name 的参数值<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Enumeration getParameterNames( )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得所有的参数名称<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String [] getParameterValues(String name)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得所有name 的参数值<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Map getParameterMap( )&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; 取得一个要求参数的Map<br>&nbsp;&nbsp;&nbsp; 3.能够取得请求HTTP 标头的方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getHeader(String name)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得name 的标头<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Enumeration getHeaderNames()&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; Enumeration getHeaders(String name)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得所有name 的标头<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int getIntHeader(String name)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得整数类型name 的标头<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long getDateHeader(String name)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得日期类型name 的标头<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Cookie [] getCookies( )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得与请求有关的cookies<br>&nbsp;&nbsp;&nbsp; 4.其他的方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getContextPath( )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得Context 路径(即站台名称)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getMethod( )&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; 取得HTTP 的方法(GET、POST)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getProtocol( )&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; 取得使用的协议 (HTTP/1.1、HTTP/1.0 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getQueryString( )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得请求的参数字符串，不过，HTTP的方法必须为GET<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getRequestedSessionId( )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得用户端的Session ID<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getRequestURI( )&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; 取得请求的URL，但是不包括请求的参数字符串<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getRemoteAddr( )&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; 取得用户的IP 地址<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getRemoteHost( )&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; int getRemotePort( )&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; String getRemoteUser( )&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; void etCharacterEncoding(String&nbsp;&nbsp;&nbsp; encoding)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 设定编码格式，用来解决窗体传递中文的问题</font></p>
<p><font face=Verdana>&nbsp;&nbsp;&nbsp; 四、response 对象<br>&nbsp;&nbsp;&nbsp; response 对象主要将JSP 处理数据后的结果传回到客户端。<br>&nbsp;&nbsp;&nbsp; response 对象是实现javax.servlet.http.HttpServletResponse 接口。response对象所提供的方法。<br>&nbsp;&nbsp;&nbsp; 1.设定表头的方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void addCookie(Cookie cookie)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 新增cookie<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void addDateHeader(String name, long date)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 新增long类型的值到name标头<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void addHeader(String name, String value)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 新增String类型的值到name标头<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void addIntHeader(String name, int value)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 新增int类型的值到name标头<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void setDateHeader(String name, long date)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 指定long类型的值到name标头<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void setHeader(String name, String value)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 指定String类型的值到name标头<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void setIntHeader(String name, int value)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 指定int类型的值到name标头<br>&nbsp;&nbsp;&nbsp; 2.设定响应状态码的方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void sendError(int sc)&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; 传送状态码(status code)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void sendError(int sc, String msg)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 传送状态码和错误信息<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void setStatus(int sc)&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; 3.用来URL 重写(rewriting)的方法&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String encodeRedirectURL(String&nbsp;&nbsp;&nbsp; url)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对使用sendRedirect( )方法的URL予以编码</font></p>
<p><font face=Verdana>&nbsp;&nbsp;&nbsp; 五、out 对象<br>&nbsp;&nbsp;&nbsp; out 对象能把结果输出到网页上。<br>&nbsp;&nbsp;&nbsp; out主要是用来控制管理输出的缓冲区(buffer)和输出流(output stream)。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void clear( )&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;&nbsp;&nbsp;&nbsp;&nbsp; 清除输出缓冲区的内容<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void clearBuffer( )&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; void close( )&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;&nbsp;&nbsp;&nbsp;&nbsp; 关闭输出流，清除所有的内容<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int getBufferSize( )&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; 取得目前缓冲区的大小(KB)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int getRemaining( )&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; 取得目前使用后还剩下的缓冲区大小(KB)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; boolean isAutoFlush( )&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; 回传true表示缓冲区满时会自动清除；false表示不会自动清除并且产生异常处理<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 六、session 对象<br>&nbsp;&nbsp;&nbsp; session对象表示目前个别用户的会话(session)状况。<br>&nbsp;&nbsp;&nbsp; session对象实现javax.servlet.http.HttpSession接口，HttpSession接口所提供的方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long getCreationTime()&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; 取得session产生的时间，单位是毫秒<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getId()&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;&nbsp;&nbsp;&nbsp;&nbsp; 取得session 的ID<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long getLastAccessedTime()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得用户最后通过这个session送出请求的时间<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long getMaxInactiveInterval()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得最大session不活动的时间，若超过这时间，session 将会失效<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void invalidate()&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; 取消session 对象，并将对象存放的内容完全抛弃<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; boolean isNew()&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;&nbsp;&nbsp;&nbsp; 判断session 是否为"新"的<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void setMaxInactiveInterval(int&nbsp;&nbsp;&nbsp; interval)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 设定最大session不活动的时间，若超过这时间，session 将会失效</font></p>
<p><font face=Verdana>&nbsp;&nbsp;&nbsp; 七、application对象<br>&nbsp;&nbsp;&nbsp; application对象最常被使用在存取环境的信息。<br>&nbsp;&nbsp;&nbsp; 因为环境的信息通常都储存在ServletContext中，所以常利用application对象来存取ServletContext中的信息。<br>&nbsp;&nbsp;&nbsp; application 对象实现javax.servlet.ServletContext 接口，ServletContext接口容器所提供的方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int getMajorVersion( )&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; 取得Container主要的Servlet API版本<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int getMinorVersion( )&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; 取得Container次要的Servlet API 版本<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getServerInfo( )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得Container的名称和版本<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getMimeType(String file)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得指定文件的MIME 类型<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServletContext getContext(String uripath)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得指定Local URL的Application context<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getRealPath(String path)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 取得本地端path的绝对路径<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void log(String message)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 将信息写入log文件中<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void log(String message, Throwable throwable)&nbsp;&nbsp;&nbsp; 将stack trace 所产生的异常信息写入log文件中</font></p>
<p><font face=Verdana>&nbsp;&nbsp;&nbsp; 八、pageContext对象<br>&nbsp;&nbsp;&nbsp; pageContext对象能够存取其他隐含对象。<br>&nbsp;&nbsp;&nbsp; 1.pageContext对象存取其他隐含对象属性的方法，此时需要指定范围的参数。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object getAttribute(String name, int scope)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Enumeration getAttributeNamesInScope(int scope)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void removeAttribute(String name, int scope)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void setAttribute(String name, Object value, int scope)<br>&nbsp;&nbsp;&nbsp; 范围参数有四个，分别代表四种范围：PAGE_SCOPE、REQUEST_SCOPE、SESSION_SCOPE、APPLICATION_SCOPE<br>&nbsp;&nbsp;&nbsp; 2.PageContext对象取得其他隐含对象的方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Exception getException( )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 回传目前网页的异常，不过此网页要为error page，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JspWriter getOut( )&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; 回传目前网页的输出流，例如：out <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object getPage( )&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; 回传目前网页的Servlet 实体(instance)，例如：page<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServletRequest getRequest( )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 回传目前网页的请求，例如：request<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServletResponse getResponse( )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 回传目前网页的响应，例如：response<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServletConfig getServletConfig( )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 回传目前此网页的ServletConfig 对象，例如：config<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServletContext getServletContext( )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 回传目前此网页的执行环境(context)，例如：application<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpSession getSession( )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 回传和目前网页有联系的会话(session)，例如：session<br>&nbsp;&nbsp;&nbsp; 3.PageContext对象提供取得属性的方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object getAttribute(String name, int scope)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 回传name 属性，范围为scope的属性对象，回传类型为Object<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Enumeration getAttributeNamesInScope(int scope)&nbsp;&nbsp;&nbsp; 回传所有属性范围为scope 的属性名称，回传类型为Enumeration<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int getAttributesScope(String name)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 回传属性名称为name 的属性范围<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void removeAttribute(String name)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 移除属性名称为name 的属性对象<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void removeAttribute(String name, int scope)&nbsp;&nbsp;&nbsp; 移除属性名称为name，范围为scope 的属性对象<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void setAttribute(String name, Object value, int scope)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 指定属性对象的名称为name、值为value、范围为scope<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object findAttribute(String name)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 寻找在所有范围中属性名称为name 的属性对象</font></p>
<p><font face=Verdana>&nbsp;&nbsp;&nbsp; 九、exception对象<br>&nbsp;&nbsp;&nbsp; 若要使用exception 对象时，必须在page 指令中设定。&lt;%@ page isErrorPage="true" %&gt;才能使用。<br>&nbsp;&nbsp;&nbsp; exception提供的三个方法：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getMessage( )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getLocalizedMessage( )、<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printStackTrace(new java.io.PrintWriter(out))&nbsp; </font></p>
<img src ="http://www.blogjava.net/ASONG/aggbug/128037.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2007-07-04 10:33 <a href="http://www.blogjava.net/ASONG/articles/128037.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>消息传递和 Java 消息服务(JMS)</title><link>http://www.blogjava.net/ASONG/articles/107889.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Mon, 02 Apr 2007 03:06:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/107889.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/107889.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/107889.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/107889.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/107889.html</trackback:ping><description><![CDATA[<p><font size=1>消息传递系统用于构建高可靠、可伸缩及灵活的分布式应用程序。本文从大体上讨论了消息传递系统，简要叙述了它们的特性及类型，然后描述开发人员如何可以使用 Java 消息服务(Java Message Service ，JMS) 编写基于消息的应用程序。</font> </p>
<p><font size=1>正如许多以前在同步、可靠性、可伸缩性和安全性方面的未知问题一样，分布式应用程序潜在的问题也是不断增长的。一种解决方案是建立在松散耦合组件基础上的消息传递系统，这些组件之间通过消息进行通信。</font></p>
<p><font size=1>Java 消息服务提供了一致的 API 集合，让开发人员可以访问许多消息传递系统产品的公共特性。</font></p>
<p><font size=1>什么是消息传递系统？ <br>从它的本质上讲，消息传递系统允许不同的非耦合应用程序以异步方式进行可靠通信。消息传递系统体系结构一般用各个组件间的点对点关系代替客户端/服务器模型，其中每个点可以发送消息到其他点，或从其他点接收消息。</font></p>
<p><font size=1>相对于更加传统的分布式计算模型，消息传递系统提供了许多强大的优势。首先，它们倡议在消息消费者和消息生产者之间进行&#8220;松散耦合&#8221;。在生产者和消费者之间存在着高级匿名：对于消息消费者，它不关心 谁 产生消息，在网络上生产者在 哪里，或者消息是 何时 产生的。</font></p>
<p><font size=1>这使得可以构建动态、可靠和灵活的系统，由此可以修改子应用程序的整体组合，而不会影响到系统的其余部分。 消息传递系统的其他优势包括高可伸缩性（商业实现鼓吹可以支持好几万个客户端，以及支持每秒进行好几万个操作）、容易集成到异构网络以及由于减少了单点故障而提高了可靠性。</font></p>
<p><font size=1>由于消息传递系统固有的可靠性和可伸缩性，所以它们用于解决许多商业和计算科学问题。例如，它们是这样的一些不同的应用程序的基础：工作流、网络管理、通信服务（通过 IP 的语音、有声邮件、寻呼机和电子邮件）、客户服务、天气预报和供应链管理等系统。此外，消息传递系统是无价的，因为它作为&#8220;glue&#8221;（胶水）把完全不同的系统联系在一起，使之能够合并和获取消息。 </font></p>
<p><font size=1>消息系统类型<br>通常使用两种消息传递系统模型。</font></p>
<p><font size=1>发布/订阅<br>发布/订阅 (pub/sub) 消息传递系统支持事件驱动模型，在这种模型中，信息消费者和生产者参与消息的传输。生产者&#8220;发布&#8221;事件，而消费者&#8220;订阅&#8221;感兴趣的事件并消费事件。生产者把消息把与特定主题关联起来，然后消息传递系统根据消费者所注册的感兴趣主题，把消息路由给消费者。</font></p>
<p><font size=1>点对点<br>在点对点消息传递系统中，消息是被路由到各个消费者的，该消费者维护&#8220;传入&#8221;消息队列。消息传递应用程序发送消息到指定队列，然后客户端从队列中检索消息。 供应商经常会支持点对点模型或发布/订阅消息传递模型，或者同时支持这两种模型。</font></p>
<p><font size=1>既然已经从大体上认识了消息系统，现在让我们来看一下 Java 开发人员是如何可以利用它们的能力的。</font></p>
<p><font size=1>Java 消息服务<br>Java 消息服务是 J2EE (Java 2 Enterprise Edition) 套件的一部分，它提供了标准 API，Java 开发人员可以使用这些 API 来访问企业消息系统的共同特性。JMS 支持发布/订阅和点对点模型，并允许创建由任意 Java 对象组成的消息类型。</font></p>
<p><font size=1>设计目标<br>JMS 的基本设计目标是为了提供一组一致的接口，消息传递系统客户端可以独立地使用这些接口，而不必关心基础消息系统的提供商。 这样，客户端应用程序不仅可以跨计算机体系结构和操作系统进行移植，而且也可以跨消息传递产品进行移植。写到 JMS 的客户端应用程序将正常工作，而不用在所有符合 JMS 的消息传递系统上做修改（这可以与组件所运行的基础中间件上的 Enterprise Java Beans 组件的独立性进行比较）。</font></p>
<p><font size=1>JMS 也设计成：</font></p>
<p><font size=1>最小化消息传递系统提供商为其产品实现 JMS API 所需的工作量。 <br>提供了普通消息传递系统的大多数功能。 <br>许多消息系统提供商已经为它们的产品实现了 JMS，允许 Java 访问它们系统的功能。</font></p>
<p><font size=1>JMS 客户端可以使用 Java 设施<br>由于 JMS 客户端是基于 Java 的，因此它们可以利用现有的 Java API，比如：用于数据库访问的 JDBC、JavaBeans 组件模型、用于命名服务的 JNDI、用于客户端事务控制的 JTA 或用于企业应用程序服务的任何 J2SE 和 J2EE API。</font></p>
<p><font size=1>JMS 细节<br>现在，我们从消息开始，来看一下使用 JMS 构建消息传递系统客户端的细节。</font></p>
<p><font size=1>什么是消息？<br>在消息传递系统中，应用程序之间的通信点是消息本身，因此使用 JMS 的开发人员必须理解消息。</font></p>
<p><font size=1>尽管消息传递系统之间消息定义区别甚大，但 JMS 提供了统一的方式用于描述和访问消息。JMS 消息由三部分组成：</font></p>
<p><font size=1>消息标题 <br>用于消息标识。例如，标题用于确定指定的消息是否适合于&#8220;订阅者&#8221;。 <br>属性 <br>用于特定于应用程序、特定于提供商及可选的标题字段。 <br>消息体 <br>保存消息的内容。支持几种格式，其中包括：TextMessages——用于包装简单的 String; 和 ObjectMessages——用于包装任意的 Java 对象（它们必须是可序列化的）。也支持其他的格式。 <br>TextMessages<br>TextMessage 用于包装简单的 String 对象。在只传递字符串的情形下，这是有用的。期望许多消息传递系统将以 <a class=Channel_KeyLink href="http://www.chinageren.com/Article/WEB/XML/Index.html"><u><font color=#0000ff>XML</font></u></a> 为基础，TextMessages 是它们的自然容器。</font></p< p>
</p>
<br>
<p><font size=1>创建 TextMessage 对象是简单的，如下面两行代码所指出的那样：</font></p>
<p><font size=1>TextMessage message = <br>session.createMessage();<br>message.setText("hello world");<br><br>（在下一节，我们将看到 session 对象）。</font></p>
<p><font size=1>以这种方式创建的 TextMessage 准备发布到消息传递系统中。</font></p>
<p><font size=1>ObjectMessages<br>顾名思义，ObjectMessage 是包装了 Java 对象的消息。任何可序列化的 Java 对象均可用作 ObjectMessage。如果多个对象必须在单个消息中传输，那么可以使用包含几个可序列化对象的 Collection 对象（比如 List 或 Set）。</font></p>
<p><font size=1>下面展示了如何创建 Object 消息：</font></p>
<p><font size=1>ObjectMessage message = session.createObjectMessage();<br>message.setObject(myObject);<br><br>关于 JNDI<br>像许多 J2EE API 一样，JMS 利用了 JNDI（Java 命名和目录接口）来发现所需资源。详细讨论 JNDI 超出了本文的范围，但可从如下地址找到更多的进一步信息：Java 命名和目录接口 (JNDI)。</font></p>
<p><font size=1>构建 JMS 客户端<br>可以遵循下面的基本步骤来构建典型的 JMS 客户端：</font></p>
<p><font size=1>创建到消息传递系统提供商的连接。 <br>创建会话用于发送和接收消息。 <br>创建 MessageProducers 和 MessageConsumers 来创建或接收消息。 <br>一旦这些步骤执行完毕，消息生产的客户端将创建消息，并把它们发布到主题，而消息消费的客户端将侦听与主题有关的消息，并在它们到达时消费它们。</font></p>
<p><font size=1>为了详细展示它是如何工作的，我们研究了典型的消息生产者，它用于在 pub/sub 消息传递系统中把消息发布到特定的主题。注意，为简洁起见，省略了所有异常处理代码。</font></p>
<p><font size=1>创建连接<br>连接为客户端提供了对基础消息传递系统的访问，并执行资源分配和管理。连接是使用 ConnectionFactory 创建的，而 ConnectionFactory 通常是使用 JNDI 查找的。</font></p>
<p><font size=1>下面这些代码展示了创建连接过程中涉及的一些步骤：</font></p>
<p><font size=1>Context messaging = new InitialContext(); <br>// get JNDI context<br>TopicConnectionFactory topicConnectionFactory = <br>(TopicConnectionFactory) <br>messaging.lookup("TopicConnectionFactory");<br>TopicConnection topicConnection =<br>topicConnectionFactory.createTopicConnection();<br><br></font></p>
<p><br><font size=1>创建会话<br>会话是轻量级 JMS 对象，它提供了用于生产和消费消息的上下文。会话用于生成消息生产者和消息消费者，以及用于生成消息本身。</font></p>
<p><font size=1>TopicSession session = <br>topicConnection.createTopicSession(false,<br>Session.CLIENT_ACKNOWLEDGE);<br><br>createTopicSession() 的两个参数控制事务和消息的确认。</font></p>
<p><font size=1>查找主题<br>主题（也称标题、组或频道）是通过 JNDI 来查找的。主题标识了发送中或接收中的消息。在发布/阅系统中，订阅者订阅了指定的主题，而发布者把主题与它们发布的消息关联。</font></p>
<p><font size=1>这里我们创建了一个称为&#8220;WeatherData&#8221;的主题。</font></p>
<p><font size=1>Topic weatherTopic = messaging.lookup("WeatherData");<br><br>启动连接<br>在上面的初始化过程中，为了防止初始化期间出现不可预知的行为，消息流是被禁止的。一旦初始化完成，必须告诉连接开始消息流。</font></p>
<p><font size=1>topicConnection.start();<br><br>创建消息生产者<br>在发布/订阅领域中，生产者把消息发布到指定主题。下面代码展示了发布者的创建及后续的简单文本消息的生成和发布。</font></p>
<p><font size=1>TopicPublisher publisher = <br>session.createPublisher(weatherData);<br>TextMessage message = session.createMessage();<br>message.setText("temperature: 35 degrees");</font></p>
<p><font size=1>publisher.publish(message);<br><br></font></p>
<p><br><font size=1>创建订阅者及点对点系统的 JMS 客户端遵循相似的过程。可以在 [1] 中找到这一过程的完整细节。</font></p>
<p><font size=1>结束语<br>我们已经看到在使用 JMS 来构建基于消息传递的应用程序的过程中涉及的基本概念。在编写 JMS 代码之前，你将需要访问符合 JMS 的消息传递系统。可从如下地址获取符合 JMS 的供应商的列表：</font></p>
<br>
<p><font size=1>JMS 供应商 </font></p>
<p><br><font size=1>创建和构建基于 JMS 的应用程序是简单的，可是它为构建强大、可伸缩和高可靠的分布式系统提供了基础。</font></p>
<p><font size=1>注意，JMS 远远比本文讨论的要复杂，并为下面的这些提供了支持：管理、安全、错误处理和恢复、优化、分布式事务、消息排序和消息确认等。</font></p>
<p><font size=1>有关 Java Message Service 的进一步信息和文档，请参阅：</font></p>
<p><font size=1>Java Message Service 文档</font></p>
<p><font size=1>参考资料<br>本文中的许多内容是以 Java Message Service 文档 为基础的。 </font></p>
<p><font size=1>本文的再版须有 SunServer 许可。</font></p>
<img src ="http://www.blogjava.net/ASONG/aggbug/107889.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2007-04-02 11:06 <a href="http://www.blogjava.net/ASONG/articles/107889.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring 的持久层封装</title><link>http://www.blogjava.net/ASONG/articles/103938.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Thu, 15 Mar 2007 01:40:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/103938.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/103938.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/103938.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/103938.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/103938.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 一、概述																		（一）基本概念																		1、数据访问的关键技术												     我们可以将数据访问分为两个部分：一是获得数据源；二是进行数据库操作（增删改查）。												2、获得数据源的几种方法								因为只是为了记录一些关键的...&nbsp;&nbsp;<a href='http://www.blogjava.net/ASONG/articles/103938.html'>阅读全文</a><img src ="http://www.blogjava.net/ASONG/aggbug/103938.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2007-03-15 09:40 <a href="http://www.blogjava.net/ASONG/articles/103938.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Apache Commons</title><link>http://www.blogjava.net/ASONG/articles/103321.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Mon, 12 Mar 2007 06:44:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/103321.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/103321.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/103321.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/103321.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/103321.html</trackback:ping><description><![CDATA[
		<p>
				<font face="Arial">Apache Commons是一个非常有用的工具包，解决各种实际的通用问题，下面是一个简述表，详细信息访问</font>
				<a href="http://jakarta.apache.org/commons/index.html" target="_blank">
						<font face="Arial">http://jakarta.apache.org/commons/index.html</font>
				</a>
		</p>
		<p>
				<font face="Arial">
						<b>BeanUtils</b>
						<br />Commons-BeanUtils 提供对 Java 反射和自省API的包装</font>
		</p>
		<p>
				<font face="Arial">
						<b>Betwixt</b>
						<br />Betwixt提供将 JavaBean 映射至 XML 文档，以及相反映射的服务.</font>
		</p>
		<p>
				<font face="Arial">
						<b>Chain</b>
						<br />Chain 提供实现组织复杂的处理流程的“责任链模式”.</font>
		</p>
		<p>
				<font face="Arial">
						<b>CLI</b>
						<br />CLI 提供针对命令行参数，选项，选项组，强制选项等的简单API.</font>
		</p>
		<p>
				<font face="Arial">
						<b>Codec</b>
						<br />Codec 包含一些通用的编码解码算法。包括一些语音编码器， Hex, Base64, 以及URL encoder.</font>
		</p>
		<p>
				<font face="Arial">
						<b>Collections</b>
						<br />Commons-Collections 提供一个类包来扩展和增加标准的 Java Collection框架</font>
		</p>
		<p>
				<font face="Arial">
						<b>Configuration</b>
						<br />Commons-Configuration 工具对各种各式的配置和参考文件提供读取帮助.</font>
		</p>
		<p>
				<font face="Arial">
						<b>Daemon</b>
						<br />一种 unix-daemon-like java 代码的替代机制</font>
		</p>
		<p>
				<font face="Arial">
						<b>DBCP</b>
						<br />Commons-DBCP 提供数据库连接池服务</font>
		</p>
		<p>
				<font face="Arial">
						<b>DbUtils</b>
						<br />DbUtils 是一个 JDBC helper 类库，完成数据库任务的简单的资源清除代码.</font>
		</p>
		<p>
				<font face="Arial">
						<b>Digester</b>
						<br />Commons-Digester 是一个 XML-Java对象的映射工具，用于解析 XML配置文件.</font>
		</p>
		<p>
				<font face="Arial">
						<b>Discovery</b>
						<br />Commons-Discovery 提供工具来定位资源 (包括类) ，通过使用各种模式来映射服务/引用名称和资源名称。.</font>
		</p>
		<p>
				<font face="Arial">
						<b>EL</b>
						<br />Commons-EL 提供在JSP2.0规范中定义的EL表达式的解释器.</font>
		</p>
		<p>
				<font face="Arial">
						<b>FileUpload</b>
						<br />FileUpload 使得在你可以在应用和Servlet中容易的加入强大和高性能的文件上传能力</font>
		</p>
		<p>
				<font face="Arial">
						<b>HttpClient</b>
						<br />Commons-HttpClient 提供了可以工作于HTTP协议客户端的一个框架.</font>
		</p>
		<p>
				<font face="Arial">
						<b>IO</b>
						<br />IO 是一个 I/O 工具集</font>
		</p>
		<p>
				<font face="Arial">
						<b>Jelly</b>
						<br />Jelly是一个基于 XML 的脚本和处理引擎。 Jelly 借鉴了 JSP 定指标签，Velocity, Cocoon和Xdoclet中的脚本引擎的许多优点。Jelly 可以用在命令行， Ant 或者 Servlet之中。</font>
		</p>
		<p>
				<font face="Arial">
						<b>Jexl</b>
						<br />Jexl是一个表达式语言，通过借鉴来自于Velocity的经验扩展了JSTL定义的表达式语言。.</font>
		</p>
		<p>
				<font face="Arial">
						<b>JXPath</b>
						<br />Commons-JXPath 提供了使用Xpath语法操纵符合Java类命名规范的 JavaBeans的工具。也支持 maps, DOM 和其他对象模型。.</font>
		</p>
		<p>
				<font face="Arial">
						<b>Lang</b>
						<br />Commons-Lang 提供了许多许多通用的工具类集，提供了一些java.lang中类的扩展功能</font>
		</p>
		<p>
				<font face="Arial">
						<b>Latka</b>
						<br />Commons-Latka 是一个HTTP 功能测试包，用于自动化的QA,验收和衰减测试.</font>
		</p>
		<p>
				<font face="Arial">
						<b>Launcher</b>
						<br />Launcher 组件是一个交叉平台的Java 应用载入器。 Commons-launcher 消除了需要批处理或者Shell脚本来载入Java 类。.原始的 Java 类来自于Jakarta Tomcat 4.0 项目</font>
		</p>
		<p>
				<font face="Arial">
						<b>Logging</b>
						<br />Commons-Logging 是一个各种 logging API实现的包裹类.</font>
		</p>
		<p>
				<font face="Arial">
						<b>Math</b>
						<br />Math 是一个轻量的，自包含的数学和统计组件，解决了许多非常通用但没有及时出现在Java标准语言中的实践问题.</font>
		</p>
		<p>
				<font face="Arial">
						<b>Modeler</b>
						<br />Commons-Modeler 提供了建模兼容JMX规范的 Mbean的机制.</font>
		</p>
		<p>
				<font face="Arial">
						<b>Net</b>
						<br />Net 是一个网络工具集，基于 NetComponents 代码，包括 FTP 客户端等等。</font>
		</p>
		<p>
				<font face="Arial">
						<b>Pool</b>
						<br />Commons-Pool 提供了通用对象池接口，一个用于创建模块化对象池的工具包，以及通常的对象池实现.</font>
		</p>
		<p>
				<font face="Arial">
						<b>Primitives</b>
						<br />Commons-Primitives提供了一个更小，更快和更易使用的对Java基本类型的支持。当前主要是针对基本类型的 collection。.</font>
		</p>
		<p>
				<font face="Arial">
						<b>Validator</b>
						<br />The commons-validator提供了一个简单的，可扩展的框架来在一个XML文件中定义校验器 (校验方法)和校验规则。支持校验规则的和错误消息的国际化。</font>
		</p>
<img src ="http://www.blogjava.net/ASONG/aggbug/103321.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2007-03-12 14:44 <a href="http://www.blogjava.net/ASONG/articles/103321.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JSP中基于Session的在线用户统计分析</title><link>http://www.blogjava.net/ASONG/articles/103318.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Mon, 12 Mar 2007 06:37:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/103318.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/103318.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/103318.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/103318.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/103318.html</trackback:ping><description><![CDATA[
		<a href="http://dev.21tx.com/java/jsp/" target="_blank">
				<font color="#3366cc">JSP</font>
		</a>作为后起之秀能够在<a href="http://www.21tx.com/server/" target="_blank"><font color="#3366cc">服务器</font></a>编程环境中占据一定地位，是和它良好支持一系列<a href="http://news.21tx.com/yejie/" target="_blank"><font color="#3366cc">业界</font></a>标准密切相关的。Session就是它提供的<a href="http://dev.21tx.com/java/base/" target="_blank"><font color="#3366cc">基础</font></a>设施之一。作为一个程序员，你可以不介意具体在客户端是如何实现，就方便的实现简单的基于session的用户管理。现在对于处理在线用户，有几种不同的处理方法。<br /><br />　　一种是页面刷新由用户控制，服务器端控制一个超时时间比如30分钟，到了时间之后用户没有动作就被踢出。这种方法的优点是，如果用户忘了退出，可以防止别人恶意操作。缺点是，如果你在做一件很耗时间的事情，超过了这个时间限制，submit的时候可能要再次面临登陆。如果原来的叶面又是强制失效的话，就有可能丢失你做的工作。在实现的角度来看，这是最简单的，Server端默认实现的就是这样的模式。<br /><br />　　另一种方式是，站点采用框架结构，有一个Frame或者隐藏的iframe在不断刷新，这样你永远不会被踢出，但是服务器端为了判断你是否在线，需要定一个发呆时间，如果超过这个发呆时间你除了这个自动刷新的页面外没有刷新其他页面的话，就认为你已经不在线了。采取这种方式的典型是xici<a href="http://dev.21tx.com/dotnet/" target="_blank"><font color="#3366cc">.net</font></a>。 他的优点是可以可以利用不断的刷新实现一些类似server-push的功能，比如网友之间发送消息。<br /><br />　　不管哪一种模式，为了实现浏览当前所有的在线用户，还需要做一些额外的工作。<a href="http://dev.21tx.com/java/jsp/" target="_blank"><font color="#3366cc">Servlet</font></a> API中没有得到Session列表的API。<br /><br />　　可以利用的是Listener. Servlet 2.2和2.3规范在这里略微有一些不一样。2.2中HttpSessionBindingListener可以实现当一个HTTPSession中的Attribute变化的时候通知你的类。而2.3中还引入了HttpSessionAttributeListener.鉴于我使用的环境是Visual age for <a href="http://dev.21tx.com/java/" target="_blank"><font color="#3366cc">Java</font></a> 4和JRun server 3.1,他们还不直接支持Servlet 2.3的编程，这里我用的是HttpSessionBindingListener.<br /><br />　　需要做的事情包括做一个新的类来实现HttpSessionBindingListener接口。这个接口有两个方法：<br /><br /><table class="txcode" cellspacing="0" cellpadding="0" align="center" border="0"><tbody><tr><td>public void valueBound(HttpSessionBindingEvent event)<br />public void valueUnbound(HttpSessionBindingEvent event)</td></tr></tbody></table><br />　　当你执行Session.addAttribute(String,Object)的时候，如果你已经把一个实现了HttpSessionBindingListener接口的类加入为Attribute,Session会通知你的类，调用你的valueBound方法。相反，Session.removeAttribute方法对应的是valueUndound方法。<br /><br /><table class="txcode" cellspacing="0" cellpadding="0" align="center" border="0"><tbody><tr><td>public class HttpSessionBinding implements javax.servlet.http.HttpSessionBindingListener <br />{<br />　ServletContext application = null;<br /><br />　public HttpSessionBinding(ServletContext application)<br />　{<br />　　super();<br />　　if (application ==null)<br />　　　throw new IllegalArgumentException("Null application is not accept.");<br />　　this.application = application;<br />　}<br /><br />　public void valueBound(javax.servlet.http.HttpSessionBindingEvent e) <br />　{ <br />　　Vector activeSessions = (Vector) application.getAttribute("activeSessions");<br />　　if (activeSessions == null)<br />　　{<br />　　　activeSessions = new Vector();<br />　　}<br /><br />　　<a href="http://dev.21tx.com/java/adv/jdbc/" target="_blank"><font color="#3366cc">JDBC</font></a>User sessionUser = (JDBCUser)e.getSession().getAttribute("user");<br />　　if (sessionUser != null)<br />　　{<br />　　　activeSessions.add(e.getSession());<br />　　}<br />　　application.setAttribute("activeSessions",activeSessions);<br />　}<br /><br />　public void valueUnbound(javax.servlet.http.HttpSessionBindingEvent e) <br />　{<br />　　JDBCUser sessionUser = (JDBCUser)e.getSession().getAttribute("user");<br />　　if (sessionUser == null)<br />　　{<br />　　　Vector activeSessions = (Vector) application.getAttribute("activeSessions");<br />　　　if (activeSessions != null)<br />　　　{<br />　　　　activeSessions.remove(e.getSession().getId());<br />　　　　application.setAttribute("activeSessions",activeSessions);<br />　　　}<br />　　}<br />　}<br />}</td></tr></tbody></table><br />　　假设其中的JDBCUser类是一个任意User类。在执行用户登录时，把User类和HttpSessionBinding类都加入到Session中去。<br /><br />　　这样，每次用户登录后，在application中的attribute "activeSessions"这个vector中都会增加一条记录。每当session超时，valueUnbound被触发，在这个vector中删去将要被超时的session.<br /><br /><table class="txcode" cellspacing="0" cellpadding="0" align="center" border="0"><tbody><tr><td>public void login()<br />throws ACLException,SQLException,IOException<br />{<br />　/* get JDBC User Class */<br />　if (user != null)<br />　{<br />　　logout();<br />　}<br />　{<br />　　// if session time out, or user didn't login, save the target url temporary.<br /><br />　　JDBCUserFactory uf = new JDBCUserFactory();<br /><br />　　if ( (this.request.getParameter("userID")==null) || (this.request.getParameter("password")==null) )<br />　　{<br />　　　throw new ACLException("Please input a valid userName and password.");<br />　　}<br /><br />　　JDBCUser user = (JDBCUser) uf.UserLogin(<br />　　　this.request.getParameter("userID"),<br />　　　this.request.getParameter("password") );<br />　　　user.touchLoginTime();<br />　　　this.session.setAttribute("user",user);<br />　　　this.session.setAttribute("BindingNotify",new HttpSessionBinding(application));<br />　　}<br />　}</td></tr></tbody></table><br />　　Login的时候，把User和这个BindingNotofy目的的类都加入到session中去。logout的时候，就要主动在activeSessions这个vector中删去这个session.<br /><br /><table class="txcode" cellspacing="0" cellpadding="0" align="center" border="0"><tbody><tr><td>public void logout()<br />throws SQLException,ACLException<br />{<br />　if (this.user == null &amp;&amp; this.session.getAttribute("user")==null)<br />　{<br />　　return;<br />　}<br /><br />　Vector activeSessions = (Vector) this.application.getAttribute("activeSessions");<br />　if (activeSessions != null)<br />　{<br />　　activeSessions.remove(this.session);<br />　　application.setAttribute("activeSessions",activeSessions);<br />　}<br /><br />　java.util.Enumeration e = this.session.getAttributeNames();<br /><br />　while (e.hasMoreElements())<br />　{<br />　　String s = (String)e.nextElement();<br />　　this.session.removeAttribute(s);<br />　}<br />　this.user.touchLogoutTime();<br />　this.user = null;<br />}</td></tr></tbody></table><br />　　 这两个函数位于一个HttpSessionManager类中.这个类引用了jsp里面的application全局对象。这个类的其他代码和本文无关且相当长，我就不贴出来了。<br />　　下面来看看<a href="http://dev.21tx.com/java/jsp/" target="_blank"><font color="#3366cc">JSP</font></a>里面怎么用。<br /><br />　　假设一个登录用的表单被提交到doLogin.jsp, 表单中包含UserName和password域。节选部分片段：<br /><br /><table class="txcode" cellspacing="0" cellpadding="0" align="center" border="0"><tbody><tr><td>＜%<br />HttpSessionManager hsm = new HttpSessionManager(application,request,response);<br />try<br />{<br />　hsm.login();<br />}<br />catch ( UserNotFoundException e)<br />{<br />　response.sendRedirect("InsufficientPrivilege.jsp?detail=User%20does%20not%20exist.");<br />　return;<br />}<br />catch ( InvalidPasswordException e2)<br />{ <br />　response.sendRedirect("InsufficientPrivilege.jsp?detail=Invalid%20Password");<br />　return;<br />}<br />catch ( Exception e3)<br />{<br />　%＞ Error:＜%=e3.toString() %＞＜br＞<br />　Press ＜a href="login.jsp"＞Here＜/a＞ to relogin.<br />　＜% return;<br />}<br />response.sendRedirect("index.jsp");<br />%＞</td></tr></tbody></table><br />　　再来看看现在我们怎么得到一个当前在线的用户列表。<br /><br /><table class="txcode" cellspacing="0" cellpadding="0" align="center" border="0"><tbody><tr><td>＜body bgcolor="#FFFFFF"＞<br />＜table cellspacing="0" cellpadding="0" width="100%"＞<br /><br />＜tr ＞<br />＜td style="width:24px"＞SessionId<br />＜/td＞<br />＜td style="width:80px" ＞User<br />＜/td＞<br />＜td style="width:80px" ＞Login Time<br />＜/td＞<br />＜td style="width:80px" ＞Last <a href="http://dev.21tx.com/database/access/" target="_blank"><font color="#3366cc">Access</font></a> Time<br />＜/td＞<br />＜/tr＞<br />＜%<br />Vector activeSessions = (Vector) application.getAttribute("activeSessions");<br />if (activeSessions == null)<br />{<br />　activeSessions = new Vector();<br />　application.setAttribute("activeSessions",activeSessions);<br />}<br /><br />Iterator it = activeSessions.iterator();<br />while (it.hasNext())<br />{<br />　HttpSession sess = (HttpSession)it.next();<br />　<a href="http://dev.21tx.com/java/adv/jdbc/" target="_blank"><font color="#3366cc">JDBC</font></a>User sessionUser = (JDBCUser)sess.getAttribute("user");<br />　String userId = (sessionUser!=null)?sessionUser.getUserID():"None";<br />%＞<br />＜tr＞<br />＜td nowrap=''＞＜%= sess.getId() %＞＜/td＞<br />＜td nowrap=''＞＜%= userId %＞＜/td＞<br />＜td nowrap=''＞<br />＜%= BeaconDate.getInstance( new <a href="http://dev.21tx.com/java/" target="_blank"><font color="#3366cc">Java</font></a>.util.Date(sess.getCreationTime())).getDateTimeString()%＞＜/td＞<br />＜td class="＜%= stl %＞3" nowrap=''＞<br />＜%= BeaconDate.getInstance( new java.util.Date(sess.getLastAccessedTime())).getDateTimeString()%＞＜/td＞<br />＜/tr＞<br />＜%<br />}<br />%＞<br />＜/table＞<br />＜/body＞</td></tr></tbody></table><br />　　以上的代码从application中取出activeSessions，并且显示出具体的时间。其中BeaconDate类假设为格式化时间的类。<br /><br />　　这样，我们得到了一个察看在线用户的列表的框架。至于在线用户列表分页等功能，与本文无关，不予讨论。<br /><br />　　这是一个非刷新模型的例子，依赖于session的超时机制。我的同事sonymusic指出很多时候由于各个厂商思想的不同，这有可能是不可信赖的。考虑到这种需求，需要在每个叶面刷新的时候都判断当前用户距离上次使用的时间是否超过某一个预定时间值。这实质上就是自己实现session超时。如果需要实现刷新模型，就必须使用这种每个叶面进行刷新判断的方法。<img src ="http://www.blogjava.net/ASONG/aggbug/103318.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2007-03-12 14:37 <a href="http://www.blogjava.net/ASONG/articles/103318.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Commons-logging + Log4j 入门指南 </title><link>http://www.blogjava.net/ASONG/articles/103316.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Mon, 12 Mar 2007 06:36:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/103316.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/103316.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/103316.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/103316.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/103316.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 为什么要用日志（														Log												）														?																								这个……就不必说了吧。																		为什么不用														System.out.println()?										...&nbsp;&nbsp;<a href='http://www.blogjava.net/ASONG/articles/103316.html'>阅读全文</a><img src ="http://www.blogjava.net/ASONG/aggbug/103316.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2007-03-12 14:36 <a href="http://www.blogjava.net/ASONG/articles/103316.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入剖析Java编程中的中文问题及建议最优解决方法</title><link>http://www.blogjava.net/ASONG/articles/54286.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Wed, 21 Jun 2006 07:59:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/54286.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/54286.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/54286.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/54286.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/54286.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 说明：本文为作者原创，作者联系地址为：				josserchai@yahoo.com				。由于				Java				编程中的中文								问题是一个老生常谈的问题，在阅读了许多关于				Java				中文问题解决方法之后，结合作者的编程实践，我发现过去谈的许多方法都不能清晰地说明问题及解决问题，尤其是跨平台时的中文问题。于是我给出此篇文章，内容...&nbsp;&nbsp;<a href='http://www.blogjava.net/ASONG/articles/54286.html'>阅读全文</a><img src ="http://www.blogjava.net/ASONG/aggbug/54286.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2006-06-21 15:59 <a href="http://www.blogjava.net/ASONG/articles/54286.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Applet与Servlet通信的具体实现</title><link>http://www.blogjava.net/ASONG/articles/53689.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Mon, 19 Jun 2006 01:03:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/53689.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/53689.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/53689.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/53689.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/53689.html</trackback:ping><description><![CDATA[
		<p>通信过程包括下述两个方面： <br />2.1 Applet对Servlet的访问及参数传递的实现<br />2.1.1创建URL对象<br />在JAVA程序中，可以利用如下的形式创建URL对象<br />URL servletURL = new URL( "<a href="http://localhost:8080/servlet/dbServlet.DbServlet">http://localhost:8080/servlet/dbServlet.DbServlet</a>" );<br />2.1.2 与URL地址建立连接<br />在成功的创建了URL对象之后，可以调用URL类中的openConnection（）函数来建立连接。openConnection（）函数在建立连接的同时，进行通信连接的初始化工作。<br />URLConnection servletConnection = servletURL.openConnection();<br />2.1.3 利用URLConnection对象进行读写操作<br />2.1.3.1 利用URLConnection对象读取Servlet返回的信息<br />在获得URLConnection对象之后，如果Servlet向Applet传送的是JAVA对象，则可以利用URLConnection对象的openStream()方法获得输入流，然后新生成一个ObjectInputStream对象，利用ObjectInputStream对象的readObject（）方法即可得到Servlet传回的JAVA对象。<br />ResultSet rs = null; // Temporary storage for the data.<br />URL servletURL = null; // The URL to the servlet.<br />URLConnection servletConnection = null; // The connection to the servlet.<br />ObjectInputStream dbStream = null; // The stream from the servlet.<br />try<br />{<br />servletURL = new URL( "<a href="http://localhost:8080/servlet/dbServlet.DbServlet">http://localhost:8080/servlet/dbServlet.DbServlet</a>" );<br />servletConnection = servletURL.openConnection();<br />dbStream = new ObjectInputStream( servletURL.openStream() );<br />// Read an object from the servlet stream and cast it to a DataSetData<br />// object.<br />data = (ResultSet) dbStream.readObject();<br />}<br />catch( Exception e )<br />{<br />。。。<br />}<br />如果Servlet向Applet传送的是普通的文本，则可以利用URLConnection对象的getInputStream()方法获得输入流，然后新生成一个DataInputStream对象，利用DataInputStream对象的readLine（）方法依次取得Servlet传回的文本。<br />DataInputStream dbStream = new DataInputStream ( servletURL.getInputStream() );<br />// Read text from the servlet stream line by line.<br />While((inputLine= dbStream.readLine())!=null)<br />{<br />••••••.<br />}<br />2.3.1.2利用URLConnection对象对Servlet的传值操作<br />Applet向Servlet的有关参数传递，可以通过下面两种方法实现：<br />可以通过在URL地址后附加参数以GET的方式实现参数的传递：<br />servletURL = new URL( "<a href="http://localhost:8080/servlet/dbServlet.DbServlet">http://localhost:8080/servlet/dbServlet.DbServlet</a>？sql=select * from hklhzsj where total&gt;100" );<br />另一种方法是从URLConnection连接获得输出流，这个输出流被连接到公共网关程序的（服务器端）的标准输入流上，然后把要传送的有关数据写入输出流，发送完毕关闭输出流。<br />servletConnection.setDoOutput(true);<br />PrintStream outStream=new PrintStream(servletURL.openConnection());<br />outStream.println(“sql=select * from hklhzsj where total&gt;100”);<br />outStream.close();<br />2.2 Servlet向Applet的数据传递的实现<br />可以通过Servlet对象的request参数的getParameter()获得Applet传递过来的参数：String sql=request.getParameter("sql");<br />通过Servlet对象的request参数的getOutputStream()所得到的输出流新生成一个对象输出流ObjectOutputStream类型的对象，然后通过该对象的writeObject（）方法输出JAVA类型的对象。 <br />Class.forName（“sun:jdbc:odbcdriver”）；<br />Connection conn ＝DriverManager.getConnection（connetionString）<br />Statement st=conn.createStatement()<br />ResultSet rs＝st.execute(sql)<br />dbStream = new ObjectOutputStream( response.getOutputStream() );<br />// Write the object...<br />dbStream.writeObject(rs );<br />通过request参数的getWriter ()方法得到PrintWriter类型的输出，通过此对象的println()方法可以从Servlet想Applet输出文本：<br />PrintWriter out = response.getWriter();<br />out.println("&lt;head&gt;&lt;title&gt;DataCenter&lt;/title&gt;&lt;/head&gt;");<br />通过上面的分析我们通过Servlet 实现了对非宿主机上的数据库的访问，Servlet 与Applet通信提供了语言级别上的互相传递JAVA对象的便利，我们同样可以利用Applet通过CGI对各种服务器端的CGI程序或其它脚本应用程序（如ASP、JSP等）提供访问，以文本的方式实现通信。</p>
<img src ="http://www.blogjava.net/ASONG/aggbug/53689.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2006-06-19 09:03 <a href="http://www.blogjava.net/ASONG/articles/53689.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>HttpClient入门</title><link>http://www.blogjava.net/ASONG/articles/51537.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Thu, 08 Jun 2006 14:47:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/51537.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/51537.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/51537.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/51537.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/51537.html</trackback:ping><description><![CDATA[
		<p>
				<a name="IDADCG0">
						<span class="atitle">
								<font face="Arial" size="4">HttpClient简介</font>
						</span>
				</a>
		</p>
		<p>HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了，越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java.net 包中已经提供了访问 HTTP 协议的基本功能，但是对于大部分应用程序来说，JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目，用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包，并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中，比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient，更多使用 HttpClient 的应用可以参见<a href="http://wiki.apache.org/jakarta-httpclient/HttpClientPowered"><font color="#5c81a7">http://wiki.apache.org/jakarta-httpclient/HttpClientPowered</font></a>。HttpClient 项目非常活跃，使用的人还是非常多的。目前 HttpClient 版本是在 2005.10.11 发布的 3.0 RC4 。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" src="http://www.crackj2ee.com/Article/UploadFiles/200511/20051125151629941.gif" width="823" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/#main">
																				<b>
																						<font color="#996699">
																						</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="IDAOCG0">
						<span class="atitle">
								<font face="Arial" size="4">HttpClient 功能介绍</font>
						</span>
				</a>
		</p>
		<p>以下列出的是 HttpClient 提供的主要的功能，要知道更多详细的功能可以参见 HttpClient 的主页。</p>
		<ul>
				<li>实现了所有 HTTP 的方法（GET,POST,PUT,HEAD 等） 
</li>
				<li>支持自动转向 
</li>
				<li>支持 HTTPS 协议 
</li>
				<li>支持代理服务器等</li>
		</ul>
		<p>下面将逐一介绍怎样使用这些功能。首先，我们必须安装好 HttpClient。</p>
		<ul>
				<li>HttpClient 可以在<a href="http://jakarta.apache.org/commons/httpclient/downloads.html"><font color="#5c81a7">http://jakarta.apache.org/commons/httpclient/downloads.html</font></a>下载 
</li>
				<li>HttpClient 用到了 Apache Jakarta common 下的子项目 logging，你可以从这个地址<a href="http://jakarta.apache.org/site/downloads/downloads_commons-logging.cgi"><font color="#5c81a7">http://jakarta.apache.org/site/downloads/downloads_commons-logging.cgi</font></a>下载到 common logging，从下载后的压缩包中取出 commons-logging.jar 加到 CLASSPATH 中 
</li>
				<li>HttpClient 用到了 Apache Jakarta common 下的子项目 codec，你可以从这个地址<a href="http://jakarta.apache.org/site/downloads/downloads_commons-codec"><font color="#5c81a7">http://jakarta.apache.org/site/downloads/downloads_commons-codec</font></a>.cgi 下载到最新的 common codec，从下载后的压缩包中取出 commons-codec-1.x.jar 加到 CLASSPATH 中</li>
		</ul>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" src="http://www.crackj2ee.com/Article/UploadFiles/200511/20051125151629941.gif" width="823" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/#main">
																				<b>
																						<font color="#996699">
																						</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="IDANDG0">
						<span class="atitle">
								<font face="Arial" size="4">HttpClient 基本功能的使用</font>
						</span>
				</a>
		</p>
		<p>
				<a name="IDASDG0">
						<span class="smalltitle">
								<strong>
										<font face="Arial" size="3">GET 方法</font>
								</strong>
						</span>
				</a>
		</p>
		<p>使用 HttpClient 需要以下 6 个步骤：</p>
		<p>1. 创建 HttpClient 的实例</p>
		<p>2. 创建某种连接方法的实例，在这里是 GetMethod。在 GetMethod 的构造函数中传入待连接的地址</p>
		<p>3. 调用第一步中创建好的实例的 execute 方法来执行第二步中创建好的 method 实例</p>
		<p>4. 读 response</p>
		<p>5. 释放连接。无论执行方法是否成功，都必须释放连接</p>
		<p>6. 对得到后的内容进行处理</p>
		<p>根据以上步骤，我们来编写用GET方法来取得某网页内容的代码。</p>
		<ul>
				<li>大部分情况下 HttpClient 默认的构造函数已经足够使用。 
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">HttpClient httpClient = new HttpClient();
</font></code></pre></td></tr></tbody></table><br /></li>
				<li>创建GET方法的实例。在GET方法的构造函数中传入待连接的地址即可。用GetMethod将会自动处理转发过程，如果想要把自动处理转发过程去掉的话，可以调用方法setFollowRedirects(false)。 
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">GetMethod getMethod = new GetMethod("http://www.ibm.com/");
</font></code></pre></td></tr></tbody></table><br /></li>
				<li>调用实例httpClient的executeMethod方法来执行getMethod。由于是执行在网络上的程序，在运行executeMethod方法的时候，需要处理两个异常，分别是HttpException和IOException。引起第一种异常的原因主要可能是在构造getMethod的时候传入的协议不对，比如不小心将"http"写成"htp"，或者服务器端返回的内容不正常等，并且该异常发生是不可恢复的；第二种异常一般是由于网络原因引起的异常，对于这种异常 （IOException），HttpClient会根据你指定的恢复策略自动试着重新执行executeMethod方法。HttpClient的恢复策略可以自定义（通过实现接口HttpMethodRetryHandler来实现）。通过httpClient的方法setParameter设置你实现的恢复策略，本文中使用的是系统提供的默认恢复策略，该策略在碰到第二类异常的时候将自动重试3次。executeMethod返回值是一个整数，表示了执行该方法后服务器返回的状态码，该状态码能表示出该方法执行是否成功、需要认证或者页面发生了跳转（默认状态下GetMethod的实例是自动处理跳转的）等。 
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">//设置成了默认的恢复策略，在发生异常时候将自动重试3次，在这里你也可以设置成自定义的恢复策略
getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, 
    		new DefaultHttpMethodRetryHandler()); 
//执行getMethod
int statusCode = client.executeMethod(getMethod);
if (statusCode != HttpStatus.SC_OK) {
  System.err.println("Method failed: " + getMethod.getStatusLine());
}
</font></code></pre></td></tr></tbody></table><br /></li>
				<li>在返回的状态码正确后，即可取得内容。取得目标地址的内容有三种方法：第一种，getResponseBody，该方法返回的是目标的二进制的byte流；第二种，getResponseBodyAsString，这个方法返回的是String类型，值得注意的是该方法返回的String的编码是根据系统默认的编码方式，所以返回的String值可能编码类型有误，在本文的"字符编码"部分中将对此做详细介绍；第三种，getResponseBodyAsStream，这个方法对于目标地址中有大量数据需要传输是最佳的。在这里我们使用了最简单的getResponseBody方法。 
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">byte[] responseBody = method.getResponseBody();
</font></code></pre></td></tr></tbody></table><br /></li>
				<li>释放连接。无论执行方法是否成功，都必须释放连接。 
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">method.releaseConnection();
</font></code></pre></td></tr></tbody></table><br /></li>
				<li>处理内容。在这一步中根据你的需要处理内容，在例子中只是简单的将内容打印到控制台。 
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">System.out.println(new String(responseBody));
</font></code></pre></td></tr></tbody></table><br /></li>
		</ul>
		<p>下面是程序的完整代码，这些代码也可在附件中的test.GetSample中找到。</p>
		<br />
		<a name="IDA5EG0">
				<b>
				</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">package test;
import java.io.IOException;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;

public class GetSample{
  public static void main(String[] args) {
  //构造HttpClient的实例
  HttpClient httpClient = new HttpClient();
  //创建GET方法的实例
  GetMethod getMethod = new GetMethod("http://www.ibm.com");
  //使用系统提供的默认的恢复策略
  getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
    new DefaultHttpMethodRetryHandler());
  try {
   //执行getMethod
   int statusCode = httpClient.executeMethod(getMethod);
   if (statusCode != HttpStatus.SC_OK) {
    System.err.println("Method failed: "
      + getMethod.getStatusLine());
   }
   //读取内容 
   byte[] responseBody = getMethod.getResponseBody();
   //处理内容
   System.out.println(new String(responseBody));
  } catch (HttpException e) {
   //发生致命的异常，可能是协议不对或者返回的内容有问题
   System.out.println("Please check your provided http address!");
   e.printStackTrace();
  } catch (IOException e) {
   //发生网络异常
   e.printStackTrace();
  } finally {
   //释放连接
   getMethod.releaseConnection();
  }
 }
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<a name="IDAGFG0">
						<span class="smalltitle">
								<strong>
										<font face="Arial" size="3">POST方法</font>
								</strong>
						</span>
				</a>
		</p>
		<p>根据RFC2616，对POST的解释如下：POST方法用来向目的服务器发出请求，要求它接受被附在请求后的实体，并把它当作请求队列（Request-Line）中请求URI所指定资源的附加新子项。POST被设计成用统一的方法实现下列功能：</p>
		<ul>
				<li>对现有资源的注释（Annotation of existing resources） 
</li>
				<li>向电子公告栏、新闻组，邮件列表或类似讨论组发送消息 
</li>
				<li>提交数据块，如将表单的结果提交给数据处理过程 
</li>
				<li>通过附加操作来扩展数据库</li>
		</ul>
		<p>调用HttpClient中的PostMethod与GetMethod类似，除了设置PostMethod的实例与GetMethod有些不同之外，剩下的步骤都差不多。在下面的例子中，省去了与GetMethod相同的步骤，只说明与上面不同的地方，并以登录清华大学BBS为例子进行说明。</p>
		<ul>
				<li>构造PostMethod之前的步骤都相同，与GetMethod一样，构造PostMethod也需要一个URI参数，在本例中，登录的地址是http://www.newsmth.net/bbslogin2.php。在创建了PostMethod的实例之后，需要给method实例填充表单的值，在BBS的登录表单中需要有两个域，第一个是用户名（域名叫id），第二个是密码（域名叫passwd）。表单中的域用类NameValuePair来表示，该类的构造函数第一个参数是域名，第二参数是该域的值；将表单所有的值设置到PostMethod中用方法setRequestBody。另外由于BBS登录成功后会转向另外一个页面，但是HttpClient对于要求接受后继服务的请求，比如POST和PUT，不支持自动转发，因此需要自己对页面转向做处理。具体的页面转向处理请参见下面的"自动转向"部分。代码如下： 
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">String url = "http://www.newsmth.net/bbslogin2.php";
PostMethod postMethod = new PostMethod(url);
// 填入各个表单域的值
NameValuePair[] data = { new NameValuePair("id", "youUserName"),				
new NameValuePair("passwd", "yourPwd") };
// 将表单的值放入postMethod中
postMethod.setRequestBody(data);
// 执行postMethod
int statusCode = httpClient.executeMethod(postMethod);
// HttpClient对于要求接受后继服务的请求，象POST和PUT等不能自动处理转发
// 301或者302
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || 
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
    // 从头中取出转向的地址
    Header locationHeader = postMethod.getResponseHeader("location");
    String location = null;
    if (locationHeader != null) {
     location = locationHeader.getValue();
     System.out.println("The page was redirected to:" + location);
    } else {
     System.err.println("Location field value is null.");
    }
    return;
}
</font></code></pre></td></tr></tbody></table><br /></li>
		</ul>
		<p>完整的程序代码请参见附件中的test.PostSample</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" src="http://www.crackj2ee.com/Article/UploadFiles/200511/20051125151629941.gif" width="823" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/#main">
																				<b>
																						<font color="#996699">
																						</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="IDAZFG0">
						<span class="atitle">
								<font face="Arial" size="4">使用HttpClient过程中常见的一些问题</font>
						</span>
				</a>
		</p>
		<p>下面介绍在使用HttpClient过程中常见的一些问题。</p>
		<p>
				<a name="IDA5FG0">
						<span class="smalltitle">
								<strong>
										<font face="Arial" size="3">字符编码</font>
								</strong>
						</span>
				</a>
		</p>
		<p>某目标页的编码可能出现在两个地方，第一个地方是服务器返回的http头中，另外一个地方是得到的html/xml页面中。</p>
		<ul>
				<li>在http头的Content-Type字段可能会包含字符编码信息。例如可能返回的头会包含这样子的信息：Content-Type: text/html; charset=UTF-8。这个头信息表明该页的编码是UTF-8，但是服务器返回的头信息未必与内容能匹配上。比如对于一些双字节语言国家，可能服务器返回的编码类型是UTF-8，但真正的内容却不是UTF-8编码的，因此需要在另外的地方去得到页面的编码信息；但是如果服务器返回的编码不是UTF-8，而是具体的一些编码，比如gb2312等，那服务器返回的可能是正确的编码信息。通过method对象的getResponseCharSet()方法就可以得到http头中的编码信息。 
</li>
				<li>对于象xml或者html这样的文件，允许作者在页面中直接指定编码类型。比如在html中会有&lt;meta http-equiv="Content-Type" content="text/html; charset=gb2312"/&gt;这样的标签；或者在xml中会有&lt;?xml version="1.0" encoding="gb2312"?&gt;这样的标签，在这些情况下，可能与http头中返回的编码信息冲突，需要用户自己判断到底那种编码类型应该是真正的编码。</li>
		</ul>
		<p>
				<a name="IDAIGG0">
						<span class="smalltitle">
								<strong>
										<font face="Arial" size="3">自动转向</font>
								</strong>
						</span>
				</a>
		</p>
		<p>根据RFC2616中对自动转向的定义，主要有两种：301和302。301表示永久的移走（Moved Permanently），当返回的是301，则表示请求的资源已经被移到一个固定的新地方，任何向该地址发起请求都会被转到新的地址上。302表示暂时的转向，比如在服务器端的servlet程序调用了sendRedirect方法，则在客户端就会得到一个302的代码，这时服务器返回的头信息中location的值就是sendRedirect转向的目标地址。</p>
		<p>HttpClient支持自动转向处理，但是象POST和PUT方式这种要求接受后继服务的请求方式，暂时不支持自动转向，因此如果碰到POST方式提交后返回的是301或者302的话需要自己处理。就像刚才在POSTMethod中举的例子：如果想进入登录BBS后的页面，必须重新发起登录的请求，请求的地址可以在头字段location中得到。不过需要注意的是，有时候location返回的可能是相对路径，因此需要对location返回的值做一些处理才可以发起向新地址的请求。</p>
		<p>另外除了在头中包含的信息可能使页面发生重定向外，在页面中也有可能会发生页面的重定向。引起页面自动转发的标签是：&lt;meta http-equiv="refresh" content="5; url=http://www.ibm.com/us"&gt;。如果你想在程序中也处理这种情况的话得自己分析页面来实现转向。需要注意的是，在上面那个标签中url的值也可以是一个相对地址，如果是这样的话，需要对它做一些处理后才可以转发。</p>
		<p>
				<a name="IDAQGG0">
						<span class="smalltitle">
								<strong>
										<font face="Arial" size="3">处理HTTPS协议</font>
								</strong>
						</span>
				</a>
		</p>
		<p>HttpClient提供了对SSL的支持，在使用SSL之前必须安装JSSE。在Sun提供的1.4以后的版本中，JSSE已经集成到JDK中，如果你使用的是JDK1.4以前的版本则必须安装JSSE。JSSE不同的厂家有不同的实现。下面介绍怎么使用HttpClient来打开Https连接。这里有两种方法可以打开https连接，第一种就是得到服务器颁发的证书，然后导入到本地的keystore中；另外一种办法就是通过扩展HttpClient的类来实现自动接受证书。</p>
		<p>方法1，取得证书，并导入本地的keystore：</p>
		<ul>
				<li>安装JSSE （如果你使用的JDK版本是1.4或者1.4以上就可以跳过这一步）。本文以IBM的JSSE为例子说明。先到IBM网站上下载JSSE的安装包。然后解压开之后将ibmjsse.jar包拷贝到&lt;java-home&gt;\lib\ext\目录下。 
</li>
				<li>取得并且导入证书。证书可以通过IE来获得： 
<p>1． 用IE打开需要连接的https网址，会弹出如下对话框：</p><br /><a name="IDA3GG0"><b></b></a><br /><img height="301" src="http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/images/image001.png" width="384" border="0" /><br /><p>2． 单击"View Certificate"，在弹出的对话框中选择"Details"，然后再单击"Copy to File"，根据提供的向导生成待访问网页的证书文件</p><br /><a name="IDALHG0"><b></b></a><br /><img height="476" src="http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/images/image003.png" width="409" border="0" /><br /><p>3． 向导第一步，欢迎界面，直接单击"Next"，</p><br /><a name="IDAZHG0"><b></b></a><br /><img height="386" src="http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/images/image005.png" width="503" border="0" /><br /><p>4． 向导第二步，选择导出的文件格式，默认，单击"Next"，</p><br /><a name="IDAKAO0"><b></b></a><br /><img height="386" src="http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/images/image007.png" width="503" border="0" /><br /><p>5． 向导第三步，输入导出的文件名，输入后，单击"Next"，</p><br /><a name="IDAYAO0"><b></b></a><br /><img height="454" src="http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/images/image009.png" width="506" border="0" /><br /><p>6． 向导第四步，单击"Finish"，完成向导</p><br /><a name="IDAGBO0"><b></b></a><br /><img height="386" src="http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/images/image011.png" width="503" border="0" /><br /><p>7． 最后弹出一个对话框，显示导出成功</p><br /><a name="IDAUBO0"><b></b></a><br /><img height="100" src="http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/images/image013.png" width="183" border="0" /><br /></li>
				<li>
						<p>用keytool工具把刚才导出的证书倒入本地keystore。Keytool命令在&lt;java-home&gt;\bin\下，打开命令行窗口，并到&lt;java-home&gt;\lib\security\目录下，运行下面的命令：</p>
						<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
								<tbody>
										<tr>
												<td>
														<pre>
																<code class="section">
																		<font face="Lucida Console">keytool -import -noprompt -keystore cacerts -storepass changeit -alias yourEntry1 -file your.cer
</font>
																</code>
														</pre>
												</td>
										</tr>
								</tbody>
						</table>
						<br />
						<p>其中参数alias后跟的值是当前证书在keystore中的唯一标识符，但是大小写不区分；参数file后跟的是刚才通过IE导出的证书所在的路径和文件名；如果你想删除刚才导入到keystore的证书，可以用命令：</p>
						<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
								<tbody>
										<tr>
												<td>
														<pre>
																<code class="section">
																		<font face="Lucida Console">keytool -delete -keystore cacerts -storepass changeit -alias yourEntry1
</font>
																</code>
														</pre>
												</td>
										</tr>
								</tbody>
						</table>
						<br />
				</li>
				<li>写程序访问https地址。如果想测试是否能连上https，只需要稍改一下GetSample例子，把请求的目标变成一个https地址。 
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">GetMethod getMethod = new GetMethod("https://www.yourdomain.com");
</font></code></pre></td></tr></tbody></table><br /><p>运行该程序可能出现的问题：</p><p>1. 抛出异常java.net.SocketException: Algorithm SSL not available。出现这个异常可能是因为没有加JSSEProvider，如果用的是IBM的JSSE Provider，在程序中加入这样的一行：</p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console"> if(Security.getProvider("com.ibm.jsse.IBMJSSEProvider") == null)
 Security.addProvider(new IBMJSSEProvider());
 </font></code></pre></td></tr></tbody></table><br /><p>或者也可以打开&lt;java-home&gt;\lib\security\java.security，在行</p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">security.provider.1=sun.security.provider.Sun
security.provider.2=com.ibm.crypto.provider.IBMJCE
</font></code></pre></td></tr></tbody></table><br /><p>后面加入security.provider.3=com.ibm.jsse.IBMJSSEProvider</p><p>2. 抛出异常java.net.SocketException: SSL implementation not available。出现这个异常可能是你没有把ibmjsse.jar拷贝到&lt;java-home&gt;\lib\ext\目录下。</p><p>3. 抛出异常javax.net.ssl.SSLHandshakeException: unknown certificate。出现这个异常表明你的JSSE应该已经安装正确，但是可能因为你没有把证书导入到当前运行JRE的keystore中，请按照前面介绍的步骤来导入你的证书。</p></li>
		</ul>
		<p>方法２，扩展HttpClient类实现自动接受证书</p>
		<p>因为这种方法自动接收所有证书，因此存在一定的安全问题，所以在使用这种方法前请仔细考虑您的系统的安全需求。具体的步骤如下：</p>
		<ul>
				<li>提供一个自定义的socket factory（test.MySecureProtocolSocketFactory）。这个自定义的类必须实现接口org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory，在实现接口的类中调用自定义的X509TrustManager(test.MyX509TrustManager)，这两个类可以在随本文带的附件中得到 
</li>
				<li>创建一个org.apache.commons.httpclient.protocol.Protocol的实例，指定协议名称和默认的端口号 
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">Protocol myhttps = new Protocol("https", new MySecureProtocolSocketFactory (), 443);
</font></code></pre></td></tr></tbody></table><br /></li>
				<li>注册刚才创建的https协议对象 
<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">Protocol.registerProtocol("https ", myhttps);
</font></code></pre></td></tr></tbody></table><br /></li>
				<li>然后按照普通编程方式打开https的目标地址，代码请参见test.NoCertificationHttpsGetSample</li>
		</ul>
		<p>
				<a name="IDAJDO0">
						<span class="smalltitle">
								<strong>
										<font face="Arial" size="3">处理代理服务器</font>
								</strong>
						</span>
				</a>
		</p>
		<p>HttpClient中使用代理服务器非常简单，调用HttpClient中setProxy方法就可以，方法的第一个参数是代理服务器地址，第二个参数是端口号。另外HttpClient也支持SOCKS代理。</p>
		<br />
		<a name="IDAPDO0">
				<b>
				</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">httpClient.getHostConfiguration().setProxy(hostName,port);
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<font face="Lucida Console">
												<img height="1" src="http://www.crackj2ee.com/Article/UploadFiles/200511/20051125151629941.gif" width="823" />
										</font>
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<font face="Lucida Console">
																				<br />
																		</font>
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/#main">
																				<b>
																						<font color="#996699">
																						</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="IDAWDO0">
						<span class="atitle">
								<font face="Arial" size="4">结论</font>
						</span>
				</a>
		</p>
		<p>从上面的介绍中，可以知道HttpClient对http协议支持非常好，使用起来很简单，版本更新快，功能也很强大，具有足够的灵活性和扩展性。对于想在Java应用中直接访问http资源的编程人员来说，HttpClient是一个不可多得的好工具。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" src="http://www.crackj2ee.com/Article/UploadFiles/200511/20051125151629941.gif" width="823" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/#main">
																				<b>
																						<font color="#996699">
																						</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="resources">
						<span class="atitle">
								<font face="Arial" size="4">参考资料 </font>
						</span>
				</a>
		</p>
		<ul>
				<li>本文样码下载：<a href="http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/src.zip"><font color="#5c81a7">src.zip</font></a></li>
				<li>Commons logging包含了各种各样的日志API的实现，读者可以通过站点http://jakarta.apache.org/commons/logging/得到详细的内容<br /><br /></li>
				<li>Commons codec包含了一些一般的解码/编码算法。包含了语音编码、十六进制、Base64和URL编码等，通过http://jakarta.apache.org/commons/codec/可以得到详细的内容<br /><br /></li>
				<li>rfc2616是关于HTTP/1.1的文档，可以在http://www.faqs.org/rfcs/rfc2616.html上得到详细的内容，另外rfc1945是关于HTTP/1.0的文档，通过http://www.faqs.org/rfcs/rfc1945.html可以得到详细内容<br /><br /></li>
				<li>SSL――SSL 是由 Netscape Communications Corporation 于 1994 年开发的，而 TLS V1.0 是由 Internet Engineering Task Force（IETF）定义的标准，它基于 SSL V3.0，并且在使用的加密算法上与其有些许的不同。例如，SSL 使用 Message Authentication Code（MAC）算法来生成完整性校验值，而 TLS 应用密钥的 Hashing for Message Authentication Code（HMAC）算法。<br /><br /></li>
				<li>IBM JSSE提供了SSL（Secure Sockets Layer）和TLS（Transport Layer Security）的java实现，在http://www-03.ibm.com/servers/eserver/zseries/software/java/jsse.html中可以得到详细的信息<br /><br /></li>
				<li>Keytool是一个管理密钥和证书的工具。关于它详细的使用信息可以在http://www.doc.ic.ac.uk/csg/java/1.3.1docs/tooldocs/solaris/keytool.html上得到<br /><br /></li>
				<li>HTTPClient的主页是http://jakarta.apache.org/commons/httpclient/，你可以在这里得到关于HttpClient更加详细的信息<br /></li>
		</ul>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" src="http://www.crackj2ee.com/Article/UploadFiles/200511/20051125151629941.gif" width="823" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/#main">
																				<b>
																						<font color="#996699">
																						</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<span class="atitle">
						<font face="Arial" size="4">
						</font>
				</span> </p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td colspan="2">
										<font face="Arial" size="4">
												<img height="5" src="http://www.crackj2ee.com/Article/UploadFiles/200511/20051125151632968.gif" width="823" />
										</font>
								</td>
						</tr>
						<tr valign="top" align="left">
								<td>
										<p>
												<font face="Arial" size="4">
												</font>
										</p>
								</td>
								<td>
										<p> </p>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td colspan="2">
										<img height="5" src="http://www.crackj2ee.com/Article/UploadFiles/200511/20051125151632968.gif" width="823" />
								</td>
						</tr>
						<tr valign="top" align="left">
								<td>
										<p>
										</p>
								</td>
								<td>
										<p> </p>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/ASONG/aggbug/51537.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2006-06-08 22:47 <a href="http://www.blogjava.net/ASONG/articles/51537.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java/JSP中文乱码问题解决心得</title><link>http://www.blogjava.net/ASONG/articles/51121.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Wed, 07 Jun 2006 06:36:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/51121.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/51121.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/51121.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/51121.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/51121.html</trackback:ping><description><![CDATA[
		<p>作者信息：liqian  liqianbnu@126.com   http://202.112.88.39/liqian/<br />文章原始出处：http://202.112.88.39/liqian/000271.html</p>
		<p>自从接触Java和JSP以来，就不断与Java的中文乱码问题打交道，现在终于得到了彻底的解决，现将我们的解决心得与大家共享。</p>
		<p>
				<b>一、Java中文问题的由来</b>
		</p>
		<p>Java的内核和class文件是基于unicode的，这使Java程序具有良好的跨平台性，但也带来了一些中文乱码问题的麻烦。原因主要有两方面，Java和JSP文件本身编译时产生的乱码问题和Java程序于其他媒介交互产生的乱码问题。</p>
		<p>首先Java（包括JSP）源文件中很可能包含有中文，而Java和JSP源文件的保存方式是基于字节流的，如果Java和JSP编译成class文件过程中，使用的编码方式与源文件的编码不一致，就会出现乱码。基于这种乱码，建议在Java文件中尽量不要写中文（注释部分不参与编译，写中文没关系），如果必须写的话，尽量手动带参数－ecoding GBK或－ecoding gb2312编译；对于JSP，在文件头加上&lt;%@ page contentType="text/html;charset=GBK"%&gt;或&lt;%@ page contentType="text/html;charset=gb2312"%&gt;基本上就能解决这类乱码问题。</p>
		<p>本文要重点讨论的是第二类乱码，即Java程序与其他存储媒介交互时产生的乱码。很多存储媒介，如数据库，文件，流等的存储方式都是基于字节流的，Java程序与这些媒介交互时就会发生字符(char)与字节(byte)之间的转换，具体情况如下：</p>
		<p>从页面form提交数据到java程序 byte－&gt;char<br />从java程序到页面显示 char—&gt;byte</p>
		<p>从数据库到java程序 byte—&gt;char<br />从java程序到数据库 char—&gt;byte</p>
		<p>从文件到java程序 byte－&gt;char<br />从java程序到文件 char－&gt;byte</p>
		<p>从流到java程序 byte－&gt;char<br />从java程序到流 char－&gt;byte</p>
		<p>如果在以上转换过程中使用的编码方式与字节原有的编码不一致，很可能就会出现乱码。</p>
		<p>
				<b>二、解决方法</b>
				<br />
				<br />前面已经提到了Java程序与其他媒介交互时字符和字节的转换过程，如果这些转换过程中容易产生乱码。解决这些乱码问题的关键在于确保转换时使用的编码方式与字节原有的编码方式保持一致，下面分别论述（Java或JSP自身产生的乱码请参看第一部分）。</p>
		<p>
				<b>1、JSP与页面参数之间的乱码</b>
				<br />JSP获取页面参数时一般采用系统默认的编码方式，如果页面参数的编码类型和系统默认的编码类型不一致，很可能就会出现乱码。解决这类乱码问题的基本方法是在页面获取参数之前，强制指定request获取参数的编码方式：request.setCharacterEncoding("GBK")或request.setCharacterEncoding("gb2312")。<br />如果在JSP将变量输出到页面时出现了乱码，可以通过设置response.setContentType("text/html;charset=GBK")或response.setContentType("text/html;charset=gb2312")解决。<br />如果不想在每个文件里都写这样两句话，更简洁的办法是使用Servlet规范中的过虑器指定编码，过滤器的在web.xml中的典型配置和主要代码如下：<br />web.xml:</p>
		<p>&lt;filter&gt;<br />&lt;filter-name&gt;CharacterEncodingFilter&lt;/filter-name&gt;<br />&lt;filter-class&gt;net.vschool.web.CharacterEncodingFilter&lt;/filter-class&gt;<br />&lt;init-param&gt;<br />&lt;param-name&gt;encoding&lt;/param-name&gt;<br />&lt;param-value&gt;GBK&lt;/param-value&gt;<br />&lt;/init-param&gt;<br />&lt;/filter&gt;<br />&lt;filter-mapping&gt;<br />&lt;filter-name&gt;CharacterEncodingFilter&lt;/filter-name&gt;<br />&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br />&lt;/filter-mapping&gt;</p>
		<p>CharacterEncodingFilter.java:</p>
		<p>public class CharacterEncodingFilter implements Filter <br />{</p>
		<p>protected String encoding = null; </p>
		<p>public void init(FilterConfig filterConfig) throws ServletException <br />{<br />this.encoding = filterConfig.getInitParameter("encoding");<br />}</p>
		<p>public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException <br />{<br />request.setCharacterEncoding(encoding);<br />response.setContentType("text/html;charset="+encoding);<br />chain.doFilter(request, response);<br />}</p>
		<p>}</p>
		<p>
				<br />
				<b>2、Java与数据库之间的乱码</b>
				<br />大部分数据库都支持以unicode编码方式，所以解决Java与数据库之间的乱码问题比较明智的方式是直接使用unicode编码与数据库交互。很多数据库驱动自动支持unicode，如Microsoft的SQLServer驱动。其他大部分数据库驱动，可以在驱动的url参数中指定，如如mm的mysql驱动：jdbc:mysql://localhost/WEBCLDB?useUnicode=true&amp;characterEncoding=GBK。</p>
		<p>
				<b>3、Java与文件/流之间的乱码</b>
				<br />Java读写文件最常用的类是FileInputStream/FileOutputStream和FileReader/FileWriter。其中FileInputStream和FileOutputStream是基于字节流的，常用于读写二进制文件。读写字符文件建议使用基于字符的FileReader和FileWriter，省去了字节与字符之间的转换。但这两个类的构造函数默认使用系统的编码方式，如果文件内容与系统编码方式不一致，可能会出现乱码。在这种情况下，建议使用FileReader和FileWriter的父类：InputStreamReader/OutputStreamWriter，它们也是基于字符的，但在构造函数中可以指定编码类型：InputStreamReader(InputStream in, Charset cs) 和OutputStreamWriter(OutputStream out, Charset cs)。 </p>
		<p>
				<b>4、其他</b>
				<br />上面提到的方法应该能解决大部分乱码问题，如果在其他地方还出现乱码，可能需要手动修改代码。解决Java乱码问题的关键在于在字节与字符的转换过程中，你必须知道原来字节或转换后的字节的编码方式，转换时采用的编码必须与这个编码方式保持一致。我们以前使用Resin服务器，使用smartUpload组件上传文件，上传文件同时传递的中文参数获取没有乱码问题。当在Linux中把Resin设置成服务后，上传文件同时的中文参数获取出现了乱码。这个问题困扰了我们很久，后来我们分析smartUpload组件的源文件，因为文件上传采用的是字节流的方式，里面包含的参数名称和值也是字节流的方式传递的。smartUpload组件读取字节流后再将参数名称和值从字节流中解析出来，问题就出现在smartUpload将字节流转换成字符串时采用了系统默认的编码，而将Resin设置成服务后，系统默认的编码可能发生了改变，因此出现了乱码。后来，我们更改了smartUpload的源文件，增加了一个属性charset和setCharset(String)方法，将upload()方法中提取参数语句：<br />String value = new String(m_binArray, m_startData, (m_endData - m_startData) + 1 );<br />改成了<br />String value = new String(m_binArray, m_startData, (m_endData - m_startData) + 1, charset );<br />终于解决了这个乱码问题。</p>
<img src ="http://www.blogjava.net/ASONG/aggbug/51121.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2006-06-07 14:36 <a href="http://www.blogjava.net/ASONG/articles/51121.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用HttpURLConnection将字符流发送到servlet</title><link>http://www.blogjava.net/ASONG/articles/50758.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Tue, 06 Jun 2006 05:41:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/50758.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/50758.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/50758.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/50758.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/50758.html</trackback:ping><description><![CDATA[使用HttpURLConnection将字符流发送到servlet.<br />此功能将字符串以流的形式发送给一个servlet.<br /><br />代码如下:<br /><br />public String postStringToUrl(String Str, String urlStr){<br />        BufferedWriter bWriter = null;<br />        HttpURLConnection urlConn = null;<br />        String message = Str;<br />        String target = urlStr;<br />        String sCurrentLine = "";<br />        String sTotalString = "";<br />        int res = 0;<br />        try{<br />            byte[] byteBuffer = message.getBytes("GB2312");<br />            URL httpurl = new URL(target);<br />            urlConn = (HttpURLConnection)httpurl.openConnection();<br />            urlConn.setRequestProperty("Content-Type","application/octet-stream");<br />            urlConn.setRequestProperty("Content-length", ""    + byteBuffer.length);<br />            urlConn.setRequestProperty("pure-data", "yes");<br />            urlConn.setRequestProperty("Connection","Keep-Alive");<br />            <br />            urlConn.setDoOutput(true);<br />            OutputStream out = urlConn.getOutputStream();<br />            <br />            try{<br />                out.write(byteBuffer);<br />            }<br />            finally{<br />                out.flush();<br />                out.close();<br />                message = null;<br />                target = null;<br />            }<br />    <br />            //url请求返回code值<br />            res = urlConn.getResponseCode();<br />            if (res == 200) {<br />                java.io.InputStream is = urlConn.getInputStream();<br />                BufferedReader reader = new BufferedReader(<br />                        new InputStreamReader(is));<br />                while ((sCurrentLine = reader.readLine()) != null)<br />                    if (sCurrentLine.length() &gt; 0)<br />                        sTotalString = sTotalString + sCurrentLine.trim();<br />                String tmpStr = new String(sTotalString.getBytes("GB2312"));<br />                sTotalString = tmpStr;<br />            } else {<br />                sTotalString = "远程服务器连接失败,错误代码:"+res; <br /><br />            }<br /><br />            if (bWriter != null)<br />                bWriter.close();<br />                <br />        }<br />        catch(Exception e){<br />            sTotalString = "连接服务器失败.";<br />            e.printStackTrace();<br />        }<br />        return sTotalString;<br />    }<br /><img src ="http://www.blogjava.net/ASONG/aggbug/50758.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2006-06-06 13:41 <a href="http://www.blogjava.net/ASONG/articles/50758.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> XML详解----Schema(精)[原创]</title><link>http://www.blogjava.net/ASONG/articles/48843.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Mon, 29 May 2006 14:49:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/48843.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/48843.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/48843.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/48843.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/48843.html</trackback:ping><description><![CDATA[
		<font color="#ff0000">有人会问，DTD和Schema都是对XML文档的一种约束，为什么不就选其中之一，而又有Schema呢。因为DTD安全度太低了，也就是说它的约束定义能力不足，无法对XML实例文档做出更细致的语义限制。其实细心的人会发现，在DTD中，只有一个数据类型，就是PCDATA（用在元素中）和CDATA（用在属性中），在里面写日期也行，数字还行，字符更是没问题。而Schema正是针对这些DTD的缺点而设计的，Schema是完全使用XML作为描述手段，具有很强的描述能力，扩展能力和处理维护能力等。下面让我们看一个简单的例子吧：<br />hello.xml<br />-------------------<br />&lt;?xml version="1.0"?&gt; <br />&lt;greeting&gt;Hello World!!&lt;/greeting&gt; </font>
		<p>
		</p>
		<p>说明：<br />一个根元素：greeting；且这个元素不含属性，无子元素，内容是字符串。</p>
		<p>hello.xsd<br />－－－－－－－－－－<br />&lt;?xml version="1.0"?&gt; <br />&lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />&lt;xsd:element name="greeting" type="xsd:string"/&gt;<br />&lt;/xsd:schema&gt;<br />说明：<br />XML Schema文档后缀名是.xsd，完全符合XML语法，根元素是schema，命名空间xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema</font></a>，用元素&lt;element&gt;定义实例文档中的元素，如greeting。xsd:string就是定义的数据类型了，其中的数据类型有很多，比如：int,double,dateTime,Boolean,long,integer,float,等，总之Java等语言里有的数据类型它都有，但要以“xsd:”开头。<br />让我们再看一个里面有子无素的例子：<br />customer.xml<br />-----------<br />&lt;customer&gt;<br />&lt;name&gt;teiki&lt;/name&gt;<br />&lt;address&gt;No.237, Road Waitan, Shanghai&lt;/address&gt;<br />&lt;/customer&gt;<br />则可以写出以下的XML Schema文档：<br />customer.xsd<br />----------------<br />1: &lt;?xml version="1.0"?&gt;<br />2: &lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />3: &lt;xsd:element name="customer"&gt;<br />4: &lt;xsd:complexType&gt;<br />5: &lt;xsd:sequence&gt;<br />6: &lt;xsd:element name="name" type="xsd:string"/&gt;<br />7: &lt;xsd:element name="address" type="xsd:string" /&gt;<br />8: &lt;/xsd:sequence&gt;<br />9: &lt;/xsd:complexType&gt;<br />10: &lt;/xsd:element&gt;<br />11: &lt;/xsd:schema&gt;<br />说明：<br />实例文档customer.xml中，&lt;customer&gt;元素含有两个子元素，在Schema中凡是有两个以上的子元素，就认为是复杂类型的，所以我们在Schema文档中采用ComplexType来定义该元素。表示有多个XML子元素。<br />sequence表示子元素依次出现的顺序。<br />如果有多层子元素怎么办呢，同样的道理，有几层写几层，一直往下嵌就OK了，这里我要说的是另一种方法，结构看上去会清晰一些。<br />address.xml<br />---------------<br />&lt;customer&gt;<br />&lt;name&gt;Teiki&lt;/name&gt;<br />&lt;address&gt;<br />&lt;!-- address追加一个地址子元素 --&gt;<br />&lt;prefecture&gt;Zhejiang&lt;/prefecture&gt;<br />&lt;city&gt;Hangzhou&lt;/city&gt;<br />&lt;street&gt;Xilu Road, No.121, 7F&lt;/street&gt;<br />&lt;/address&gt;<br />&lt;/customer&gt;<br />下面就是采用ref元素来编写的这个Schema文档：<br />address2.xsd<br />----------------------<br />1: &lt;?xm l version="1.0"?&gt;<br />2: &lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />3:<br />4: &lt;xsd:element name="customer"&gt;<br />5: &lt;xsd:complexType&gt;<br />6: &lt;xsd:sequence&gt;<br />7: &lt;xsd:element name="name" type="xsd:string"/&gt;<br />8: &lt;xsd:element ref="address"/&gt;<br />9: &lt;/xsd:sequence&gt;<br />10: &lt;/xsd:complexType&gt;<br />11: &lt;/xsd:element&gt;<br />12:<br />13: &lt;xsd:element name="address"&gt;<br />14: &lt;xsd:complexType&gt;<br />15: &lt;xsd:sequence&gt;<br />16: &lt;xsd:element name="prefecture" type="xsd:string"/&gt;<br />17: &lt;xsd:element name="city" type="xsd:string" /&gt;<br />18: &lt;xsd:element name="street" type="xsd:string" /&gt;<br />19: &lt;/xsd:sequence&gt;<br />20: &lt;/xsd:complexType&gt;<br />21: &lt;/xsd:element&gt;<br />22:<br />23: &lt;/xsd:schema&gt;<br />说明：<br />如果按正常的写法，应该把13－21行的内容替换到每8行去，但这里使用ref元素可以直接将其指向另一个模块，使文档更加具有可读性。<br />如果元素中包含属性怎么办呢？一样简单，只要在定义完子元素的后面再着定义属性就行了。定义属性用“attribute”，还是举个例子吧<br />customer2.xml<br />---------------<br />&lt;customer id="001718"&gt;<br />&lt;name&gt;Teiki&lt;/name&gt;<br />&lt;address&gt;No.237, Road Waitan, Shanghai&lt;/address&gt;<br />&lt;/customer&gt;<br />这个例子和上面的一个例子差不多，只是在元素customer中设了一个属性id。<br />customer2.xsd<br />------------------<br />1: &lt;?xml version="1.0"?&gt;<br />2: &lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />3:<br />4: &lt;xsd:element name="customer"&gt;<br />5: &lt;xsd:complexType&gt;<br />6: &lt;xsd:sequence&gt;<br />7: &lt;xsd:element name="name" type="xsd:string"/&gt;<br />8: &lt;xsd:element name="address" type="xsd:string" /&gt;<br />9: &lt;/xsd:sequence&gt;<br />10: &lt;!-- 增加属性定义 --&gt;<br />11: &lt;xsd:attribute name="id" type="xsd:string"/&gt;<br />12: &lt;/xsd:complexType&gt;<br />13: &lt;/xsd:element&gt;<br />14:<br />15: &lt;/xsd:schema&gt;<br />说明：<br />需要注意的一点是，属性和元素不是一家的，所以要把它放在sequence外面写，但它们都在customer的孩子，所以要写在complexType的里面。<br />下面再看一个例子：<br />order4-1.xsd<br />----------------------<br />1:&lt;?xml version="1.0"?&gt;<br />2:&lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />3:<br />4: &lt;xsd:element name="order"&gt;<br />5: &lt;xsd:complexType&gt;<br />6: &lt;xsd:sequence&gt;<br />7: &lt;xsd:element ref="orderItem" maxOccurs="10"/&gt;<br />8: &lt;/xsd:sequence&gt;<br />9: &lt;/xsd:complexType&gt;<br />10: &lt;/xsd:element&gt;<br />11:<br />12: &lt;xsd:element name="orderItem"&gt;<br />13: &lt;xsd:complexType&gt;<br />14: &lt;xsd:sequence&gt;<br />15: &lt;xsd:choice&gt;<br />16: &lt;xsd:element name="id" type="idType"/&gt;<br />17: &lt;xsd:element name="name" type="xsd:string"/&gt;<br />18: &lt;/xsd:choice&gt;<br />19: &lt;xsd:element name="quantity" type="quantityType"/&gt;<br />20: &lt;/xsd:sequence&gt;<br />21: &lt;/xsd:complexType&gt;<br />22: &lt;/xsd:element&gt;<br />23: <br />24: &lt;xsd:simpleType name="idType"&gt;<br />25: &lt;xsd:restriction base="xsd:string"&gt;<br />26: &lt;xsd:enumeration <i>value</i>="7-5058-3496-7"/&gt;<br />27: &lt;xsd:enumeration <i>value</i>="7-5005-6450-3"/&gt;<br />28: &lt;xsd:enumeration <i>value</i>="7-3020-6069-7"/&gt;<br />29: &lt;/xsd:restriction&gt;<br />30: &lt;/xsd:simpleType&gt;<br />31: <br />32: &lt;xsd:simpleType name="quantityType"&gt;<br />33: &lt;xsd:restriction base="xsd:integer"&gt;<br />34: &lt;xsd:minInclusive <i>value</i>="1"/&gt;<br />35: &lt;xsd:maxInclusive <i>value</i>="10"/&gt;<br />36: &lt;/xsd:restriction&gt;<br />37: &lt;/xsd:simpleType&gt;<br />38:<br />39:&lt;/xsd:schema&gt;<br />上面的例子中，maxOccurs代表:相同元素最多出现的次数,与些相反的是minOccurs代表：出现的最少次数。默认情况下两个都为“1”，如果把minOccurs设为“0”，表示该元素可有可无。choice代表：可选的元素，也就是在这里面写的元素只能选其中之一，不能全写。simpleType代表自定义数据类型，也就是name里的并不是真正的数据类型，而是根据自己的意愿定制的。restriction代表对某一数据类型做约束，也就是只能取其中范围之内符合要求的，比如第25－29行中，里面又有个元素enumeration代表枚举，也就是只能在枚举的那几个中选一个，而下在的36－36行，里面又有一对元素minInclusive和maxInclusive代表类型的取值范围，也就是只能取大于等于minInclusive并且小于等于maxInclusive的数。<br />把这个Schema用在XML中是这样的：<br />&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />&lt;order xmlns:xsi="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema-instance&quot;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema-instance"</font></a> xsi:noNamespaceSchemaLocation="D:\ProgramCode\Year2\XML\practice\temp.xsd"&gt;<br />&lt;orderItem&gt;<br />&lt;id&gt;7-5005-6450-3&lt;/id&gt;<br />&lt;quantity&gt;3&lt;/quantity&gt;<br />&lt;/orderItem&gt;<br />&lt;orderItem&gt;<br />&lt;name&gt;xyb&lt;/name&gt;<br />&lt;quantity&gt;3&lt;/quantity&gt;<br />&lt;/orderItem&gt;<br />&lt;!-- 注意下面这个是错的，因为超出了规定的范围 --&gt;<br />&lt;orderItem&gt;<br />&lt;id&gt;7-5005-6450-3&lt;/id&gt;<br />&lt;quantity&gt;13&lt;/quantity&gt;<br />&lt;/orderItem&gt;<br />&lt;!? 从上面的注释到此，是错误的 --&gt;<br />&lt;/order&gt;<br />说明：<br />红色的部分是导入Schema的命令，绿色的是Schema文件的路径，如果用XMLSPY写时，会自动加上。<br />对属性的设置，基本上和元素的差不多。比如：<br />&lt;xsd:element name="orderItem"&gt;<br />&lt;xsd:complexType&gt;<br />&lt;xsd:sequence&gt;&lt;/xsd:sequence&gt;<br />&lt;xsd:attribute name="id" type="idType" use="required"/&gt;<br />&lt;xsd:attribute name="quantity" type="xsd:integer" default="1"/&gt;<br />&lt;/xsd:complexType&gt;<br />&lt;xsd:simpleType name="idType"&gt;<br />&lt;xsd:restriction base="xsd:string"&gt;<br />&lt;xsd:pattern <i>value</i>="\d{1}-\d{4}-\d{4}-\d{1}"/&gt;<br />&lt;/xsd:restriction&gt;<br />&lt;/xsd:simpleType&gt;<br />&lt;/xsd:element&gt;<br />这里我们讲id属性类型作为一种自定义数据类型idType。它的格式就是上面用到的也就是类似于“7-5005-6450-3”样的。<br />而且，用attribute元素的use属性来定义是否是必须的属性。<br />required是必须值，optional是可选值，prohibited是无属性值。default属性是默认值。<br /></p>
<img src ="http://www.blogjava.net/ASONG/aggbug/48843.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2006-05-29 22:49 <a href="http://www.blogjava.net/ASONG/articles/48843.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[原创] 跟我学XML Schema</title><link>http://www.blogjava.net/ASONG/articles/48841.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Mon, 29 May 2006 14:31:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/48841.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/48841.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/48841.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/48841.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/48841.html</trackback:ping><description><![CDATA[
		<div>如何写一个最简单的XML Schema文档呢？ 
<p></p><p>首先，我们写出一个最简单的XML文档.</p><p>hello.xml<br />-------------------<br />&lt;?xml version="1.0"?&gt; <br />&lt;greeting&gt;Hello World!!&lt;/greeting&gt;</p><p>（一个根元素：greeting；且这个元素不含属性，无子元素，内容是字符串。）</p><p>hello.xsd<br />－－－－－－－－－－<br />&lt;?xml version="1.0"?&gt; <br />&lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />  &lt;xsd:element name="greeting" type="xsd:string"/&gt;<br />&lt;/xsd:schema&gt;</p><p>XML Schema文档后缀名是.xsd，完全符合XML语法，根元素是schema，命名空间xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema</font></a>，用元素&lt;element&gt;定义实例文档中的元素，如greeting。<br /><br /><br /></p><div>跟我学XML Schema(2):含子元素的稍复杂Schema文档  
<p></p><p>假设实例文档是如下的：</p><p>customer.xml<br />-----------<br />&lt;customer&gt;<br />  &lt;name&gt;teiki&lt;/name&gt;<br />  &lt;address&gt;No.237, Road Waitan, Shanghai&lt;/address&gt;<br />&lt;/customer&gt;</p><p>则可以写出以下的XML Schema文档：<br />customer.xsd<br />----------------<br />1: &lt;?xml version="1.0"?&gt;<br />2: &lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />3:   &lt;xsd:element name="customer"&gt;<br />4:    &lt;xsd:complexType&gt;<br />5:      &lt;xsd:sequence&gt;<br />6:        &lt;xsd:element name="name" type="xsd:string"/&gt;<br />7:        &lt;xsd:element name="address" type="xsd:string" /&gt;<br />8:      &lt;/xsd:sequence&gt;<br />9:    &lt;/xsd:complexType&gt;<br />10:  &lt;/xsd:element&gt;<br />11: &lt;/xsd:schema&gt;</p><p>实例文档customer.xml中，&lt;customer&gt;元素含有两个子元素，所以我们在Schema文档中采用ComplexType来定义该元素。</p><p>sequence表示子元素依次出现的顺序。<br /><br /><br /></p><div>跟我学XML Schema(3):含子元素和孙元素的更复杂Schema文档 
<p></p><p>这次我们给出一个更加复杂一些的文档：</p><p>address.xml<br />---------------<br />&lt;customer&gt;<br />  &lt;name&gt;Teiki&lt;/name&gt;<br />  &lt;address&gt;<br />    &lt;!-- address追加一个地址子元素 --&gt;<br />    &lt;prefecture&gt;Zhejiang&lt;/prefecture&gt;<br />    &lt;city&gt;Hangzhou&lt;/city&gt;<br />    &lt;street&gt;Xilu Road, No.121, 7F&lt;/street&gt;<br />  &lt;/address&gt;<br />&lt;/customer&gt;</p><p><br />为此，我们需要一个更加复杂一点的Schema文档：</p><p>address.xsd<br />-----------------<br />1: &lt;?xml version="1.0"?&gt;<br />2: &lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />3:<br />4:   &lt;xsd:element name="customer"&gt;<br />5:     &lt;xsd:complexType&gt;<br />6:       &lt;xsd:sequence&gt;<br />7:         &lt;xsd:element name="name" type="xsd:string"/&gt;<br />8:         &lt;!-- 追加子元素address--&gt;<br />9:         &lt;xsd:element name="address"&gt;<br />10:          &lt;xsd:complexType&gt;<br />11:            &lt;xsd:sequence&gt;<br />12:              &lt;xsd:element name="prefecture" type="xsd:string"/&gt;<br />13:              &lt;xsd:element name="city" type="xsd:string" /&gt;<br />14:              &lt;xsd:element name="street" type="xsd:string" /&gt;<br />15:            &lt;/xsd:sequence&gt;<br />16:          &lt;/xsd:complexType&gt;<br />17:        &lt;/xsd:element&gt;<br />18:        &lt;!-- end --&gt;<br />19:      &lt;/xsd:sequence&gt;<br />20:    &lt;/xsd:complexType&gt;<br />21:  &lt;/xsd:element&gt;<br />22:<br />23:&lt;/xsd:schema&gt;<br />不过，我们还可以采用ref元素来重新编写这个Schema文档：<br />address2.xsd<br />----------------------<br />1: &lt;?xml version="1.0"?&gt;<br />2: &lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />3:<br />4:   &lt;xsd:element name="customer"&gt;<br />5:     &lt;xsd:complexType&gt;<br />6:       &lt;xsd:sequence&gt;<br />7:         &lt;xsd:element name="name" type="xsd:string"/&gt;<br />8:         &lt;xsd:element ref="address"/&gt;<br />9:       &lt;/xsd:sequence&gt;<br />10:    &lt;/xsd:complexType&gt;<br />11:  &lt;/xsd:element&gt;<br />12:<br />13:  &lt;xsd:element name="address"&gt;<br />14:    &lt;xsd:complexType&gt;<br />15:      &lt;xsd:sequence&gt;<br />16:        &lt;xsd:element name="prefecture" type="xsd:string"/&gt;<br />17:        &lt;xsd:element name="city" type="xsd:string" /&gt;<br />18:        &lt;xsd:element name="street" type="xsd:string" /&gt;<br />19:      &lt;/xsd:sequence&gt;<br />20:    &lt;/xsd:complexType&gt;<br />21:  &lt;/xsd:element&gt;<br />22:<br />23: &lt;/xsd:schema&gt;<br />使用ref元素可以直接将其指向另一个模块，使文档更加具有可读性。<br /><br />跟我学XML Schema(4):如何定义属性呢？  </p><p></p><p><br />加入实例文档的元素包含属性，怎么办呢？</p><p>customer2.xml<br />---------------<br />&lt;customer id="001718"&gt;<br />  &lt;name&gt;Teiki&lt;/name&gt;<br />  &lt;address&gt;No.237, Road Waitan, Shanghai&lt;/address&gt;<br />&lt;/customer&gt;</p><p>那就这样写Schema文档：<br />customer2.xsd<br />------------------<br />1: &lt;?xml version="1.0"?&gt;<br />2: &lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />3:<br />4:   &lt;xsd:element name="customer"&gt;<br />5:    &lt;xsd:complexType&gt;<br />6:      &lt;xsd:sequence&gt;<br />7:        &lt;xsd:element name="name" type="xsd:string"/&gt;<br />8:        &lt;xsd:element name="address" type="xsd:string" /&gt;<br />9:      &lt;/xsd:sequence&gt;<br />10:      &lt;!-- 增加属性定义 --&gt;<br />11:      &lt;xsd:attribute name="id" type="xsd:string"/&gt;<br />12:    &lt;/xsd:complexType&gt;<br />13:  &lt;/xsd:element&gt;<br />14:<br />15: &lt;/xsd:schema&gt;</p><p>很简单，在&lt;sequence&gt;元素定义完成后，再用&lt;attribute&gt;元素id。<br /><br /></p><div>跟我学XML Schema(5):如何定义相同子元素的数量？  
<p></p><p>先看这个简单的订购数据实例文档：</p><p>order.xml<br />---------<br />&lt;order&gt;<br />  &lt;orderItem&gt;Accounting Book&lt;/orderItem&gt;<br />  &lt;orderItem&gt;Taxation Book&lt;/orderItem&gt;<br />&lt;/order&gt;</p><p>假设&lt;orderItem&gt;元素，即每次的订购书目不能超过10种，那该怎么写这个Schema文档呢？这里要用到&lt;element&gt;的maxOccurs属性。</p><p>order.xsd<br />--------------------<br />1:&lt;?xml version="1.0"?&gt;<br />2:&lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />3:<br />4:  &lt;xsd:element name="order"&gt;<br />5:    &lt;xsd:complexType&gt;<br />6:      &lt;xsd:sequence&gt;<br />7:        &lt;xsd:element name="orderItem"   type="xsd:string"  maxOccurs="10" /&gt;<br />8:      &lt;/xsd:sequence&gt;<br />9:    &lt;/xsd:complexType&gt;<br />10:  &lt;/xsd:element&gt;<br />11:<br />12:&lt;/xsd:schema&gt;</p><p>第7行中的maxOccurs属性为10，代表orderItem元素可以最大有10个。如果，不设定元素个数，则可以用maxOccurs="unbounded"来定义.</p><p>类似，如果要定义最小值，可以使用minOccurs，比如下面这句：<br />&lt;xsd:element name="orderItem" type="xsd:string" minOccurs="5" maxOccurs="10"/&gt;<br />这两个属性缺省值都是1。<br /><br /></p><div>跟我学XML Schema(6):如何定义可选项的子元素？ 
<p></p><p>假如上面的订书数据中，可以用书名或者书号任一一种订购，则实例文档可能如下：</p><p>order2.xml<br />-----------------<br />&lt;order&gt;<br />  &lt;orderItem&gt;<br />    &lt;!--书名订购--&gt;<br />    &lt;name&gt;Accounting Book&lt;/name&gt;<br />  &lt;/orderItem&gt;<br />  &lt;orderItem&gt;<br />    &lt;!--书号订购--&gt;<br />    &lt;id&gt;7-5058-3496-7&lt;/id&gt;<br />  &lt;/orderItem&gt;<br />&lt;/order&gt;</p><p>这时书写Schema文档还需要使用choice元素。</p><p>order2.xsd<br />-------------------------<br />1:&lt;?xml version="1.0"?&gt;<br />2:&lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />3:<br />4:  &lt;xsd:element name="order"&gt;<br />5:    &lt;xsd:complexType&gt;<br />6:      &lt;xsd:sequence&gt;<br />7:        &lt;xsd:element ref="orderItem" maxOccurs="10" /&gt;<br />8:      &lt;/xsd:sequence&gt;<br />9:    &lt;/xsd:complexType&gt;<br />10:  &lt;/xsd:element&gt;<br />11:<br />12:  &lt;xsd:element name="orderItem"&gt;<br />13:    &lt;xsd:complexType&gt;<br />14:      &lt;xsd:choice&gt;<br />15:        &lt;xsd:element name="name" type="xsd:string"/&gt;<br />16:        &lt;xsd:element name="id" type="xsd:string"/&gt;<br />17:      &lt;/xsd:choice&gt;<br />18:    &lt;/xsd:complexType&gt;<br />19:  &lt;/xsd:element&gt;<br />20:<br />21:&lt;/xsd:schema&gt;<br /><br /></p><div>跟我学XML Schema(7):稍微更复杂的可选项子元素 
<p></p><p>再稍微修改一下订书数据的实例文档：</p><p>order3.xml<br />-----------------<br />&lt;order&gt;<br />  &lt;orderItem&gt;<br />    &lt;name&gt;Accounting Book&lt;/name&gt;<br />    &lt;quantity&gt;2&lt;/quantity&gt;<br />  &lt;/orderItem&gt;<br />  &lt;orderItem&gt;<br />    &lt;id&gt;7-5058-3496-7&lt;/id&gt;<br />  &lt;/orderItem&gt;<br />&lt;/order&gt;</p><p>这里假定&lt;quantity&gt;值为1时，缺省。</p><p>如何修改Schema文档呢？</p><p>order3.xsd<br />-----------------<br />1:&lt;?xml version="1.0"?&gt;<br />2:&lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />3:<br />4:  &lt;xsd:element name="order"&gt;<br />5:    &lt;xsd:complexType&gt;<br />6:      &lt;xsd:sequence&gt;<br />7:        &lt;xsd:element ref="orderItem" maxOccurs="10"/&gt;<br />8:      &lt;/xsd:sequence&gt;<br />9:    &lt;/xsd:complexType&gt;<br />10:  &lt;/xsd:element&gt;<br />11:<br />12:  &lt;xsd:element name="orderItem"&gt;<br />13:    &lt;xsd:complexType&gt;<br />14:      &lt;xsd:sequence&gt;<br />15:        &lt;xsd:choice&gt;<br />16:          &lt;xsd:element name="name" type="xsd:string"/&gt;<br />17:          &lt;xsd:element name="id" type="xsd:string"/&gt;<br />18:        &lt;/xsd:choice&gt;<br />19:        &lt;xsd:element name="quantity" type="xsd:string" minOccurs="0"/&gt;<br />20:      &lt;/xsd:sequence&gt;<br />21:    &lt;/xsd:complexType&gt;<br />22:  &lt;/xsd:element&gt;<br />23:<br />24:&lt;/xsd:schema&gt;</p><p>19行中的quantity最少出现值为0，也就是可以有，也可以没有。<br />当然，也可以直接在&lt;choice&gt;元素中，包含quantity,然后定义它的minOccurs。</p><div>跟我学XML Schema(8):内置简单类型 
<p></p><p>内建于XML Schema的简单类型有44种。他们在XML Schema推荐标准的第二部分中公布，下面这是一张内置类型的层次结构图：</p><p><a class="contentlink" href="http://www.w3.org/TR/2001/PR-xmlschema-2-20010330/type-hierarchy.jpg" target="_blank"><font color="#4455aa">http://www.w3.org/TR/2001/PR-xmlschema-2-20010330/type-hierarchy.jpg</font></a><br /><br /></p><div>跟我学XML Schema(9):自定义简单类型 
<p></p><p>如果内置简单类型的44种还不能满足要求，怎么办呢？下面学习自定义简单类型。(XML的扩展性充分体现在这里)</p><p>例如这个实例文档：</p><p>order4.xml<br />-----------------<br />&lt;order&gt;<br />  &lt;orderItem&gt;<br />    &lt;id&gt;7-5058-3496-7&lt;/id&gt;<br />    &lt;quantity&gt;5&lt;/quantity&gt;<br />  &lt;/orderItem&gt;<br />&lt;/order&gt;</p><p>ID是一个标准的ISBN编码，我们怎么定义这个ISBN编码呢？</p><p>&lt;xsd:simpleType name="idType"&gt;<br />  &lt;xsd:restriction base="xsd:string"&gt;<br />    &lt;xsd:pattern <i>value</i>="\d{1}-\d{4}-\d{4}-\d{1}"/&gt;<br />  &lt;/xsd:restriction&gt;<br />&lt;/xsd:simpleType&gt;</p><p>idType是一个自定义的简单类型。<br />我们对它做了限制：<br />&lt;xsd:restriction base="xsd:string"&gt;代表它是基于一个字符串类型。再用pattern元素来描述该字符串的形式。</p><p><i>value</i>="\d{1}-\d{4}-\d{4}-\d{1}"这是一个正则表达式，关于正则表达式，以后再介绍。嘻嘻！</p><p>利用这个自定义的简单类型，我们可以重新写Schema文档：</p><p>order4.xsd<br />---------------<br />1:&lt;?xml version="1.0"?&gt;<br />2:&lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />3:<br />4:  &lt;xsd:element name="order"&gt;<br />5:    &lt;xsd:complexType&gt;<br />6:      &lt;xsd:sequence&gt;<br />7:        &lt;xsd:element ref="orderItem" maxOccurs="10"/&gt;<br />8:      &lt;/xsd:sequence&gt;<br />9:    &lt;/xsd:complexType&gt;<br />10:  &lt;/xsd:element&gt;<br />11:<br />12:  &lt;xsd:element name="orderItem"&gt;<br />13:    &lt;xsd:complexType&gt;<br />14:      &lt;xsd:sequence&gt;<br />15:        &lt;xsd:element name="id" type="idType"/&gt;<br />16:        &lt;xsd:element name="quantity" type="xsd:integer"/&gt;<br />17:      &lt;/xsd:sequence&gt;<br />18:    &lt;/xsd:complexType&gt;<br />19:  &lt;/xsd:element&gt;<br />20:  <br />21:  &lt;xsd:simpleType name="idType"&gt;<br />22:    &lt;xsd:restriction base="xsd:string"&gt;<br />23:      &lt;xsd:pattern <i>value</i>="\d{1}-\d{4}-\d{4}-\d{1}"/&gt;<br />24:    &lt;/xsd:restriction&gt;<br />25:  &lt;/xsd:simpleType&gt;<br />26:  <br />27:&lt;/xsd:schema&gt;</p><p><br />假如我们事先确定好ID只有3个，即只有3个ISBN是可选的，那怎么办？我们可以用enumeration元素来进行列举。</p><p>&lt;xsd:simpleType name="idType"&gt;<br />  &lt;xsd:restriction base="xsd:string"&gt;<br />    &lt;xsd:enumeration <i>value</i>="7-5058-3496-7"/&gt;<br />    &lt;xsd:enumeration <i>value</i>="7-5005-6450-3"/&gt;<br />    &lt;xsd:enumeration <i>value</i>="7-3020-6069-7"/&gt;<br />  &lt;/xsd:restriction&gt;<br />&lt;/xsd:simpleType&gt;</p><p><br />再来看订购量quantity的值，如果我们设定其值必须在1－10之间，该怎么办呢？可以这些自定义一个简单类型。</p><p>&lt;xsd:simpleType name="quantityType"&gt;<br />  &lt;xsd:restriction base="xsd:integer"&gt;<br />    &lt;xsd:minInclusive <i>value</i>="1"/&gt;<br />    &lt;xsd:maxInclusive <i>value</i>="10"/&gt;<br />  &lt;/xsd:restriction&gt;<br />&lt;/xsd:simpleType&gt;</p><p>其中，minInclusive,maxInclusive分别代表该类型的取值范围。</p><p>所以最终修改后的Schema文档如下：</p><p>order4-1.xsd<br />----------------------<br />1:&lt;?xml version="1.0"?&gt;<br />2:&lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />3:<br />4:  &lt;xsd:element name="order"&gt;<br />5:    &lt;xsd:complexType&gt;<br />6:      &lt;xsd:sequence&gt;<br />7:        &lt;xsd:element ref="orderItem" maxOccurs="10"/&gt;<br />8:      &lt;/xsd:sequence&gt;<br />9:    &lt;/xsd:complexType&gt;<br />10:  &lt;/xsd:element&gt;<br />11:<br />12:  &lt;xsd:element name="orderItem"&gt;<br />13:    &lt;xsd:complexType&gt;<br />14:      &lt;xsd:sequence&gt;<br />15:        &lt;xsd:element name="id" type="idType"/&gt;<br />16:        &lt;xsd:element name="quantity" type="quantityType"/&gt;<br />17:      &lt;/xsd:sequence&gt;<br />18:    &lt;/xsd:complexType&gt;<br />19:  &lt;/xsd:element&gt;<br />20:  <br />21:  &lt;xsd:simpleType name="idType"&gt;<br />22:    &lt;xsd:restriction base="xsd:string"&gt;<br />23:      &lt;xsd:enumeration <i>value</i>="7-5058-3496-7"/&gt;<br />    &lt;xsd:enumeration <i>value</i>="7-5005-6450-3"/&gt;<br />    &lt;xsd:enumeration <i>value</i>="7-3020-6069-7"/&gt;<br />26:    &lt;/xsd:restriction&gt;<br />27:  &lt;/xsd:simpleType&gt;<br />28:  <br />29:  &lt;xsd:simpleType name="quantityType"&gt;<br />30:    &lt;xsd:restriction base="xsd:integer"&gt;<br />31:      &lt;xsd:minInclusive <i>value</i>="1"/&gt;<br />32:      &lt;xsd:maxInclusive <i>value</i>="10"/&gt;<br />33:    &lt;/xsd:restriction&gt;<br />34:  &lt;/xsd:simpleType&gt;<br />35:<br />36:&lt;/xsd:schema&gt;<br /><br /></p><div>跟我学XML Schema(10):定义属性 
<p></p><p>最后，我们再来讲讲元素的属性如何在Schema文档中定义。</p><p>比如上面的order.xml实例文档中：</p><p>&lt;order&gt;<br />  &lt;orderItem id="7-5058-3496-7" /&gt;<br />&lt;/order&gt;</p><p>对此，我们在Schema文档中采用一个attribute来定义：</p><p>order.xsd<br />－－－－－－－－－<br />&lt;xsd:element name="orderItem"&gt;<br />  &lt;xsd:complexType&gt;<br />    &lt;xsd:sequence&gt;　　←空元素<br />    &lt;/xsd:sequence&gt;　 <br />    <br />    &lt;!--定义该元素属性--&gt;<br />    &lt;xsd:attribute name="id" type="xsd:string"/&gt;<br />  &lt;/xsd:complexType&gt;<br />&lt;/xsd:element&gt;</p><p>那么，实例文档中该属性值是必须的还是可有可无的呢？我们可以这样限制：</p><p>&lt;xsd:attribute name="id" type="idType" use="required"/&gt;</p><p>这里我们讲id属性类型作为一种自定义数据类型idType。<br />而且，用attribute元素的use属性来定义是否是必须的属性。<br />required是必须值，optional是可选值，prohibited是无属性值。</p><p><br />那么对于属性的缺省值，我们怎么定义呢？<br />比如：<br />&lt;order&gt;<br />  &lt;orderItem id="4-8443-1780-6" quantity="3"/&gt;<br />&lt;/order&gt;</p><p>我们还可以用attribute元素的另一个属性default来定义：<br />&lt;xsd:attribute name="quantity" type="xsd:integer" default="1"/&gt;</p><p>所以，我们可以重新写出一个Schema文档：</p><p>order2.xsd<br />--------------<br />&lt;xsd:element name="orderItem"&gt;<br />  &lt;xsd:complexType&gt;<br />    &lt;xsd:sequence&gt;&lt;/xsd:sequence&gt;<br />    &lt;xsd:attribute name="id" type="idType" use="required"/&gt;<br />    &lt;xsd:attribute name="quantity" type="xsd:integer" default="1"/&gt;<br />  &lt;/xsd:complexType&gt;<br />&lt;/xsd:element&gt;<br /><br /></p><div>上面的属性我们定义我们还可以采用属性组的办法来重新改写Schema文档。 
<p></p><p>order3.xsd<br />----------------<br />1:    &lt;xsd:element name="orderItem"&gt;<br />2:      &lt;xsd:complexType&gt;<br />3:        &lt;xsd:sequence&gt;&lt;/xsd:sequence&gt;<br />4:        &lt;xsd:attributeGroup ref="orderItemAttributes"/&gt;<br />5:      &lt;/xsd:complexType&gt;<br />6:    &lt;/xsd:element&gt;<br />7:<br />8:    &lt;xsd:attributeGroup name="orderItemAttributes"&gt;<br />9:      &lt;xsd:attribute name="id" type="idType" use="required"/&gt;<br />10:      &lt;xsd:attribute name="quantity" type="xsd:integer" default="1"/&gt;<br />11:    &lt;/xsd:attributeGroup&gt;</p><p>这个属性组就不详细解释了，不过，大家一看就清楚了吧。</p><p>最后，我们写一个完整的订书order.xml的Schema文档。<br />1:  &lt;?xml version="1.0"?&gt;<br />2:  &lt;xsd:schema xmlns:xsd="<a class="contentlink" href="http://www.w3.org/2001/XMLSchema&quot;&gt;" target="_blank"><font color="#4455aa">http://www.w3.org/2001/XMLSchema"&gt;</font></a><br />3:<br />4:    &lt;xsd:element name="order"&gt;<br />5:      &lt;xsd:complexType&gt;<br />6:        &lt;xsd:sequence&gt;<br />7:          &lt;xsd:element ref="orderItem" maxOccurs="10"/&gt;<br />8:        &lt;/xsd:sequence&gt;<br />9:      &lt;/xsd:complexType&gt;<br />10:    &lt;/xsd:element&gt;<br />11:<br />12:    &lt;xsd:element name="orderItem"&gt;<br />13:      &lt;xsd:complexType&gt;<br />14:        &lt;xsd:sequence&gt;&lt;/xsd:sequence&gt;<br />15:        &lt;xsd:attributeGroup ref="orderItemAttributes"/&gt;<br />16:      &lt;/xsd:complexType&gt;<br />17:    &lt;/xsd:element&gt;<br />18:<br />19:    &lt;xsd:attributeGroup name="orderItemAttributes"&gt;<br />20:      &lt;xsd:attribute name="id" type="idType" use="required"/&gt;<br />21:      &lt;xsd:attribute name="quantity" type="xsd:integer" default="1"/&gt;<br />22:    &lt;/xsd:attributeGroup&gt;<br />23:<br />24:    &lt;xsd:simpleType name="idType"&gt;<br />25:      &lt;xsd:restriction base="xsd:string"&gt;<br />26:        &lt;xsd:pattern <i>value</i>="\d{1}-\d{4}-\d{4}-\d{1}"/&gt;<br />27:      &lt;/xsd:restriction&gt;<br />28:    &lt;/xsd:simpleType&gt;<br />29:<br />30:  &lt;/xsd:schema&gt;<br /></p></div></div></div></div></div></div></div></div></div></div>
<img src ="http://www.blogjava.net/ASONG/aggbug/48841.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2006-05-29 22:31 <a href="http://www.blogjava.net/ASONG/articles/48841.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>建一个XMLHttpRequest对象池</title><link>http://www.blogjava.net/ASONG/articles/48837.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Mon, 29 May 2006 13:46:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/48837.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/48837.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/48837.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/48837.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/48837.html</trackback:ping><description><![CDATA[ 在ajax应用中，通常一个页面要同时发送多个请求，如果只有一个XMLHttpRequest对象，前面的请求还未完成，后面的就会把前面的覆盖掉，如果每次都创建一个新的XMLHttpRequest对象，也会造成浪费。解决的办法就是创建一个XMLHttpRequset的对象池，如果池里有空闲的对象，则使用此对象，否则将创建一个新的对象。 <br />  下面是我最近写的一个简单的类： <br />   <br />  /** <br />   * XMLHttpRequest Object Pool <br />   * <br />   * @author legend &lt;legendsky@hotmail.com&gt; <br />   * @link http://www.ugia.cn/?p=85 <br />   * @Copyright www.ugia.cn <br />   */ <br />   <br />  var XMLHttp = { <br />   _objPool: [], <br />   <br />   _getInstance: function () <br />   { <br />   for (var i = 0; i &lt; this._objPool.length; i ++) <br />   { <br />   if (this._objPool[i].readyState == 0 || this._objPool[i].readyState == 4) <br />   { <br />   return this._objPool[i]; <br />   } <br />   } <br />   <br />   // IE5中不支持push方法 <br />   this._objPool[this._objPool.length] = this._createObj(); <br />   <br />   return this._objPool[this._objPool.length - 1]; <br />   }, <br />   <br />   _createObj: function () <br />   { <br />   if (window.XMLHttpRequest) <br />   { <br />   var objXMLHttp = new XMLHttpRequest(); <br />   <br />   } <br />   else <br />   { <br />   var MSXML = [’MSXML2.XMLHTTP.5.0’, ’MSXML2.XMLHTTP.4.0’, ’MSXML2.XMLHTTP.3.0’, ’MSXML2.XMLHTTP’, ’Microsoft.XMLHTTP’]; <br />   for(var n = 0; n &lt; MSXML.length; n ++) <br />   { <br />   try <br />   { <br />   var objXMLHttp = new ActiveXObject(MSXML[n]); <br />   break; <br />   } <br />   catch(e) <br />   { <br />   } <br />   } <br />   } <br />   <br />   // mozilla某些版本没有readyState属性 <br />   if (objXMLHttp.readyState == null) <br />   { <br />   objXMLHttp.readyState = 0; <br />   <br />   objXMLHttp.addEventListener("load", function () <br />   { <br />   objXMLHttp.readyState = 4; <br />   <br />   if (typeof objXMLHttp.onreadystatechange == "function") <br />   { <br />   objXMLHttp.onreadystatechange(); <br />   } <br />   }, false); <br />   } <br />   <br />   return objXMLHttp; <br />   }, <br />   <br />   // 发送请求(方法[post,get], 地址, 数据, 回调函数) <br />   sendReq: function (method, url, data, callback) <br />   { <br />   var objXMLHttp = this._getInstance(); <br />   <br />   with(objXMLHttp) <br />   { <br />   try <br />   { <br />   // 加随机数防止缓存 <br />   if (url.indexOf("?") &gt; 0) <br />   { <br />   url += "&amp;randnum=" + Math.random(); <br />   } <br />   else <br />   { <br />   url += "?randnum=" + Math.random(); <br />   } <br />   <br />   open(method, url, true); <br />   <br />   // 设定请求编码方式 <br />   setRequestHeader(’Content-Type’, ’application/x-www-form-urlencoded; charset=UTF-8’); <br />   send(data); <br />   onreadystatechange = function () <br />   { <br />   if (objXMLHttp.readyState == 4 &amp;&amp; (objXMLHttp.status == 200 || objXMLHttp.status == 304)) <br />   { <br />   callback(objXMLHttp); <br />   } <br />   } <br />   } <br />   catch(e) <br />   { <br />   alert(e); <br />   } <br />   } <br />   } <br />  }; <br />   <br />  示例： <br />  &lt;script type="text/JavaScript" src="xmlhttp.js"&gt;&lt;/script&gt; <br />  &lt;script type="text/Javascript"&gt; <br />  function test(obj) <br />  { <br />   alert(obj.statusText); <br />  } <br />   <br />  XMLHttp.sendReq(’GET’, ’http://www.ugia.cn/wp-data/test.htm’, ’’, test); <br />  XMLHttp.sendReq(’GET’, ’http://www.ugia.cn/wp-data/test.htm’, ’’, test); <br />  XMLHttp.sendReq(’GET’, ’http://www.ugia.cn/wp-data/test.htm’, ’’, test); <br />  XMLHttp.sendReq(’GET’, ’http://www.ugia.cn/wp-data/test.htm’, ’’, test); <br />   <br />  alert(’Pool length:’ + XMLHttp._objPool.length); <br />  &lt;/script&gt; <br /><img src ="http://www.blogjava.net/ASONG/aggbug/48837.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2006-05-29 21:46 <a href="http://www.blogjava.net/ASONG/articles/48837.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于session的详细解释</title><link>http://www.blogjava.net/ASONG/articles/48834.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Mon, 29 May 2006 13:45:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/48834.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/48834.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/48834.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/48834.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/48834.html</trackback:ping><description><![CDATA[
		<span id="LblContent"> 
<p style="TEXT-INDENT: 2em"><span id="ArticleContent1_ArticleContent1_lblContent">一、术语session 
<p>　　在我的经验里，session这个词被滥用的程度大概仅次于transaction，更加有趣的是transaction与session在某些语境下的含义是相同的。</p><p>　　session，中文经常翻译为会话，其本来的含义是指有始有终的一系列动作/消息，比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个session。有时候我们可以看到这样的话“在一个浏览器会话期间，...”，这里的会话一词用的就是其本义，是指从一个浏览器窗口打开到关闭这个期间①。最混乱的是“用户（客户端）在一次会话期间”这样一句话，它可能指用户的一系列动作（一般情况下是同某个具体目的相关的一系列动作，比如从登录到选购商品到结账登出这样一个网上购物的过程，有时候也被称为一个transaction），然而有时候也可能仅仅是指一次连接，也有可能是指含义①，其中的差别只能靠上下文来推断②。</p><p>　　然而当session一词与网络协议相关联时，它又往往隐含了“面向连接”和/或“保持状态”这样两个含义，“面向连接”指的是在通信双方在通信之前要先建立一个通信的渠道，比如打电话，直到对方接了电话通信才能开始，与此相对的是写信，在你把信发出去的时候你并不能确认对方的地址是否正确，通信渠道不一定能建立，但对发信人来说，通信已经开始了。“保持状态”则是指通信的一方能够把一系列的消息关联起来，使得消息之间可以互相依赖，比如一个服务员能够认出再次光临的老顾客并且记得上次这个顾客还欠店里一块钱。这一类的例子有“一个TCP session”或者“一个POP3 session”③。</p><p>　　而到了web服务器蓬勃发展的时代，session在web开发语境下的语义又有了新的扩展，它的含义是指一类用来在客户端与服务器之间保持状态的解决方案④。有时候session也用来指这种解决方案的存储结构，如“把xxx保存在session里”⑤。由于各种用于web开发的语言在一定程度上都提供了对这种解决方案的支持，所以在某种特定语言的语境下，session也被用来指代该语言的解决方案，比如经常把Java里提供的javax.servlet.http.HttpSession简称为session⑥。</p><p>　　鉴于这种混乱已不可改变，本文中session一词的运用也会根据上下文有不同的含义，请大家注意分辨。</p><p>　　在本文中，使用中文“浏览器会话期间”来表达含义①，使用“session机制”来表达含义④，使用“session”表达含义⑤，使用具体的“HttpSession”来表达含义⑥</p><p>　　二、HTTP协议与状态保持</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>　　三、理解cookie机制 </p><p>　　cookie机制的基本原理就如上面的例子一样简单，但是还有几个问题需要解决：“会员卡”如何分发；“会员卡”的内容；以及客户如何使用“会员卡”。</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>　　路径就是跟在域名后面的URL路径，比如/或者/foo等等，可以用某飘柔专柜做比。</p><p>　　路径与域合在一起就构成了cookie的作用范围。<br />如果不设置过期时间，则表示这个cookie的生命期为浏览器会话期间，只要关闭浏览器窗口，cookie就消失了。这种生命期为浏览器会话期的cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里，当然这种行为并不是规范规定的。如果设置了过期时间，浏览器就会把cookie保存到硬盘上，关闭后再次打开浏览器，这些cookie仍然有效直到超过设定的过期时间。</p><p>　　存储在硬盘上的cookie可以在不同的浏览器进程间共享，比如两个IE窗口。而对于保存在内存里的cookie，不同的浏览器有不同的处理方式。对于IE，在一个打开的窗口上按Ctrl-N（或者从文件菜单）打开的窗口可以与原窗口共享，而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存cookie；对于Mozilla Firefox0.8，所有的进程和标签页都可以共享同样的cookie。一般来说是用javascript的window.open打开的窗口会与原窗口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用session机制的web应用程序开发者造成很大的困扰。</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"></p><p>　　这是使用HTTPLook这个HTTP Sniffer软件来俘获的HTTP通讯纪录的一部分</p><p align="center"></p><p><br />　　浏览器在再次访问goolge的资源时自动向外发送cookie</p><p align="center"></p><p>　　使用Firefox可以很容易的观察现有的cookie的值</p><p>　　使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。</p><p align="center"></p><p><br />　　IE也可以设置在接受cookie前询问</p><p align="center"></p><p>　　这是一个询问接受cookie的对话框。</p><p>　　四、理解session机制</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></p><p></p><form name="testform" action="/xxx"><br /><input /><br /></form></span></p><p></p><p></p><p>　　在被传递给客户端之前将被改写成</p><p></p><p></p><form name="testform" action="/xxx"><br /><input type="hidden" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764" name="jsessionid" /><br /><input /><br /></form><p>　　这种技术现在已较少应用，笔者接触过的很古老的iPlanet6(SunONE应用服务器的前身)就使用了这种技术。实际上这种技术可以简单的用对action应用URL重写来代替。</p><p>　　在谈论session机制的时候，常常听到这样一种误解“只要关闭浏览器，session就消失了”。其实可以想象一下会员卡的例子，除非顾客主动对店家提出销卡，否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的，除非程序通知服务器删除一个session，否则服务器会一直保留，程序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭，因此服务器根本不会有机会知道浏览器已经关闭，之所以会有这种错觉，是大部分session机制都使用会话cookie来保存session id，而关闭浏览器后这个session id就消失了，再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上，或者使用某种手段改写浏览器发出的HTTP请求头，把原来的session id发送给服务器，则再次打开浏览器仍然能够找到原来的session。</p><p>　　恰恰是由于关闭浏览器不会导致session被删除，迫使服务器为seesion设置了一个失效时间，当距离客户端上一次使用session的时间超过这个失效时间时，服务器就可以认为客户端已经停止了活动，才会把session删除以节省存储空间。</p><p>　　五、理解javax.servlet.http.HttpSession</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>　　六、HttpSession常见问题</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。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。<br />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">　　七、跨应用程序的session共享<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"></p><p>　　根据这个特性，我们可以推测Tomcat中session的内存结构大致如下。<br /></p><p align="center"></p><p>　　笔者以前用过的iPlanet也采用的是同样的方式，估计SunONE与iPlanet之间不会有太大的差别。对于这种方式的服务器，解决的思路很简单，实际实行起来也不难。要么让所有的应用程序共享一个session id，要么让应用程序能够获得其他应用程序的session id。</p><p>　　iPlanet中有一种很简单的方法来实现共享一个session id，那就是把各个应用程序的cookie路径都设为/（实际上应该是/NASApp，对于应用程序来讲它的作用相当于根）。</p><p><session-info><path>/NASApp</path><br /></session-info></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"></p><p align="center"></p><p>　　从截屏画面上可以看到Weblogic Server对所有的应用程序设置的cookie的路径都是/，这是不是意味着在Weblogic Server中默认的就可以共享session了呢？然而一个小实验即可证明即使不同的应用程序使用的是同一个session，各个应用程序仍然只能访问自己所设置的那些属性。这说明Weblogic Server中的session的内存结构可能如下<br /></p><p align="center"></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></span>
<img src ="http://www.blogjava.net/ASONG/aggbug/48834.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2006-05-29 21:45 <a href="http://www.blogjava.net/ASONG/articles/48834.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>给XML文件定义DTD</title><link>http://www.blogjava.net/ASONG/articles/48779.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Mon, 29 May 2006 08:33:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/48779.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/48779.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/48779.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/48779.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/48779.html</trackback:ping><description><![CDATA[DTD实际上可以看作一个或多个XML文件的模板，这些XML文件中的元素、元素的属性、元素的排列方式/顺序、元素能够包含的内容等，都必须符合DTD中的定义。XML文件中的元素，即我们所创建的标记，是根据我们应用的实际情况来创建的。想要创建一份完整性高、适应性广的DTD是非常困难的，因为各行各业都有他们自己的行业特点，所以DTD通常是以某种应用领域为定义的范围，如：医学、建筑、工商、行政。DTD定义的元素含盖范围越广泛，那么就越复杂。<br /><br />DTD可以是一个完全独立的文件，也可以在XML文件中直接设定。所以，DTD分为外部DTD（在XML文件中调用另外已经编辑好的DTD）和内部DTD（在XML文件中直接设定DTD）两种。比如，有几十家相互联系的、合作伙伴关系的公司、厂商，他们相互之间的交换电子文档都是用XML文档。那么我们可以将这些XML文档的DTD放在某个地方，让所有交换的XML文档都使用此DTD，这是最方便的做法，同时也适用于公司内部的XML文件使用。<br /><br /><font size="3"><strong>内部DTD<br /><br /></strong>内部DTD是在XML文件的文件序言区域中定义的。语法：<br /><br /></font><table cellspacing="0" cellpadding="0" width="580" border="0"><tbody><tr><td class="code" bgcolor="#e6e6e6"><strong>〈!DOCTYPE element-name[........<br /><br />]〉</strong></td></tr></tbody></table><br /><br />〈!DOCTYPE ：表示开始设定DTD，注意DOCUTYPE是大写。<br /><br />Element-name ：指定此DTD的根元素的名称，一个XML文件只能有一个根元素。注意，如果 XML文件使用了DTD，那么文件中的根元素就在这里指定。<br /><br />[.........]〉 ：在[ ]标记里面定义XML文件使用元素，然后用〉结束DTD的定义。<br /><br />下面，我们来看一下怎样给XML文件定义DTD，请见例1。<br /><br />例1中的DTD定义区可以看作是一个DTD定义的大概框架，为其他XML文件定义DTD，结构和例1的DTD差不多，只是可能需要添加、删除或者更改一些东西而已。在DTD定义的中间是元素设定，这是一个DTD的最主要部分，其主要语法如下：<br /><br /><table cellspacing="0" cellpadding="0" width="580" border="0"><tbody><tr><td class="code" bgcolor="#e6e6e6"><strong>〈!ELEMENT element-name element-definition〉</strong></td></tr></tbody></table><br /><br />〈!ELEMENT：表示开始元素设置，注意此处ELEMENT关键字是大写。<br /><br />element-name：表示要设置的元素的名称。<br /><br />element-definition：指明要对此元素进行怎样的定义，就是说〈元素〉...〈/元素〉之间能够包含什么内容，是其他元素还是一般性的文字。<br /><br />在例1中，大家可以看到，〈!ELEMENT 参考资料 (书籍)〉这个元素设定是声明了“参考资料”这个元素，并且它是作为“书籍”这个元素的父元素。〈!ELEMENT 书籍 (名称,作者,价格)〉这个元素设定声明了“书籍”这个元素，并且它是作为“名称”、“作者”、“价格”这三个元素的父元素。而〈!ELEMENT 名称 (#PCDATA)〉这个元素设定声明了“名称”这个元素，但是此元素仅仅包含一般文字，是基本元素，这是由#PCDATA关键字定义的。<br /><br />在元素设置中，如果元素包含多个子元素，如：〈!ELEMENT 书籍 (名称,作者,价格)〉这种含多个子元素的声明，那么“名称”、“作者”、“价格”这些标记在XML文件中必须以上面排列的顺序出现，每个标记必须而且只能够出现一次。如果在设定元素时，按照〈!ELEMENT 书籍ANY〉，这样在元素下就可以包含任意被设定过的元素，出现的次数和顺序也不受限制，并且在该元素下，除了可以包含子元素以外，还能够包含一般的文字。有时候，在XML文件中，一个标记可能多次出现（或者不出现），那么我们除了在它们的父元素中用ANY关键字之外，还可以在元素的旁边加上特定的符号来控制标记出现的次数。这些符号见表1。<br /><br /><table width="236" border="1"><caption class="12v" align="left">表1</caption><tbody><tr><td align="middle" width="63"><p class="12v">符号</p></td><td width="161"><p class="12v">代表标记出现的次数</p></td></tr><tr><td align="middle" width="63"><p class="12v">？</p></td><td width="161"><p class="12v">不出现或只出现一次    </p></td></tr><tr><td align="middle" width="63"><p class="12v">*</p></td><td width="161"><p class="12v">不出现或可出现多次</p></td></tr><tr><td align="middle" width="63"><p class="12v">+</p></td><td width="161"><p class="12v">必须出现一次以上</p></td></tr><tr><td align="middle" width="63"><p class="12v">无符号</p></td><td width="161"><p class="12v">只能出现一次</p></td></tr></tbody></table><br />例如：〈!ELEMENT 参考资料(书籍,报纸+,杂志?,网站)〉这个元素设定，“书籍”标记在XML文件中可以不出现或者出现多次；“报纸”标记必须出现一次以上；“杂志”标记可以不出现或只出现一次；而“网站”标记必须出现而且只能出现一次。<br /><br />在一些父元素的声明中，有可能它包含的子元素是在多个子元素中选择一个来使用，那么我们声明此父元素时，就可以把它声明成选择性元素，例如：〈!ELEMENT 配偶 (妻子|丈夫)〉。可供选择的子元素用“|”分隔，这样，我们在XML文件中可以这样写：<br /><br /><table cellspacing="0" cellpadding="0" width="580" border="0"><tbody><tr><td class="code" bgcolor="#e6e6e6"><strong>〈配偶〉<br /><br />〈丈夫〉张三〈/丈夫〉<br /><br />〈/配偶〉 </strong></td></tr></tbody></table><br /><br />只从中选择一个子元素。<br /><br />在我们的XML文件中，还可能包括很多“空元素”，即：元素是单独存在的，没有〈/元素〉这样的结束标记。那么在DTD中是用EMPTY关键字来声明的。如：〈!ELEMENT 元素名 EMPTY〉。在XML文件中，空元素不需要结束标记，但必须以〈/空元素名〉这样的写法。<br /><br />在DTD中，还可以声明一些称为Entity的东西，让DTD和XML文件使用。我们可以把Entity看作是一个常量，它有一定的值。在DTD中，Entity的声明语法为：〈!ENTITY entity-name entity-definition〉。例如：我们在DTD中声明〈!ENTITY PC "(#PCDATA)"〉 ，那么在后面的元素设定中，就可以使用这个Entity来代替“(#PCDATA)”这个字符串，如：〈!ELEMENT 作者 (#PCDATA)〉可以写成〈!ELEMENT 作者 &amp;&amp;PC;〉。引用Entity的时候，必须要在Entity名称前面加上“&amp;&amp;”符号，后面加上“；”符号。<br /><br />在例1中，〈!ATTLIST 价格 货币单位 CDATA #REQUIRED〉这一句是设定元素的属性，关于元素属性设置的语法为：〈!ATTLIST element-name attribute-name Type Default-value〉 。其中，〈!ATTLIST是开始属性的设定（注意大小写）。element-name是指明此属性设定是针对什么元素的；attribute-name是设定的属性的名称；Type是该属性的属性值的类别，属性值有多种，可以是一般的文字，或从几种属性值中取一种等，属性值的种类见表2。Default-value是指该属性的内定值种类，有四种不同的属性内定值（见表3）。<br /><br />下面我们举几个例子来看一下几个常用的元素属性的设定。例：<br /><br />〈!ATTLIST 姓名 性别 (男|女) "男"〉 <br /><br />此元素属性设定是为“姓名”这个元素设定一个名为“性别”的属性，此属性的属性值类别是Enumerated，取值范围为“男”或者“女”（用“|”分隔）。如果在XML文件中没有为此属性赋值，那么就取值为“男”，因为属性内定值是一个字符串“男”。<br /><br />〈!ATTLIST 姓名 号码 ID #REQUIRED〉<br /><br />该属性设定是为“姓名”元素设定一个名为“号码”的属性，属性值类别是ID，意味着在XML文件中为此属性赋值的时候，值在此XML文件中是唯一的，如在同一份XML文件中出现下面的XML语句：<br /><br />〈姓名 号码="1234567"〉张三〈/姓名〉<br /><br />〈姓名 号码="1234567"〉李四〈/姓名〉<br /><br />注意：“号码”属性的值重复了，这样，在解析过程中将会出现错误信息。此属性设定中的属性内定值为#REQUIRED，表示这个属性在XML文件的〈姓名〉标记中必须出现，否则解析会发生错误。<br /><br />〈!ATTLIST 电话号码 国家代码 CDATA #FIX "86"〉 <br /><br />该属性设定是为“电话号码”这个元素设定一个名为“国家代码”的属性，该属性的值是一般的文字。在〈电话号码〉标记中不能够设定该属性，因为这个属性被设为具有固定值的属性（#FIX关键字），解析器会自动地将该属性以及值“86”加到〈电话号码〉标记中。<br /><br /><table width="409" border="1"><caption class="12v" align="left">表2</caption><tbody><tr><td width="70"><p class="12v">属性值类别</p></td><td width="327"><p class="12v">描述    </p></td></tr><tr><td width="70"><p class="12v">cdata</p></td><td width="327"><p class="12v">属性值仅仅是一般的文字。    </p></td></tr><tr><td width="70"><p class="12v">enumerated</p></td><td width="327"><p class="12v">列出该属性的取值范围，一次只能有一个属性值能够赋予属性。     </p></td></tr><tr><td width="70"><p class="12v">nmtoken</p></td><td width="327"><p class="12v">表示属性值只能由字母、数字、下划线、. 、：、-这些符号组成。    </p></td></tr><tr><td width="70"><p class="12v">nmtokens</p></td><td width="327"><p class="12v">表示属性值能够由多个nmtoken组成，每个nmtoken之间用空格隔开。     </p></td></tr><tr><td width="70"><p class="12v">id</p></td><td width="327"><p class="12v">该属性在xml文件中是唯一的，常用来表示人的身份证号码。     </p></td></tr><tr><td width="70"><p class="12v">idref</p></td><td width="327"><p class="12v">表示该属性值是参考了另一个id属性。     </p></td></tr><tr><td width="70"><p class="12v">idrefs</p></td><td width="327"><p class="12v">表示该属性值是参考了多个id属性，这些id属性的值用空格隔开。     </p></td></tr><tr><td width="70"><p class="12v">entity</p></td><td width="327"><p class="12v">表示该属性的设定值是一个外部的entity，如一个图片文件。     </p></td></tr><tr><td width="70"><p class="12v">entities</p></td><td width="327"><p class="12v">该属性值包含了多个外部entity，不同的entity之间用空格隔开。     </p></td></tr><tr><td width="70"><p class="12v">notation</p></td><td width="327"><p class="12v">属性值是在dtd中声明过的notation（声明用什么应用软件解读某些二进制文件，如图片）。</p></td></tr></tbody></table><br />在XML的规范中，还规定了两个内定的属性，即：xml:space和xml:lang ，内定的属性名称以xml:开头，而你自己定义的属性名不能以xml:开头，否则在解析时会发生错误。<br /><br />我们前面已经讲过，空白格在XML文件是有含义的，xml:space属性就是来设置解析器将XML文件中的空白格传给应用程序后的处理方法。xml:space是一个Enumerated类型的属性，只能够在default和preserver之间取值。xml:space="defaule"表示解析器将空白格传递给应用程序后，由应用程序内定的方法来处理这些空白格。如果没有设定xml:space属性，则解析器会默认用default来设定该属性。xml:space="preserver"是表示解析器将空白格传递给应用程序后，要求应用程序保留这些空白格。<br /><br />xml:lang属性是用来设置标记中的文字信息是使用哪种语言，ISO-639规定了不同语言的代表缩写，如：xml:lang="en" 表示英文；xml:lang="la"表示拉丁文；xml:lang="zh" 表示中文资料；xml:lang="zh-CN" 表示中文（简体）；xml:lang="zh-TW" 表示中文（繁体）。系统内定是xml:lang="en"，即标记中间的文字信息都是英文。<br /><br /><strong>外部DTD<br /><br /></strong>外部DTD是一个独立于XML文件的文件，实际上也是一个文本文件，只是使用.dtd为文件扩展名。因为外部DTD独立于XML文件，那么它可以供多个XML文件使用，就像用同一个模板可以写出多个不同内容的文件一样，这多个XML文件因为是引用同一个外部DTD，所以它们的结构大致相同。<br /><br />外部DTD的创建方式、语法和内部DTD是一样的，把例1的内部DTD的例子用外部DTD来写，文件如下所示。文件存为后缀名为 .dtd的文件。<br /><br /><table cellspacing="0" cellpadding="0" width="580" border="0"><tbody><tr><td class="code" bgcolor="#e6e6e6"><strong>〈?xml version="1.0" encoding="GB2312" ?〉<br /><br />〈!ELEMENT 参考资料 (书籍*)〉<br /><br />〈!ELEMENT 书籍 (名称,作者,价格)〉<br /><br />〈!ELEMENT 名称 (#PCDATA)〉<br /><br />〈!ELEMENT 作者 (#PCDATA)〉<br /><br />〈!ELEMENT 价格 (#PCDATA)〉<br /><br />〈!ATTLIST 价格 货币单位 CDATA #REQUIRED〉</strong></td></tr></tbody></table><br /><br />除了没有内部DTD中的〈!DOCTYPE 参考资料 [.....]〉语句外，其他都一样。而且有关元素数目、排列顺序、空元素设定、选择性元素、Entity声明、属性设定等都和内部DTD是一样的。<br /><br />XML文件使用〈!DOCTYPE element-name SYSTEM DTD-URL〉或者〈!DOCTYPE element-name PUBLIC DTD-name DTD-URL〉来引用创建好的外部DTD文件。<br /><br /><table width="408" border="1"><caption class="12v" align="left">表3</caption><tbody><tr><td width="65"><p class="12v">属性内定值</p></td><td width="331"><p class="12v">描述</p></td></tr><tr><td width="65"><p class="12v">#required</p></td><td width="331"><p class="12v">表示在标记中必须出现此属性。     </p></td></tr><tr><td width="65"><p class="12v">#implied</p></td><td width="331"><p class="12v">标记中可以不出现此属性。    </p></td></tr><tr><td width="65"><p class="12v">#fix</p></td><td width="331"><p class="12v">属性的值是固定的某个值。    </p></td></tr><tr><td width="65"><p class="12v">字符串</p></td><td width="331"><p class="12v">标记中如没有指定属性的值，那么此字符串就是此属性的值。</p></td></tr></tbody></table><br />此语句必须位于XML文件的文件序言区，其中，〈!DOCTYPE表示开始声明应用外部DTD；element-name是指该DTD的根元素的名称；SYSTEM是指该外部DTD文件是私有的，即我们自己创建的，没有公开发行，只是个人或在公司内部或者几个合作单位之间使用；而PUBIC关键字是指该外部DTD是公用的，经过了公开讨论，用PUBLIC的DTD都有一个逻辑名称——DTD-name，我们必须在调用时指明这个逻辑名称。DTD-URL是用URL的方式指明外部DTD文件的位置。例如，我们这份DTD文件存放在URL为：http://www.xml.com/这个地方，文件名为ckzl.dtd。那么在XML文件中的声明如下：<br /><br /><table cellspacing="0" cellpadding="0" width="580" border="0"><tbody><tr><td class="code" bgcolor="#e6e6e6"><strong>〈?xml version="1.0" encoding="GB2312" ?〉<br /><br />〈!DOCTYPE 参考资料 SYSTEM "http://www.xml.com/ckzl.dtd"〉<br /><br />...</strong></td></tr></tbody></table><br /><br /><strong>Schema简介<br /><br /></strong>DTD的语法相当复杂，并且它不符合XML文件的标准，自成一个体系。上面的介绍也仅仅是作了一个简介，目的是帮助大家能读懂DTD文件以及在必要时创建简单的DTD文件，因为现在很多的XML应用是建立在DTD之上的。<br /><br />另外，一个代替DTD的就是W3C定义的Schema，Schema相对于DTD的明显好处是XML Schema文档本身也是XML文档，而不是像DTD一样使用自成一体的语法。这就方便了用户和开发者，因为可以使用相同的工具来处理XML Schema和其他XML信息，而不必专门为Schema使用特殊工具。Schema简单易懂，懂得XML语法、规则的人都可以立刻理解它。Schema的概念提出已久，但W3C的标准最近才出来，相应的应用支持尚未完善，但采用Schema已成为XML发展的一个趋势。<br /><br /><br /><br /><strong>例1<br /><br /></strong>DTD定义区:<br /><br /><table cellspacing="0" cellpadding="0" width="580" border="0"><tbody><tr><td class="code" bgcolor="#e6e6e6"><strong>〈?xml version="1.0" encoding="GB2312" ?〉<br /><br />〈!DOCTYPE 参考资料 [<br /><br />〈!ELEMENT 参考资料 (书籍)〉<br /><br />〈!ELEMENT 书籍 (名称,作者,价格)〉<br /><br />〈!ELEMENT 名称 (#PCDATA)〉<br /><br />〈!ELEMENT 作者 (#PCDATA)〉<br /><br />〈!ELEMENT 价格 (#PCDATA)〉<br /><br />〈!ATTLIST 价格 货币单位 CDATA #REQUIRED〉<br /><br />]〉<br /><br />〈参考资料〉<br /><br />〈书籍〉<br /><br />〈名称〉XML入门精解〈/名称〉<br /><br />〈作者〉张三〈/作者〉<br /><br />〈价格 货币单位="人民币"〉20.00〈/价格〉<br /><br />〈/书籍〉<br /><br />〈书籍〉<br /><br />〈名称〉XML语法〈/名称〉<br /><br />〈!-- 即将出版 --〉<br /><br />〈作者〉李四〈/作者〉<br /><br />〈价格 货币单位="人民币"〉18.00〈/价格〉<br /><br />〈/书籍〉<br /><br />〈/参考资料〉</strong></td></tr></tbody></table><img src ="http://www.blogjava.net/ASONG/aggbug/48779.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2006-05-29 16:33 <a href="http://www.blogjava.net/ASONG/articles/48779.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>通过ClassLoader管理组件依赖</title><link>http://www.blogjava.net/ASONG/articles/48758.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Mon, 29 May 2006 07:08:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/48758.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/48758.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/48758.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/48758.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/48758.html</trackback:ping><description><![CDATA[
		<center>
				<b>
						<span style="FONT-SIZE: 20px">通过ClassLoader管理组件依赖</span>
				</b>
		</center>
		<br />
		<center>作者：Don Schwarz</center>
		<br />
		<center>译者:<a href="http://www.matrix.org.cn/user.shtml?username=xMatrix" target="_new">xMatrix</a></center>
		<br />
		<br />
		<span style="COLOR: red">版权声明：任何获得Matrix授权的网站，转载时请务必以超链接形式标明文章原始出处和作者信息及本声明</span>
		<br />作者:Don Schwarz;<a href="http://www.matrix.org.cn/user.shtml?username=xMatrix" target="_new">xMatrix</a><br />原文地址:<a href="http://www.onjava.com/pub/a/onjava/2005/04/13/dependencies.html" target="_new">http://www.onjava.com/pub/a/onjava/2005/04/13/dependencies.html</a><br />中文地址:<a href="http://www.matrix.org.cn/resource/article/43/43918_ClassLoader.html" target="_new">http://www.matrix.org.cn/resource/article/43/43918_ClassLoader.html</a><br />关键词： ClassLoader<br /><br />Java的类加载机制是非常强大的。你可以利用外部第三方的组件而不需要头文件或静态连接。你只需要简单的把组件的JAR文件放到classpath下的目录中。运行时引用完全是动态处理的。但如果这些第三方组件有自己的依赖关系时会怎么样呢？通常这需要开发人员自己解决所有需要的相应版本的组件集，并且确认他们被加到classpath中。<br /><br /><b><span style="FONT-SIZE: 16px">JAR清单文件</span></b><br />实际上你不需要这样做，Java的类加载机制可以更优雅地解决这个问题。一种方案是需要每一个组件的作者在JAR清单中定义内部组件的依赖关系。这里清单是指一个被包含在JAR中的定义文件元数据的文本文件（META-INF/MANIFEST.MF）。最常用的属性是Main-Class，定义了通过java –jar方式定位哪个类会被调用。然而，还有一个不那么有名的属性Class-Path可以用来定义他所依赖的其他JAR。Java缺省的ClassLoader会检查这些属性并且自动附加这些特定的依赖到classpath中。<br /><br />让我们来看一个例子。考虑一个实现交通模拟的Java应用，他由三个JAR组成：<br />·simulator-ui.jar：基于Swing的视图来显示模拟的过程。<br />·simulator.jar:用来表示模拟状态的数据对象和实现模拟的控制类。<br />·rule-engine.jar:常用的第三方规则引擎被用来建立模拟规则的模型。<br />simulator-ui.jar依赖simulator.jar，而simulator.jar依赖rule-engine.jar。<br /><br />而通常执行这个应用的方法如下：<br /><span style="COLOR: blue">$ java -classpath<br />   simulator-ui.jar:simulator.jar:rule-engine.jar<br />   com.oreilly.simulator.ui.Main</span><br /><br />编者注：上面的命令行应该在同一行键入；只是由于网页布局的限制看起来好像是多行。<br /><br />但我们也可以在JAR的清单文件中定义这些信息，simulator-ui的MANIFEST.MF如下：<br /><br /><span style="COLOR: blue">Main-Class: com.oreilly.simulator.ui.Main<br />Class-Path: simulator.jar</span><br /><br />而simulator的MANIFEST.MF包含：<br /><span style="COLOR: blue">Class-Path: rule-engine.jar</span><br /><br />rule-engine.jar或者没有清单文件，或者清单文件为空。<br />现在我们可以这样做：<br /><span style="COLOR: blue">$ java -jar simulator-ui.jar</span><br /><br />Java会自动解析清单的入口来取得主类及修改classpath，甚至可以确定simulator-ui.jar的路径和解释所有与这个路径相关的Class-Path属性，所以我们可以简单按照下面的方式之一来做：<br /><span style="COLOR: blue">$ java -jar ../simulator-ui.jar<br />$ java -jar /home/don/build/simulator-ui.jar</span><br /><br /><b><span style="FONT-SIZE: 16px">依赖冲突</span></b><br /><br />Java的Class-Path属性的实现相对于手工定义整个classpath是一个大的改善。然而，两种方式都有自己的限制。一个重要的限制就是你只能加载组件的一个特定版本。这看起来是很显然的因为许多编程环境都有这个限制。但是在大的包含多个第三方依赖的多JAR项目中依赖冲突是很常见的。<br /><br />例如，你正在开发一个通过查询多个搜索引擎并比较他们的结果的搜索引擎。Google和Amazon的Alexa都支持使用SOAP作为通讯机制的网络服务API，也都提供了相应的Java类库方便访问这些API。让我们假设你的JAR- metasearch.jar，依赖于google.jar和amazon.jar，而他们都依赖于公共的soap.jar。<br />现在是没有问题，但如果将来SOAP协议或API发生改变时会怎么样呢？很可能这两个搜索引擎不会选择同时升级。可能在某一天你访问Amazon时需要SOAP1.x版本而访问Google时需要SOAP2.x版本，而这两个版本的SOAP并不能在同一个进程空间中共存。在这里，我们可能包含下面的JAR依赖：<br /><span style="COLOR: blue">$ cat metasearch/META-INF/MANIFEST.MF<br />Main-Class: com.onjava.metasearch.Main<br />Class-Path: google.jar amazon.jar<br /><br />$ cat amazon/META-INF/MANIFEST.MF<br />Class-Path: soap-v1.jar<br /><br />$ cat google/META-INF/MANIFEST.MF<br />Class-Path: soap-v2.jar</span><br /><br />上面正确地描述了依赖关系，但这里并没有包含什么魔法--这样设置并不会像我们期望地那样工作。如果soap-v1.jar和soap-v2.jar定义了许多相同的类，我们肯定这是会出问题的。<br /><span style="COLOR: blue">$ java -jar metasearch.jar<br />SOAP v1: remotely invoking searchAmazon<br />SOAP v1: remotely invoking searchGoogle</span><br /><br />你可以看到，soap-v1.jar被首先加在classpath中，因此实际上也只有他会被使用。上面的例子等价于：<br /><span style="COLOR: blue">$ java -classpath<br />   metasearch.jar:amazon.jar:google.jar:soap-v1.jar:soap-v2.jar<br />   # WRONG!</span><br /><br />编者注：上面的命令行应该在同一行键入；只是由于网页布局的限制看起来好像是多行。<br /><br />有趣的是如果Yahoo也发布了一个网络服务API，而他看起来并没有依赖于现有的SOAP/XML-RPC类库。在较小的项目中，组件依赖冲突常被用来作为在你只要手工包装方案或者只需要一两个类时而不使用让你不使用全量组件（如集合类库）的原因之一。手工包装方案有他的用处，但使用已有的组件是更普遍的方式。而且复制其他组件的类到你的代码库永远不是一个好主意。实际上你已经与组件的开发产生分岐而且没有机会在有问题修复或安全升级时合并他。<br /><br />许多大的项目，如主要的商业组件，已经采用将他们使用的整个组件构建到他们的JAR内部。为了这么做，他们改变了包名使其唯一（如com/acme/foobar/org/freeware/utility），而且直接在他们的JAR中包含类。这样做的好处是可以防止在这些组件中多个版本的冲突，但这也是有代价的。这么做对开发人员来说完全隐藏了对第三方的依赖。但如果这种方式大规模的应用，将会导致效率的降低（包括JAR文件的大小和加载多个JAR版本到进程中的效率降低）。这种方式的问题在于如果两个组件依赖于同一个版本的第三方组件时，就没有协调机制来确定共享的组件只被加载一次。这个问题我们会在下一节进行研究。除了效率的降低外，很可能你这种绑定第三方软件的方式会与那些软件的许可协议冲突。<br /><br />另一种解决这个问题的方式是每一个组件的开发人员显式的在他的包名中编码一个版本号。Sun的javac代码就采用这个方式—一个com.sun.tools.javac.Main类会简单地转发给com.sun.tools.javac.v8.Maino。每次一个新的Java版本发布，这个代码的包名就改变一次。这就允许一个组件的多个发布版本可以共存在同一个类加载器中并且这使得版本的选择是显式的。但这也不是一个非常好的解决方案，因为或者客户需要准确知道他们计划使用的版本而且必须改变他们的代码来转换到新的版本，或者他们必须依赖于一个包装类来转发方案调用给最新的版本（在这种情况下，这些包装类就会承受我们上面提到的相同问题）。<br /><br /><b><span style="FONT-SIZE: 16px">加载多个发布版本</span></b><br />这里我们遇到的问题在大多数项目中也存在，所有的类都会被加载到一个全局命名空间。如果每一个组件有自己的命名空间而且他会加载所有他依赖的组件到这个命名空间而不影响进程的其他部分，那又会怎么样呢？实际上我们可以在Java中这么做！类名不需要是唯一的，只要类名和其所对应的ClassLoader的组合是唯一的就可以了。这意味着ClassLoader类似于命名空间，而如果我们可以加载每一个组件在自己的ClassLoader中，他就可以控制如何满足依赖。他可以代理类定位给其他的包含他的依赖组件所需要的特定版本的ClassLoader。如图1。<br /><br /><img onmouseover="javascript:imgShowTip(this);" style="DISPLAY: inline" onclick="javascript:imgClick(this);" alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2005_11_03_091543_jFuXkmWKDz.gif" onload="javascript:imgLoad(this);" border="0" resized="0" /><br />Figure 1. Decentralized class loaders<br /><br />然而这个架构并不比绑定每一个依赖的JAR在自己的JAR中好多少。我们需要的是一个可以确保每一个组件版本仅被一个类加载器加载的中央集权。图2中的架构可以确定每一个组件版本仅被加载一次。<br /><br /><img onmouseover="javascript:imgShowTip(this);" style="DISPLAY: inline" onclick="javascript:imgClick(this);" alt="" src="http://www.matrix.org.cn/resource/upload/forum/2005_11_03_091558_yYMFUminIK.gif" onload="javascript:imgLoad(this);" border="0" resized="0" /><br />Figure 2. Class loaders with mediator<br /><br />为了实现这种方式，我们需要创建两个不同类型的类加载器。每一个ComponentClassLoader需要扩展Java的URLClassLoader来提供需要的逻辑来从一个JAR中获取.class文件。当然他也会执行两个其他的任务。在创建的时候，他会获取JAR清单文件并定位一个新属性Restricted-Class-Path。不像Sun提供的Class-Path属性，这个属性暗示特定的JAR应该只对这个组件有效。<br /><pre class="overflow"><span style="FONT-SIZE: 12px">public class ComponentClassLoader extends URLClassLoader {<br />  // ...<br /><br />  public ComponentClassLoader (MasterClassLoader master, File file)<br />  {<br />    // ...<br />    JarFile jar = new JarFile(file);<br />    Manifest man = jar.getManifest();<br />    Attributes attr = man.getMainAttributes();<br /><br />    List l = new ArrayList();<br />    String str = attr.getValue("Restricted-Class-Path");<br />    if (str != null) {<br />        StringTokenizer tok = new StringTokenizer(str);<br />        while (tok.hasMoreTokens()) {<br />            l.add(new File(file.getParentFile(),<br />                           tok.nextToken());<br />        }<br />    }<br /><br />    this.dependencies = l;<br />  }<br /><br />  public Class loadClass (String name, boolean resolve)<br />    throws ClassNotFoundException<br />  {<br />    try {<br />      // Try to load the class from our JAR.<br />      return loadClassForComponent(name, resolve);<br />    } catch (ClassNotFoundException ex) {}<br /><br />    // Couldn't find it -- let the master look for it<br />    // in another components.<br />    return master.loadClassForComponent(name,<br />                           resolve, dependencies);<br />  }<br />    <br />  public Class loadClassForComponent (String name,<br />                                   boolean resolve)<br />    throws ClassNotFoundException<br />  {<br />    Class c = findLoadedClass(name);<br />    <br />    // Even if findLoadedClass returns a real class,<br />    // we might simply be its initiating ClassLoader.<br />    // Only return it if we're actually its defining<br />    // ClassLoader (as determined by Class.getClassLoader).<br />    //<br />    if (c == null || c.getClassLoader() != this) {<br />        c = findClass(name);<br />    <br />        if (resolve) {<br />            resolveClass(c);<br />        }<br />    }<br />    return c;<br />  }<br />}</span></pre><br /><br />当一个请求要求加载一个在特定JAR中不存在的类时，他会显式的调用MasterClassLoader并传递他的JAR依赖列表作为参数而不是简单的转发给父类加载器。然后MasterClassLoader将每一个特定依赖请求转发给ComponentClassLoader<br /><pre class="overflow"><span style="FONT-SIZE: 12px">public class MasterClassLoader extends ClassLoader {<br />  // ...<br /><br />  public Class loadClassForComponent (String name,<br />                      boolean resolve, List files)<br />    throws ClassNotFoundException<br />  {<br />    try {<br />      return loadClass(name, resolve);<br />    } catch (ClassNotFoundException ex) {}<br /><br />    for (Iterator i = files.iterator(); i.hasNext(); ) {<br />      File f = (File)i.next();<br /><br />      try {<br />        ComponentClassLoader ccl =<br />            getComponentClassLoader(f);<br />        return ccl.loadClassForComponent(name, resolve);<br />      } catch (Exception ex) {<br />        // simplified for clarity<br />      }<br />    }<br /><br />    throw new ClassNotFoundException(name);<br />  }<br />}</span></pre><br /><br />这种方法有许多有用的特性。最重要的是我们现在可以满足原始的依赖图而不需要修改代码（理论上是这样的，但还需要看一下面给出的警告）。他减少了组件间的耦合，因为每一个组件可以依赖于他所需要的组件版本，而不需要强制其他组件升级或降级版本来满足他。<br /><br />另一个优点是这种技术增加了透明性。每一个组件的运行时依赖被显式地列出来了，而且这是强制的。即使使用Class-Path清单属性，你也不能确信你没有误匹配一个依赖。考虑一下当你的组件使用commons-log组件时，后来使用log4j来做日志处理。你可能有其他组件依赖log4j但没有定义这个依赖。因为他已经被加在classpath，你也不会检查到这个问题，但如果有一天你用其他的日志处理代替了log4j，你就有问题了。相反，如果使用Restricted-Class-Path而你没有列出log4j作为依赖，你会得到一个ClassNotFoundException异常。<br /><br /><b><span style="FONT-SIZE: 16px">重写系统类加载器</span></b><br />现在我们已经有了一个类加载器可以实现我们新的版本策略，我们需要通过某种方式来安装了。如果我们的代码会被嵌入在应用服务器中或其他类型的解释器，那么解释器的代码可以编程创建新的类加载器并使用他来加载我们的代码。通过这种方式，一个服务器进程可以通过定义在请求中定义需要的版本来执行多个代码版本。但如果我们只想在普通的Java应用中使用时需要怎么做呢？<br /><br />一种主观的方式是使用Java1.5的-javaagent命令行参数。这样我们可以在加载我们应用的主类之前初始化特定的JAR（这称为代理）。不幸地是，代理类被与加载主类的同一个类加载器加载（系统类加载器），因此在这时安装我们的自定义类加载器已经太迟了，因为我们的代理的方法已经执行了。<br /><br />另一种方式是创建一个“引导”主类来建立类加载器并且使用他来定位我们实际的主类并执行主方法。这种方式很简单，但去掉了一些Java的好的用法如-classpath和-jar选项并且需要我们自己调用主方法。<br /><br />实际上，我们可以重写java.system.class.loader系统属性来使我们的类加载器作为系统类加载器被初始化。这样做的话，我们会创建第三个类加载器WrapperClassLoader作为系统类加载器的代替。他的父类会是引导类加载器，他包含Java运行时类库（rt.jar）。在初始化的时候，他会读取java.library.path系统属性并且为每一个特定的JAR创建ComponentClassLoader。<br /><br /><pre class="overflow"><span style="FONT-SIZE: 12px">public static List initClassLoaders (MasterClassLoader master)<br />  throws MalformedURLException, IOException<br />{<br />  List loaders = new ArrayList();<br /><br />  String classpath =<br />                System.getProperty("java.class.path");<br /><br />  StringTokenizer tok = new StringTokenizer(classpath,<br />                                  File.pathSeparator);<br /><br />  while (tok.hasMoreTokens()) {<br />    File file = new File(tok.nextToken());<br />    loaders.add(master.getComponentClassLoader(file));<br />  }<br /><br />  return loaders;<br />}</span></pre><br /><br />现在我们可以像下面那样运行我们的搜索引擎了：<br /><span style="COLOR: blue">$ java -Xbootclasspath/a:classloader.jar \<br />    -Djava.system.class.loader=<br />        com.onjava.classloader.WrapperClassLoader \<br />    -jar metasearch.jar<br />SOAP v1: remotely invoking searchAmazon<br />SOAP v2: remotely invoking searchGoogle (with newFlag = true)</span><br /><br /><b><span style="FONT-SIZE: 16px">小结</span></b><br />在最后版本中，我们实际上进行了超过原始需求的更多研究。除了在一个静态字段中嵌入版本号之外，我们现在可以从属性文件中获取了。这意味着可以通过我们的类加载器加载资源文件，而且必须包含与实际类加载类似的逻辑。我们也可以修改一下soap-v2.jar的API，从 public Object invokeMethod (String name, Object[] args)<br />到public Object invokeMethod (String name, Object[] args,                    boolean newFlag)<br /><br />这看起来有些奇怪，但这意味站如果我们将刚才运行的源程序放在同一个目录下，我们可能不能编译他。如果我们尝试用同一版本的soap.jar同时构建google 和amazon，其中一个的方法标识可能不匹配。如果我们用两个soap.jar的版本，又会得到重复类错误。但是，我们可以分别编译google.jar和amazon.jar，而不需要考虑他们是否使用兼容的soap.jar版本，而且我们可以在同一进程中用不同的类加载器运行他们。<br /><br />考虑一下，如果将这种技术运用在一个在构建时管理组件依赖的构建工具（如Maven），你将不会遇到缺少依赖或JAR冲突的问题了。<br /><br /><br />关于作者：Don Schwarz是一家专注于元编程和语言集成的大投资银行的Java开发人员。<br /><br /><b>资源</b><br />·onjava.com:<a href="http://www.onjava.com/" target="_new">onjava.com</a><br />·Matrix-Java开发者社区:<a href="http://www.matrix.org.cn/" target="_new">http://www.matrix.org.cn/</a><br /><img src ="http://www.blogjava.net/ASONG/aggbug/48758.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2006-05-29 15:08 <a href="http://www.blogjava.net/ASONG/articles/48758.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>log4j配置详解 </title><link>http://www.blogjava.net/ASONG/articles/48755.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Mon, 29 May 2006 07:05:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/48755.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/48755.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/48755.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/48755.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/48755.html</trackback:ping><description><![CDATA[
		<font size="2">
				<font color="#0000ff">
						<strong>Log4j日志管理系统简单使用说明<br /></strong>
						<br />
				</font>    通常，我们都提供一个名为 log4j.properties的文件，在第一次调用到Log4J时，Log4J会在类路径（../web-inf/class/当然也可以放到其它任何目录，只要该目录被包含到类路径中即可）中定位这个文件，并读入这个文件完成的配置。这个配置文件告诉Log4J以什么样的格式、把什么样的信息、输出到什么地方。<br />　　Log4j有三个主要的组件：Loggers(记录器)，Appenders (输出源)和Layouts(布局)，这里可简单理解为日志类别，日志要输出的地方和日志以何种形式输出。综合使用这三个组件可以轻松的记录信息的类型和级别，并可以在运行时控制日志输出的样式和位置。下面对三个组件分别进行说明：<br />　　<br />　　</font>
		<font size="2">
				<strong>1、 Loggers<br /></strong>　　Loggers组件在此系统中被分为五个级别：DEBUG、INFO、WARN、ERROR和FATAL。这五个级别是有顺序的，DEBUG &lt; INFO &lt; WARN &lt; ERROR &lt; FATAL，分别用来指定这条日志信息的重要程度,明白这一点很重要，这里Log4j有一个规则：假设Loggers级别为P，如果在Loggers中发生了一个级别Q比P高，则可以启动，否则屏蔽掉。<br />假设你定义的级别是info，那么error和warn的日志可以显示而比他低的debug信息就不显示了。<br /> 　<br />　　Java程序举例来说：<br />　　<br />　　//建立Logger的一个实例，命名为“com.foo”<br />　　　Logger　logger = Logger.getLogger("com.foo"); //"com.foo"是实例进行命名，也可以任意<br />　　//设置logger的级别。通常不在程序中设置logger的级别。一般在配置文件中设置。<br />　　logger.setLevel(Level.INFO);<br />　　Logger barlogger = Logger.getLogger("com.foo.Bar");<br />　　//下面这个请求可用，因为WARN &gt;= INFO<br />　　logger.warn("Low fuel level.");<br />　　//下面这个请求不可用，因为DEBUG &lt; INFO<br />　　logger.debug("Starting search for nearest gas station.");<br />　　//命名为“com.foo.bar”的实例barlogger会继承实例“com.foo”的级别。因此，下面这个请求可用，因为INFO &gt;= INFO<br />　　barlogger.info("Located nearest gas station.");<br />　　//下面这个请求不可用，因为DEBUG &lt; INFO<br />　　barlogger.debug("Exiting gas station search");<br />　　这里“是否可用”的意思是能否输出Logger信息。<br />　　　　在对Logger实例进行命名时，没有限制，可以取任意自己感兴趣的名字。一般情况下建议以类的所在位置来命名Logger实例，这是目前来讲比较有效的Logger命名方式。这样可以使得每个类建立自己的日志信息，便于管理。比如：<br />　　<br />　　static Logger logger = Logger.getLogger(ClientWithLog4j.class.getName());<br />　　<br />　</font>
		<font size="2">
				<strong>　2、Appenders<br /></strong>　　禁用与使用日志请求只是Log4j其中的一个小小的地方，Log4j日志系统允许把日志输出到不同的地方，如控制台（Console）、文件（Files）、根据天数或者文件大小产生新的文件、以流的形式发送到其它地方等等。<br />　　<br />　　其语法表示为：<br />　　<br />　　org.apache.log4j.ConsoleAppender（控制台）<br />　　org.apache.log4j.FileAppender（文件）<br />　　org.apache.log4j.DailyRollingFileAppender（每天产生一个日志文件）<br />    org.apache.log4j.RollingFileAppender（文件大小到达指定尺寸的时候产生一个新的文件）<br />　　org.apache.log4j.WriterAppender（将日志信息以流格式发送到任意指定的地方）<br />　　<br />　　配置时使用方式为：<br />　　log4j.appender.appenderName = fully.qualified.name.of.appender.class<br />　　log4j.appender.appenderName.option1 = value1<br />　　…<br />    log4j.appender.appenderName.option = valueN<br />　　这样就为日志的输出提供了相当大的便利。<br />　　<br /><strong>　　3、Layouts</strong><br />　　有时用户希望根据自己的喜好格式化自己的日志输出。Log4j可以在Appenders的后面附加Layouts来完成这个功能。Layouts提供了四种日志输出样式，如根据HTML样式、自由指定样式、包含日志级别与信息的样式和包含日志时间、线程、类别等信息的样式等等。<br />　　<br />　　其语法表示为：<br />　　<br />　　org.apache.log4j.HTMLLayout（以HTML表格形式布局），<br />　　org.apache.log4j.PatternLayout（可以灵活地指定布局模式），<br />　　org.apache.log4j.SimpleLayout（包含日志信息的级别和信息字符串），<br />　　org.apache.log4j.TTCCLayout（包含日志产生的时间、线程、类别等等信息）<br />　　<br />　　配置时使用方式为：<br />　　<br />　　log4j.appender.appenderName.layout =fully.qualified.name.of.layout.class<br />　　log4j.appender.appenderName.layout.option1 = value1<br />　　…<br />　　log4j.appender.appenderName.layout.option = valueN</font>
		<p>
				<font size="2">
						<strong>
								<font color="#0000ff"> Log4j的配置 </font>
						</strong>
						<br />　　<br />　　以上是从原理方面说明Log4j的使用方法，在具体Java编程使用Log4j可以参照以下示例：<br />　　<br />　　<strong>1、 建立Logger实例</strong>：<br />　　语法表示：public static Logger getLogger( String name)<br />　　实际使用：static Logger logger = Logger.getLogger(ServerWithLog4j.class.getName ()) ;<br />　　<br />　　<strong>2、 读取配置文件</strong>：<br />　　获得了Logger的实例之后，接下来将配置Log4j使用环境：<br />　　语法表示：<br />　　BasicConfigurator.configure()：自动快速地使用缺省Log4j环境。<br />　　PropertyConfigurator.configure(String configFilename)：读取使用Java的特性文件编写的配置文件。<br />　　DOMConfigurator.configure(String filename)：读取XML形式的配置文件。<br />　　实际使用：<br />    PropertyConfigurator.configure("ServerWithLog4j.properties");<br />　　<br />　　<strong>3、 插入日志信息</strong><br />　　完成了以上连个步骤以后，下面就可以按日志的不同级别插入到你要记录日志的任何地方了。<br />　　语法表示：<br />　　Logger.debug(Object message);//调试信息<br />　　Logger.info(Object message);//一般信息<br />　　Logger.warn(Object message);//警告信息<br />　　Logger.error(Object message);//错误信息<br />　　Logger.fatal(Object message);//致命错误信息</font>
		</p>
		<p>
				<font size="2">　　实际使用：logger.info("ServerSocket before accept: " + server);<br />　　<br /><strong><font color="#0000ff">  配置过程 <br /><br /></font></strong>　在实际编程时，要使Log4j真正在系统中运行事先还要对配置文件进行定义。定义步骤就是对Logger、Appender及Layout的分别使用。<br />    Log4j支持两种配置文件格式，一种是XML格式的文件，一种是java properties（key=value）【Java特性文件（键=值）】。下面我们介绍使用Java特性文件做为配置文件的方法<br />   具体如下：<br />　　<br />　　<strong>1、配置根Logger，</strong>其语法为：<br />　　log4j.rootLogger = [ level ] , appenderName1, appenderName2, …<br />         level : 是日志记录的优先级，分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。Log4j建议只使用四个级别，优先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别，您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别，则应用程序中所有DEBUG级别的日志信息将不被打印出来。<br />　　     appenderName:就是指定日志信息输出到哪个地方。您可以同时指定多个输出目的地。<br />   例如：log4j.rootLogger＝info,A1,B2,C3<br />　　<br />　　<strong>2、配置日志信息输出目的地</strong>，其语法为：<br />　　log4j.appender.appenderName = fully.qualified.name.of.appender.class  //<br />　　  "fully.qualified.name.of.appender.class" 可以指定下面五个目的地中的一个：<br />          1.org.apache.log4j.ConsoleAppender（控制台）<br />          2.org.apache.log4j.FileAppender（文件）<br />          3.org.apache.log4j.DailyRollingFileAppender（每天产生一个日志文件）<br />          4.org.apache.log4j.RollingFileAppender（文件大小到达指定尺寸的时候产生一个新的文件）<br />          5.org.apache.log4j.WriterAppender（将日志信息以流格式发送到任意指定的地方）<br />             <strong>1.ConsoleAppender选项<br /></strong>                    Threshold=WARN:指定日志消息的输出最低层次。<br />                    ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。<br />                    Target=System.err：默认情况下是：System.out,指定输出控制台<br />              <strong>2.FileAppender</strong> 选项<br />                    Threshold=WARN:指定日志消息的输出最低层次。<br />                    ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。<br />                    File=mylog.txt:指定消息输出到mylog.txt文件。<br />                    Append=false:默认值是true,即将消息增加到指定文件中，false指将消息覆盖指定的文件内容。<br />            3.<strong>DailyRollingFileAppender</strong> 选项<br />                    Threshold=WARN:指定日志消息的输出最低层次。<br />                    ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。<br />                     File=mylog.txt:指定消息输出到 mylog.txt文件。<br />                    Append=false:默认值是true,即将消息增加到指定文件中，false指将消息覆盖指定的文件内容。<br />                    DatePattern='.'yyyy-ww:每周滚动一次文件，即每周产生一个新的文件。当然也可以指定按月、周、天、时和分。即对应的格式如下：<br />                    1)'.'yyyy-MM: 每月<br />                    2)'.'yyyy-ww: 每周 <br />                    3)'.'yyyy-MM-dd: 每天<br />                    4)'.'yyyy-MM-dd-a: 每天两次<br />                    5)'.'yyyy-MM-dd-HH: 每小时<br />                    6)'.'yyyy-MM-dd-HH-mm: 每分钟<br />            4.<strong>RollingFileAppender</strong> 选项<br />                     Threshold=WARN:指定日志消息的输出最低层次。<br />                     ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。<br />                    File=mylog.txt:指定消息输出到mylog.txt文件。<br />                    Append=false:默认值是true,即将消息增加到指定文件中，false指将消息覆盖指定的文件内容。<br />                    MaxFileSize=100KB: 后缀可以是KB, MB 或者是 GB. 在日志文件到达该大小时，将会自动滚动，即将原来的内容移到mylog.log.1文件。<br />                    MaxBackupIndex=2:指定可以产生的滚动文件的最大数。</font>
		</p>
		<p>
				<font size="2">实际应用：<br />　　log4j.appender.A1=org.apache.log4j.ConsoleAppender //这里指定了日志输出的第一个位置A1是控制台ConsoleAppender<br />　　<br />　　<strong>3、配置日志信息的格式</strong>，其语法为：<br />　　<strong>A.</strong><strong>log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class<br /></strong>              "fully.qualified.name.of.layout.class" 可以指定下面4个格式中的一个：<br />               1.org.apache.log4j.HTMLLayout（以HTML表格形式布局），<br />　　       2.org.apache.log4j.PatternLayout（可以灵活地指定布局模式），<br />　　       3.org.apache.log4j.SimpleLayout（包含日志信息的级别和信息字符串），<br />　　       4.org.apache.log4j.TTCCLayout（包含日志产生的时间、线程、类别等等信息）<br />                   1.HTMLLayout 选项<br />                      LocationInfo=true:默认值是false,输出java文件名称和行号<br />                      Title=my app file: 默认值是 Log4J Log Messages.<br />                   2.PatternLayout 选项<br />                      ConversionPattern=%m%n :指定怎样格式化指定的消息。<br />                   3.XMLLayout  选项<br />                      LocationInfo=true:默认值是false,输出java文件和行号<br />   实际应用：<br />   　　log4j.appender.A1.layout=org.apache.log4j.PatternLayout</font>
		</p>
		<p>
				<font size="2">       <strong>B</strong>.<strong> log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n<br /></strong>           这里需要说明的就是日志信息格式中几个符号所代表的含义：<br />　　         －X号: X信息输出时左对齐；<br />                   %p: 输出日志信息优先级，即DEBUG，INFO，WARN，ERROR，FATAL,<br />                   %d: 输出日志时间点的日期或时间，默认格式为ISO8601，也可以在其后指定格式，比如：%d{yyy MMM dd HH:mm:ss,SSS}，输出类似：2002年10月18日 22：10：28，921<br />                   %r: 输出自应用启动到输出该log信息耗费的毫秒数<br />                   %c: 输出日志信息所属的类目，通常就是所在类的全名<br />                   %t: 输出产生该日志事件的线程名<br />                   %l: 输出日志事件的发生位置，相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程，以及在代码中的行数。举例：Testlog4.main (TestLog4.java:10)<br />                   %x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。<br />                   %%: 输出一个"%"字符<br />                   %F: 输出日志消息产生时所在的文件名称<br />                   %L: 输出代码中的行号<br />                   %m: 输出代码中指定的消息,产生的日志具体信息<br />                   %n: 输出一个回车换行符，Windows平台为"\r\n"，Unix平台为"\n"输出日志信息换行<br />            可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。如：<br />                     1)%20c：指定输出category的名称，最小的宽度是20，如果category的名称小于20的话，默认的情况下右对齐。<br />                     2)%-20c:指定输出category的名称，最小的宽度是20，如果category的名称小于20的话，"-"号指定左对齐。<br />                     3)%.30c:指定输出category的名称，最大的宽度是30，如果category的名称大于30的话，就会将左边多出的字符截掉，但小于30的话也不会有空格。<br />                     4)%20.30c:如果category的名称小于20就补空格，并且右对齐，如果其名称长于30字符，就从左边交远销出的字符截掉。</font>
		</p>
		<p>
				<font size="2">　　这里上面三个步骤是对前面Log4j组件说明的一个简化；下面给出一个具体配置例子，在程序中可以参照执行：<br />　　log4j.rootLogger=INFO,A1，B2<br />　　log4j.appender.A1=org.apache.log4j.ConsoleAppender<br />　　log4j.appender.A1.layout=org.apache.log4j.PatternLayout<br />　　log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n<br /> 　　根据上面的日志格式，某一个程序的输出结果如下：<br />　　0　　INFO　2003-06-13 13:23:46968 ClientWithLog4j Client socket: Socket[addr=localhost/127.0.0.1,port=8002,localport=2014]<br />         16　 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server says: 'Java server with log4j, Fri Jun 13 13:23:46 CST 2003'<br />　　16　 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j GOOD<br />　　16　 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server responds: 'Command 'HELLO' not understood.'<br />　　16　 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j HELP<br />　　16　 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j Server responds: 'Vocabulary: HELP QUIT'<br />　　16　 DEBUG 2003-06-13 13:23:46984 ClientWithLog4j QUIT<br /> <br />     <strong> 4. # 当输出信息于回滚文件时</strong></font>
		</p>
		<p>
				<font size="2">          log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender   //指定以文件的方式输出日志<br />           log4j.appender.ROLLING_FILE.Threshold=ERROR <br />           log4j.appender.ROLLING_FILE.File=rolling.log  //文件位置,也可以用变量${java.home}、rolling.log<br />           log4j.appender.ROLLING_FILE.Append=true <br />           log4j.appender.ROLLING_FILE.MaxFileSize=10KB  //文件最大尺寸<br />           log4j.appender.ROLLING_FILE.MaxBackupIndex=1  //备份数<br />           log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout <br />           log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n  　　</font>
		</p>
		<p>
				<font size="2">××××××××××××××××××××××××××××××××××××××××××××××××<br /><strong><font color="#0000ff"> <br /></font></strong><strong><font color="#0000ff"> Log4j比较全面的配置 <br /></font></strong></font>
		</p>
		<p>
				<font size="2"> LOG4J的配置之简单使它遍及于越来越多的应用中了：Log4J配置文件实现了输出到控制台、文件、回滚文件、发送日志邮件、输出到数据库日志表、自定义标签等全套功能。择其一二使用就够用了， </font>
		</p>
		<p>
				<font size="2"> log4j.rootLogger=DEBUG,CONSOLE,A1,im <br /> log4j.addivity.org.apache=true </font>
		</p>
		<p>
				<font size="2"> # 应用于控制台 </font>
		</p>
		<p>
				<font size="2"> log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender <br /> log4j.appender.Threshold=DEBUG <br /> log4j.appender.CONSOLE.Target=System.out <br /> log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout <br /> log4j.appender.CONSOLE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n <br /> #log4j.appender.CONSOLE.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD] n%c[CATEGORY]%n%m[MESSAGE]%n%n </font>
		</p>
		<p>
				<font size="2"> #应用于文件 </font>
		</p>
		<p>
				<font size="2"> log4j.appender.FILE=org.apache.log4j.FileAppender <br /> log4j.appender.FILE.File=file.log <br /> log4j.appender.FILE.Append=false <br /> log4j.appender.FILE.layout=org.apache.log4j.PatternLayout <br /> log4j.appender.FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n <br /> # Use this layout for LogFactor 5 analysis </font>
		</p>
		<p>
				<font size="2"> # 应用于文件回滚 </font>
		</p>
		<p>
				<font size="2"> log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender <br /> log4j.appender.ROLLING_FILE.Threshold=ERROR <br /> log4j.appender.ROLLING_FILE.File=rolling.log  //文件位置,也可以用变量${java.home}、rolling.log<br /> log4j.appender.ROLLING_FILE.Append=true       //true:添加  false:覆盖<br /> log4j.appender.ROLLING_FILE.MaxFileSize=10KB   //文件最大尺寸<br /> log4j.appender.ROLLING_FILE.MaxBackupIndex=1  //备份数<br /> log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout <br /> log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n </font>
		</p>
		<p>
				<font size="2">
						<br /> #应用于socket <br /> log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender <br /> log4j.appender.SOCKET.RemoteHost=localhost <br /> log4j.appender.SOCKET.Port=5001 <br /> log4j.appender.SOCKET.LocationInfo=true <br /> # Set up for Log Facter 5 <br /> log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout <br /> log4j.appender.SOCET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n </font>
		</p>
		<p>
				<font size="2">
						<br /> # Log Factor 5 Appender <br /> log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender <br /> log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000 </font>
		</p>
		<p>
				<font size="2"> # 发送日志给邮件 </font>
		</p>
		<p>
				<font size="2"> log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender <br /> log4j.appender.MAIL.Threshold=FATAL <br /> log4j.appender.MAIL.BufferSize=10 <br /> <a href="mailto:log4j.appender.MAIL.From=web@www.wuset.com">log4j.appender.MAIL.From=web@www.wuset.com</a><br /> log4j.appender.MAIL.SMTPHost=www.wusetu.com <br /> log4j.appender.MAIL.Subject=Log4J Message <br /> <a href="mailto:log4j.appender.MAIL.To=web@www.wusetu.com">log4j.appender.MAIL.To=web@www.wusetu.com</a><br /> log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout <br /> log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n </font>
		</p>
		<p>
				<font size="2"> # 用于数据库 <br /> log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender <br /> log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test <br /> log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver <br /> log4j.appender.DATABASE.user=root <br /> log4j.appender.DATABASE.password= <br /> log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n') <br /> log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout <br /> log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n </font>
		</p>
		<p>
				<font size="2">
						<br /> log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender <br /> log4j.appender.A1.File=SampleMessages.log4j <br /> log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j' <br /> log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout </font>
		</p>
		<p>
				<font size="2"> #自定义Appender </font>
		</p>
		<p>
				<font size="2"> log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender </font>
		</p>
		<p>
				<font size="2"> log4j.appender.im.host = mail.cybercorlin.net <br /> log4j.appender.im.username = username <br /> log4j.appender.im.password = password <br /> log4j.appender.im.recipient = <a href="mailto:corlin@cybercorlin.net">corlin@cybercorlin.net</a></font>
		</p>
		<p>
				<font size="2"> log4j.appender.im.layout=org.apache.log4j.PatternLayout <br /> log4j.appender.im.layout.ConversionPattern =[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n</font>
		</p>
		<p>
				<font size="2">摘自:http://blog.csdn.net/eako/archive/2005/09/23/488099.aspx<br /></font>
		</p>
<img src ="http://www.blogjava.net/ASONG/aggbug/48755.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2006-05-29 15:05 <a href="http://www.blogjava.net/ASONG/articles/48755.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ClassLoader说明</title><link>http://www.blogjava.net/ASONG/articles/48719.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Mon, 29 May 2006 04:33:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/48719.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/48719.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/48719.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/48719.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/48719.html</trackback:ping><description><![CDATA[
		<p>这是一篇较早时候写的文章，最近在<a href="http://www.jdon.com/"><font color="#4a664d">J道</font></a>看到一个与classloader<a href="http://www.jdon.com/jive/thread.jsp?forum=16&amp;thread=13561"><font color="#4a664d">有关的讨论</font></a>，于是重新翻出来。</p>
		<p>
				<strong>静态库、动态连接库</strong>
		</p>
		<p>程序编制一般需经编辑、编译、连接、加载和运行几个步骤。在我们的应用中，有一些公共代码是需要反复使用，就把这些代码编译为库文件；在连接步骤中，连接器将从库文件取得所需的代码，复制到生成的可执行文件中。这种库称为静态库，其特点是可执行文件中包含了库代码的一份完整拷贝；缺点就是被多次使用就会有多份冗余拷贝。</p>
		<p>为了克服这个缺点可以采用动态连接库。这个时候连接器仅仅是在可执行文件中打上标志，说明需要使用哪些动态连接库；当运行程序时，加载器根据这些标志把所需的动态连接库加载到内存。</p>
		<p>另外在当前的编程环境中，一般都提供方法让程序在运行的时候把某个特定的动态连接库加载并运行，也可以将其卸载（例如Win32的LoadLibrary()&amp;FreeLibrary()和Posix的dlopen()&amp;dlclose()）。这个功能被广泛地用于在程序运行时刻更新某些功能模块或者是程序外观。</p>
		<p>
				<strong>What is ClassLoader?</strong>
		</p>
		<p>与普通程序不同的是，Java程序（class文件）并不是本地的可执行程序。当运行Java程序时，首先运行JVM（Java虚拟机），然后再把Java class加载到JVM里头运行，负责加载Java class的这部分就叫做Class Loader。</p>
		<p>JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader，和JVM一样，Bootstrap ClassLoader是用本地代码实现的，它负责加载核心Java Class（即所有java.*开头的类）。另外JVM还会提供两个ClassLoader，它们都是用Java语言编写的，由Bootstrap ClassLoader加载；其中Extension ClassLoader负责加载扩展的Java class（例如所有javax.*开头的类和存放在JRE的ext目录下的类），Application ClassLoader负责加载应用程序自身的类。</p>
		<p>
				<strong>When to load the class?</strong>
		</p>
		<p>什么时候JVM会使用ClassLoader加载一个类呢？当你使用java去执行一个类，JVM使用Application ClassLoader加载这个类；然后如果类A引用了类B，不管是直接引用还是用Class.forName()引用，JVM就会找到加载类A的ClassLoader，并用这个ClassLoader来加载类B。</p>
		<p>
				<strong>Why use your own ClassLoader?</strong>
		</p>
		<p>似乎JVM自身的ClassLoader已经足够了，为什么我们还需要创建自己的ClassLoader呢？</p>
		<p>因为JVM自带的ClassLoader只是懂得从本地文件系统加载标准的java class文件，如果编写你自己的ClassLoader，你可以做到：<br />1）在执行非置信代码之前，自动验证数字签名<br />2）动态地创建符合用户特定需要的定制化构建类<br />3）从特定的场所取得java class，例如数据库中<br />4) 等等</p>
		<p>事实上当使用Applet的时候，就用到了特定的ClassLoader，因为这时需要从网络上加载java class，并且要检查相关的安全信息。</p>
		<p>目前的应用服务器大都使用了ClassLoader技术，即使你不需要创建自己的ClassLoader，了解其原理也有助于更好地部署自己的应用。 </p>
		<p>
				<strong>ClassLoader Tree &amp; Delegation Model</strong>
		</p>
		<p>当你决定创建你自己的ClassLoader时，需要继承java.lang.ClassLoader或者它的子类。在实例化每个ClassLoader对象时，需要指定一个父对象；如果没有指定的话，系统自动指定ClassLoader.getSystemClassLoader()为父对象。如下图：</p>
		<p>
				<img alt="" src="http://www.51dibs.com/html/2006/article/uploadfile/200504/2005414114823644.gif" />
		</p>
		<p>在Java 1.2后，java class的加载采用所谓的委托模式（Delegation Modle），当调用一个ClassLoader.loadClass()加载一个类的时候，将遵循以下的步骤：<br />1）检查这个类是否已经被加载进来了？<br />2）如果还没有加载，调用父对象加载该类<br />3）如果父对象无法加载，调用本对象的findClass()取得这个类。</p>
		<p>所以当创建自己的Class Loader时，只需要重载findClass()这个方法。</p>
		<p>
				<strong>Unloading? Reloading?</strong>
		</p>
		<p>当一个java class被加载到JVM之后，它有没有可能被卸载呢？我们知道Win32有FreeLibrary()函数，Posix有dlclose()函数可以被调用来卸载指定的动态连接库，但是Java并没有提供一个UnloadClass()的方法来卸载指定的类。</p>
		<p>在Java中，java class的卸载仅仅是一种对系统的优化，有助于减少应用对内存的占用。既然是一种优化方法，那么就完全是JVM自行决定如何实现，对Java开发人员来说是完全透明的。</p>
		<p>在什么时候一个java class/interface会被卸载呢？Sun公司的<a href="http://java.sun.com/docs/books/jls/unloading-rationale.html"><font color="#4a664d">原话</font></a>是这么说的："class or interface may be unloaded if and only if its class loader is unreachable. Classes loaded by the bootstrap loader may not be unloaded."</p>
		<p>事实上我们关心的不是如何卸载类的，我们关心的是如何更新已经被加载了的类从而更新应用的功能。JSP则是一个非常典型的例子，如果一个JSP文件被更改了，应用服务器则需要把更改后的JSP重新编译，然后加载新生成的类来响应后继的请求。</p>
		<p>其实一个已经加载的类是无法被更新的，如果你试图用同一个ClassLoader再次加载同一个类，就会得到异常（java.lang.LinkageError: duplicate class definition），我们只能够重新创建一个新的ClassLoader实例来再次加载新类。至于原来已经加载的类，开发人员不必去管它，因为它可能还有实例正在被使用，只要相关的实例都被内存回收了，那么JVM就会在适当的时候把不会再使用的类卸载。</p>
<img src ="http://www.blogjava.net/ASONG/aggbug/48719.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2006-05-29 12:33 <a href="http://www.blogjava.net/ASONG/articles/48719.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于web.xml配置的详细说明</title><link>http://www.blogjava.net/ASONG/articles/47970.html</link><dc:creator>ASONG</dc:creator><author>ASONG</author><pubDate>Thu, 25 May 2006 02:01:00 GMT</pubDate><guid>http://www.blogjava.net/ASONG/articles/47970.html</guid><wfw:comment>http://www.blogjava.net/ASONG/comments/47970.html</wfw:comment><comments>http://www.blogjava.net/ASONG/articles/47970.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ASONG/comments/commentRss/47970.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ASONG/services/trackbacks/47970.html</trackback:ping><description><![CDATA[
		<font size="2">1 定义头和根元素<br />部署描述符文件就像所有XML文件一样，必须以一个XML头开始。这个头声明可以使用的XML版本并给出文件的字符编码。<br />DOCYTPE声明必须立即出现在此头之后。这个声明告诉服务器适用的<font style="BACKGROUND-COLOR: #800080" color="#000000">servlet</font>规范的版本（如2.2或2.3）并指定管理此文件其余部分内容的语法的DTD(Document Type Definition，文档类型定义)。<br />所有部署描述符文件的顶层（根）元素为web-app。请注意，XML元素不像HTML，他们是大小写敏感的。因此，web-App和WEB-APP都是不合法的，web-app必须用小写。<br />2 部署描述符文件内的元素次序<br />XML 元素不仅是大小写敏感的，而且它们还对出现在其他元素中的次序敏感。例如，XML头必须是文件中的第一项，DOCTYPE声明必须是第二项，而web- app元素必须是第三项。在web-app元素内，元素的次序也很重要。服务器不一定强制要求这种次序，但它们允许（实际上有些服务器就是这样做的）完全拒绝执行含有次序不正确的元素的Web应用。这表示使用非标准元素次序的web.xml文件是不可移植的。<br />下面的列表给出了所有可直接出现在web-app元素内的合法元素所必需的次序。例如，此列表说明servlet元素必须出现在所有servlet-mapping元素之前。请注意，所有这些元素都是可选的。因此，可以省略掉某一元素，但不能把它放于不正确的位置。<br />l icon icon元素指出IDE和GUI工具用来表示Web应用的一个和两个图像文件的位置。<br />l display-name display-name元素提供GUI工具可能会用来标记这个特定的Web应用的一个名称。<br />l description description元素给出与此有关的说明性文本。<br />l context-param context-param元素声明应用范围内的初始化参数。<br />l filter 过滤器元素将一个名字与一个实现javax.servlet.Filter接口的类相关联。<br />l filter-mapping 一旦命名了一个过滤器，就要利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。<br />l listener servlet API的版本2.3增加了对事件监听程序的支持，事件监听程序在建立、修改和删除会话或servlet环境时得到通知。Listener元素指出事件监听程序类。<br />l servlet 在向servlet或JSP页面制定初始化参数或定制URL时，必须首先命名servlet或JSP页面。Servlet元素就是用来完成此项任务的。<br />l servlet-mapping 服务器一般为servlet提供一个缺省的URL：</font>
		<a href="http://host/webAppPrefix/servlet/ServletName" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/servlet/ServletName</font>
				</font>
		</a>
		<font size="2">。但是，常常会更改这个URL，以便servlet可以访问初始化参数或更容易地处理相对URL。在更改缺省URL时，使用servlet-mapping元素。<br />l session-config 如果某个会话在一定时间内未被访问，服务器可以抛弃它以节省内存。可通过使用HttpSession的setMaxInactiveInterval方法明确设置单个会话对象的超时值，或者可利用session-config元素制定缺省超时值。<br />l mime-mapping 如果Web应用具有想到特殊的文件，希望能保证给他们分配特定的MIME类型，则mime-mapping元素提供这种保证。<br />l welcom-file-list welcome-file-list元素指示服务器在收到引用一个目录名而不是文件名的URL时，使用哪个文件。<br />l error-page error-page元素使得在返回特定HTTP状态代码时，或者特定类型的异常被抛出时，能够制定将要显示的页面。<br />l taglib taglib元素对标记库描述符文件（Tag Libraryu Descriptor file）指定别名。此功能使你能够更改TLD文件的位置，而不用编辑使用这些文件的JSP页面。<br />l resource-env-ref resource-env-ref元素声明与资源相关的一个管理对象。<br />l resource-ref resource-ref元素声明一个资源工厂使用的外部资源。<br />l security-constraint security-constraint元素制定应该保护的URL。它与login-config元素联合使用<br />l login-config 用login-config元素来指定服务器应该怎样给试图访问受保护页面的用户授权。它与sercurity-constraint元素联合使用。<br />l security-role security-role元素给出安全角色的一个列表，这些角色将出现在servlet元素内的security-role-ref元素的role-name子元素中。分别地声明角色可使高级IDE处理安全信息更为容易。<br />l env-entry env-entry元素声明Web应用的环境项。<br />l ejb-ref ejb-ref元素声明一个EJB的主目录的引用。<br />l ejb-local-ref ejb-local-ref元素声明一个EJB的本地主目录的应用。<br />3 分配名称和定制的UL<br />在web.xml中完成的一个最常见的任务是对servlet或JSP页面给出名称和定制的URL。用servlet元素分配名称，使用servlet-mapping元素将定制的URL与刚分配的名称相关联。<br />3.1 分配名称<br />为了提供初始化参数，对servlet或JSP页面定义一个定制URL或分配一个安全角色，必须首先给servlet或JSP页面一个名称。可通过 servlet元素分配一个名称。最常见的格式包括servlet-name和servlet-class子元素（在web-app元素内），如下所示：<br />&lt;servlet&gt;<br />&lt;servlet-name&gt;Test&lt;/servlet-name&gt;<br />&lt;servlet-class&gt;moreservlets.TestServlet&lt;/servlet-class&gt;<br />&lt;/servlet&gt; <br />这表示位于WEB-INF/classes/moreservlets/TestServlet的servlet已经得到了注册名Test。给 servlet一个名称具有两个主要的含义。首先，初始化参数、定制的URL模式以及其他定制通过此注册名而不是类名引用此servlet。其次,可在 URL而不是类名中使用此名称。因此，利用刚才给出的定义，URL </font>
		<a href="http://host/webAppPrefix/servlet/Test" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/servlet/Test</font>
				</font>
		</a>
		<font size="2">可用于 </font>
		<a href="http://host/webAppPrefix/servlet/moreservlets.TestServlet" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/servlet/moreservlets.TestServlet</font>
				</font>
		</a>
		<font size="2">的场所。<br />请记住：XML元素不仅是大小写敏感的，而且定义它们的次序也很重要。例如，web-app元素内所有servlet元素必须位于所有servlet- mapping元素（下一小节介绍）之前，而且还要位于5.6节和5.11节讨论的与过滤器或文档相关的元素（如果有的话）之前。类似地，servlet 的servlet-name子元素也必须出现在servlet-class之前。5.2节"部署描述符文件内的元素次序"将详细介绍这种必需的次序。<br />例如，程序清单5-1给出了一个名为TestServlet的简单servlet，它驻留在moreservlets程序包中。因为此servlet是扎根在一个名为deployDemo的目录中的Web应用的组成部分，所以TestServlet.class放在deployDemo/WEB- INF/classes/moreservlets中。程序清单5-2给出将放置在deployDemo/WEB-INF/内的web.xml文件的一部分。此web.xml文件使用servlet-name和servlet-class元素将名称Test与TestServlet.class相关联。图 5-1和图5-2分别显示利用缺省URL和注册名调用TestServlet时的结果。<br />程序清单5-1 TestServlet.java<br />package moreservlets;<br /><br />import java.io.*;<br />import javax.servlet.*;<br />import javax.servlet.http.*;<br /><br />/** Simple servlet used to illustrate servlet naming<br />* and custom URLs.<br />* &lt;P&gt;<br />* Taken from More Servlets and JavaServer Pages<br />* from Prentice Hall and Sun Microsystems Press,<br />* </font>
		<a href="http://www.moreservlets.com/." target="_blank">
				<font color="#1d58d1">
						<font size="2">http://www.moreservlets.com/.</font>
				</font>
		</a>
		<br />
		<font size="2">* © 2002 Marty Hall; may be freely used or adapted.<br />*/<br /><br />public class TestServlet extends HttpServlet {<br />public void doGet(HttpServletRequest request,<br />HttpServletResponse response)<br />throws ServletException, IOException {<br />response.setContentType("text/html");<br />PrintWriter out = response.getWriter();<br />String uri = request.getRequestURI();<br />out.println(ServletUtilities.headWithTitle("Test Servlet") +<br />"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n" +<br />"&lt;H2&gt;URI: " + uri + "&lt;/H2&gt;\n" +<br />"&lt;/BODY&gt;&lt;/HTML&gt;");<br />}<br />}<br /><br />程序清单5-2 web.xml（说明servlet名称的摘录）<br />&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br />&lt;!DOCTYPE web-app<br />PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br />"</font>
		<a href="http://java.sun.com/dtd/web-app_2_3.dtd" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://java.sun.com/dtd/web-app_2_3.dtd</font>
				</font>
		</a>
		<font size="2">"&gt;<br /><br />&lt;web-app&gt;<br />&lt;!-- … --&gt;<br />&lt;servlet&gt;<br />&lt;servlet-name&gt;Test&lt;/servlet-name&gt;<br />&lt;servlet-class&gt;moreservlets.TestServlet&lt;/servlet-class&gt;<br />&lt;/servlet&gt;<br />&lt;!-- … --&gt;<br />&lt;/web-app&gt;<br /><br />3.2 定义定制的URL<br />大多数服务器具有一个缺省的serlvet URL：<br /></font>
		<a href="http://host/webAppPrefix/servlet/packageName.ServletName" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/servlet/packageName.ServletName</font>
				</font>
		</a>
		<font size="2">。虽然在开发中使用这个URL很方便，但是我们常常会希望另一个URL用于部署。例如，可能会需要一个出现在Web应用顶层的URL（如，http: //host/webAppPrefix/Anyname），并且在此URL中没有servlet项。位于顶层的URL简化了相对URL的使用。此外，对许多开发人员来说，顶层URL看上去比更长更麻烦的缺省URL更简短。<br />事实上，有时需要使用定制的URL。比如，你可能想关闭缺省URL映射，以便更好地强制实施安全限制或防止用户意外地访问无初始化参数的servlet。如果你禁止了缺省的URL，那么你怎样访问servlet呢？这时只有使用定制的URL了。<br />为了分配一个定制的URL，可使用servlet-mapping元素及其servlet-name和url-pattern子元素。Servlet- name元素提供了一个任意名称，可利用此名称引用相应的servlet；url-pattern描述了相对于Web应用的根目录的URL。url- pattern元素的值必须以斜杠（/）起始。<br />下面给出一个简单的web.xml摘录，它允许使用URL </font>
		<a href="http://host/webAppPrefix/UrlTest" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/UrlTest</font>
				</font>
		</a>
		<font size="2">而不是</font>
		<a href="http://host/webAppPrefix/servlet/Test" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/servlet/Test</font>
				</font>
		</a>
		<font size="2">或<br />http: //host/webAppPrefix/servlet/moreservlets.TestServlet。请注意，仍然需要XML头、 DOCTYPE声明以及web-app封闭元素。此外，可回忆一下，XML元素出现地次序不是随意的。特别是，需要把所有servlet元素放在所有 servlet-mapping元素之前。<br />&lt;servlet&gt;<br />&lt;servlet-name&gt;Test&lt;/servlet-name&gt;<br />&lt;servlet-class&gt;moreservlets.TestServlet&lt;/servlet-class&gt;<br />&lt;/servlet&gt;<br />&lt;!-- ... --&gt;<br />&lt;servlet-mapping&gt;<br />&lt;servlet-name&gt;Test&lt;/servlet-name&gt;<br />&lt;url-pattern&gt;/UrlTest&lt;/url-pattern&gt;<br />&lt;/servlet-mapping&gt;<br />URL模式还可以包含通配符。例如，下面的小程序指示服务器发送所有以Web应用的URL前缀开始，以..asp结束的请求到名为BashMS的servlet。<br />&lt;servlet&gt;<br />&lt;servlet-name&gt;BashMS&lt;/servlet-name&gt;<br />&lt;servlet-class&gt;msUtils.ASPTranslator&lt;/servlet-class&gt;<br />&lt;/servlet&gt;<br />&lt;!-- ... --&gt;<br />&lt;servlet-mapping&gt;<br />&lt;servlet-name&gt;BashMS&lt;/servlet-name&gt;<br />&lt;url-pattern&gt;/*.asp&lt;/url-pattern&gt;<br />&lt;/servlet-mapping&gt;<br />3.3 命名JSP页面<br />因为JSP页面要转换成sevlet，自然希望就像命名servlet一样命名JSP页面。毕竟，JSP页面可能会从初始化参数、安全设置或定制的URL中受益，正如普通的serlvet那样。虽然JSP页面的后台实际上是servlet这句话是正确的，但存在一个关键的猜疑：即，你不知道JSP页面的实际类名（因为系统自己挑选这个名字）。因此，为了命名JSP页面，可将jsp-file元素替换为servlet-calss元素，如下所示：<br />&lt;servlet&gt;<br />&lt;servlet-name&gt;Test&lt;/servlet-name&gt;<br />&lt;jsp-file&gt;/TestPage.jsp&lt;/jsp-file&gt;<br />&lt;/servlet&gt;<br />命名JSP页面的原因与命名servlet的原因完全相同：即为了提供一个与定制设置（如，初始化参数和安全设置）一起使用的名称，并且，以便能更改激活 JSP页面的URL（比方说，以便多个URL通过相同页面得以处理，或者从URL中去掉.jsp扩展名）。但是，在设置初始化参数时，应该注意，JSP页面是利用jspInit方法，而不是init方法读取初始化参数的。<br />例如，程序清单5-3给出一个名为TestPage.jsp的简单JSP页面，它的工作只是打印出用来激活它的URL的本地部分。TestPage.jsp放置在deployDemo应用的顶层。程序清单5-4给出了用来分配一个注册名PageName，然后将此注册名与</font>
		<a href="http://host/webAppPrefix/UrlTest2/anything" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/UrlTest2/anything</font>
				</font>
		</a>
		<font size="2">形式的URL相关联的web.xml文件（即，deployDemo/WEB-INF/web.xml）的一部分。<br /><br />程序清单5-3 TestPage.jsp<br />&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"&gt;<br />&lt;HTML&gt;<br />&lt;HEAD&gt;<br />&lt;TITLE&gt;<br />JSP Test Page<br />&lt;/TITLE&gt;<br />&lt;/HEAD&gt;<br />&lt;BODY BGCOLOR="#FDF5E6"&gt;<br />&lt;H2&gt;URI: &lt;%= request.getRequestURI() %&gt;&lt;/H2&gt;<br />&lt;/BODY&gt;<br />&lt;/HTML&gt;<br /><br />程序清单5-4 web.xml（说明JSP页命名的摘录）<br />&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br />&lt;!DOCTYPE web-app<br />PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br />"</font>
		<a href="http://java.sun.com/dtd/web-app_2_3.dtd" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://java.sun.com/dtd/web-app_2_3.dtd</font>
				</font>
		</a>
		<font size="2">"&gt;<br /><br />&lt;web-app&gt;<br />&lt;!-- ... --&gt;<br />&lt;servlet&gt;<br />&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;<br />&lt;jsp-file&gt;/TestPage.jsp&lt;/jsp-file&gt;<br />&lt;/servlet&gt;<br />&lt;!-- ... --&gt;<br />&lt;servlet-mapping&gt;<br />&lt;servlet-name&gt; PageName &lt;/servlet-name&gt;<br />&lt;url-pattern&gt;/UrlTest2/*&lt;/url-pattern&gt;<br />&lt;/servlet-mapping&gt;<br />&lt;!-- ... --&gt;<br />&lt;/web-app&gt;<br /><br />4 禁止激活器servlet<br />对servlet或JSP页面建立定制URL的一个原因是，这样做可以注册从 init（servlet）或jspInit（JSP页面）方法中读取得初始化参数。但是，初始化参数只在是利用定制URL模式或注册名访问 servlet或JSP页面时可以使用，用缺省URL </font>
		<a href="http://host/webAppPrefix/servlet/ServletName" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/servlet/ServletName</font>
				</font>
		</a>
		<font size="2">访问时不能使用。因此，你可能会希望关闭缺省URL，这样就不会有人意外地调用初始化servlet了。这个过程有时称为禁止激活器servlet，因为多数服务器具有一个用缺省的servlet URL注册的标准servlet，并激活缺省的URL应用的实际servlet。<br />有两种禁止此缺省URL的主要方法：<br />l 在每个Web应用中重新映射/servlet/模式。<br />l 全局关闭激活器servlet。<br />重要的是应该注意到，虽然重新映射每个Web应用中的/servlet/模式比彻底禁止激活servlet所做的工作更多，但重新映射可以用一种完全可移植的方式来完成。相反，全局禁止激活器servlet完全是针对具体机器的，事实上有的服务器（如ServletExec）没有这样的选择。下面的讨论对每个Web应用重新映射/servlet/ URL模式的策略。后面提供在Tomcat中全局禁止激活器servlet的详细内容。<br />4.1 重新映射/servlet/URL模式<br />在一个特定的Web应用中禁止以</font>
		<a href="http://host/webAppPrefix/servlet/" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/servlet/</font>
				</font>
		</a>
		<font size="2">开始的URL的处理非常简单。所需做的事情就是建立一个错误消息servlet，并使用前一节讨论的url-pattern元素将所有匹配请求转向该 servlet。只要简单地使用：<br />&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt;<br />作为servlet-mapping元素中的模式即可。<br />例如，程序清单5-5给出了将SorryServlet servlet（程序清单5-6）与所有以</font>
		<a href="http://host/webAppPrefix/servlet/" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/servlet/</font>
				</font>
		</a>
		<font size="2">开头的URL相关联的部署描述符文件的一部分。<br />程序清单5-5 web.xml（说明JSP页命名的摘录）<br />&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br />&lt;!DOCTYPE web-app<br />PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br />"</font>
		<a href="http://java.sun.com/dtd/web-app_2_3.dtd" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://java.sun.com/dtd/web-app_2_3.dtd</font>
				</font>
		</a>
		<font size="2">"&gt;<br /><br />&lt;web-app&gt;<br />&lt;!-- ... --&gt;<br />&lt;servlet&gt;<br />&lt;servlet-name&gt;Sorry&lt;/servlet-name&gt;<br />&lt;servlet-class&gt;moreservlets.SorryServlet&lt;/servlet-class&gt;<br />&lt;/servlet&gt;<br />&lt;!-- ... --&gt;<br />&lt;servlet-mapping&gt;<br />&lt;servlet-name&gt; Sorry &lt;/servlet-name&gt;<br />&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt;<br />&lt;/servlet-mapping&gt;<br />&lt;!-- ... --&gt;<br />&lt;/web-app&gt;<br /><br />程序清单5-6 SorryServlet.java<br />package moreservlets;<br /><br />import java.io.*;<br />import javax.servlet.*;<br />import javax.servlet.http.*;<br /><br />/** Simple servlet used to give error messages to<br />* users who try to access default servlet URLs<br />* (i.e., </font>
		<a href="http://host/webAppPrefix/servlet/ServletName" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/servlet/ServletName</font>
				</font>
		</a>
		<font size="2">)<br />* in Web applications that have disabled this<br />* behavior.<br />* &lt;P&gt;<br />* Taken from More Servlets and JavaServer Pages<br />* from Prentice Hall and Sun Microsystems Press,<br />* </font>
		<a href="http://www.moreservlets.com/." target="_blank">
				<font color="#1d58d1">
						<font size="2">http://www.moreservlets.com/.</font>
				</font>
		</a>
		<br />
		<font size="2">* © 2002 Marty Hall; may be freely used or adapted.<br />*/<br /><br />public class SorryServlet extends HttpServlet {<br />public void doGet(HttpServletRequest request,<br />HttpServletResponse response)<br />throws ServletException, IOException {<br />response.setContentType("text/html");<br />PrintWriter out = response.getWriter();<br />String title = "Invoker Servlet Disabled.";<br />out.println(ServletUtilities.headWithTitle(title) +<br />"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n" +<br />"&lt;H2&gt;" + title + "&lt;/H2&gt;\n" +<br />"Sorry, access to servlets by means of\n" +<br />"URLs that begin with\n" +<br />"</font>
		<a href="http://host/webAppPrefix/servlet/" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/servlet/</font>
				</font>
		</a>
		<font size="2">\n" +<br />"has been disabled.\n" + <br />"&lt;/BODY&gt;&lt;/HTML&gt;");<br />}<br /><br />public void doPost(HttpServletRequest request,<br />HttpServletResponse response)<br />throws ServletException, IOException {<br />doGet(request, response);<br />}<br />}<br /><br /><br />4.2 全局禁止激活器：Tomcat<br />Tomcat 4中用来关闭缺省URL的方法与Tomcat 3中所用的很不相同。下面介绍这两种方法：<br />1．禁止激活器： Tomcat 4<br />Tomcat 4用与前面相同的方法关闭激活器servlet，即利用web.xml中的url-mapping元素进行关闭。不同之处在于Tomcat使用了放在 install_dir/conf中的一个服务器专用的全局web.xml文件，而前面使用的是存放在每个Web应用的WEB-INF目录中的标准 web.xml文件。<br />因此，为了在Tomcat 4中关闭激活器servlet，只需在install_dir/conf/web.xml中简单地注释出/servlet/* URL映射项即可，如下所示：<br />&lt;!-- <br />&lt;servlet-mapping&gt;<br />&lt;servlet-name&gt;invoker&lt;/servlet-name&gt;<br />&lt;url-pattern&gt;/servlet/*&lt;/url-pattern&gt;<br />&lt;/servlet-mapping&gt;<br />--&gt;<br />再次提醒，应该注意这个项是位于存放在install_dir/conf的Tomcat专用的web.xml文件中的，此文件不是存放在每个Web应用的WEB-INF目录中的标准web.xml。<br />2．禁止激活器：Tomcat3<br />在Apache Tomcat的版本3中，通过在install_dir/conf/server.xml中注释出InvokerInterceptor项全局禁止缺省 servlet URL。例如，下面是禁止使用缺省servlet URL的server.xml文件的一部分。<br />&lt;!-- <br />&lt;RequsetInterceptor <br />className="org.apache.tomcat.request.InvokerInterceptor"<br />debug="0" prefix="/servlet/" /&gt;<br />--&gt;<br /><br />5 初始化和预装载servlet与JSP页面<br />这里讨论控制servlet和JSP页面的启动行为的方法。特别是，说明了怎样分配初始化参数以及怎样更改服务器生存期中装载servlet和JSP页面的时刻。<br />5.1 分配servlet初始化参数<br />利用init-param元素向servlet提供初始化参数，init-param元素具有param-name和param-value子元素。例如，在下面的例子中，如果initServlet servlet是利用它的注册名（InitTest）访问的，它将能够从其方法中调用getServletConfig(). getInitParameter("param1")获得"Value 1"，调用getServletConfig().getInitParameter("param2")获得"2"。<br />&lt;servlet&gt;<br />&lt;servlet-name&gt;InitTest&lt;/servlet-name&gt;<br />&lt;servlet-class&gt;moreservlets.InitServlet&lt;/servlet-class&gt;<br />&lt;init-param&gt;<br />&lt;param-name&gt;param1&lt;/param-name&gt;<br />&lt;param-value&gt;value1&lt;/param-value&gt;<br />&lt;/init-param&gt;<br />&lt;init-param&gt;<br />&lt;param-name&gt;param2&lt;/param-name&gt;<br />&lt;param-value&gt;2&lt;/param-value&gt;<br />&lt;/init-param&gt;<br />&lt;/servlet&gt;<br />在涉及初始化参数时，有几点需要注意：<br />l 返回值。GetInitParameter的返回值总是一个String。因此，在前一个例子中，可对param2使用Integer.parseInt获得一个int。<br />l JSP中的初始化。JSP页面使用jspInit而不是init。JSP页面还需要使用jsp-file元素代替servlet-class。<br />l 缺省URL。初始化参数只在通过它们的注册名或与它们注册名相关的定制URL模式访问Servlet时可以使用。因此，在这个例子中，param1和 param2初始化参数将能够在使用URL </font>
		<a href="http://host/webAppPrefix/servlet/InitTest" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/servlet/InitTest</font>
				</font>
		</a>
		<font size="2">时可用，但在使用URL </font>
		<a href="http://host/webAppPrefix/servlet/myPackage.InitServlet" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/servlet/myPackage.InitServlet</font>
				</font>
		</a>
		<font size="2">时不能使用。<br />例如，程序清单5-7给出一个名为InitServlet的简单servlet，它使用init方法设置firstName和emailAddress字段。程序清单5-8给出分配名称InitTest给servlet的web.xml文件。<br />程序清单5-7 InitServlet.java<br />package moreservlets;<br /><br />import java.io.*;<br />import javax.servlet.*;<br />import javax.servlet.http.*;<br /><br />/** Simple servlet used to illustrate servlet<br />* initialization parameters.<br />* &lt;P&gt;<br />* Taken from More Servlets and JavaServer Pages<br />* from Prentice Hall and Sun Microsystems Press,<br />* </font>
		<a href="http://www.moreservlets.com/." target="_blank">
				<font color="#1d58d1">
						<font size="2">http://www.moreservlets.com/.</font>
				</font>
		</a>
		<br />
		<font size="2">* © 2002 Marty Hall; may be freely used or adapted.<br />*/<br /><br />public class InitServlet extends HttpServlet {<br />private String firstName, emailAddress;<br /><br />public void init() {<br />ServletConfig config = getServletConfig();<br />firstName = config.getInitParameter("firstName");<br />emailAddress = config.getInitParameter("emailAddress");<br />}<br /><br />public void doGet(HttpServletRequest request,<br />HttpServletResponse response)<br />throws ServletException, IOException {<br />response.setContentType("text/html");<br />PrintWriter out = response.getWriter();<br />String uri = request.getRequestURI();<br />out.println(ServletUtilities.headWithTitle("Init Servlet") +<br />"&lt;BODY BGCOLOR=\"#FDF5E6\"&gt;\n" +<br />"&lt;H2&gt;Init Parameters:&lt;/H2&gt;\n" +<br />"&lt;UL&gt;\n" +<br />"&lt;LI&gt;First name: " + firstName + "\n" +<br />"&lt;LI&gt;Email address: " + emailAddress + "\n" +<br />"&lt;/UL&gt;\n" + <br />"&lt;/BODY&gt;&lt;/HTML&gt;");<br />}<br />}<br /><br /><br />程序清单5-8 web.xml（说明初始化参数的摘录）<br />&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br />&lt;!DOCTYPE web-app<br />PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br />"</font>
		<a href="http://java.sun.com/dtd/web-app_2_3.dtd" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://java.sun.com/dtd/web-app_2_3.dtd</font>
				</font>
		</a>
		<font size="2">"&gt;<br /><br />&lt;web-app&gt;<br />&lt;!-- ... --&gt;<br />&lt;servlet&gt;<br />&lt;servlet-name&gt;InitTest&lt;/servlet-name&gt;<br />&lt;servlet-class&gt;moreservlets.InitServlet&lt;/servlet-class&gt;<br />&lt;init-param&gt;<br />&lt;param-name&gt;firstName&lt;/param-name&gt;<br />&lt;param-value&gt;Larry&lt;/param-value&gt;<br />&lt;/init-param&gt;<br />&lt;init-param&gt;<br />&lt;param-name&gt;emailAddress&lt;/param-name&gt;<br />&lt;param-value&gt;</font>
		<a href="mailto:Ellison@Microsoft.com">
				<font color="#1d58d1">
						<font size="2">Ellison@Microsoft.com</font>
				</font>
		</a>
		<font size="2">&lt;/param-value&gt;<br />&lt;/init-param&gt;<br />&lt;/servlet&gt;<br />&lt;!-- ... --&gt;<br />&lt;/web-app&gt;<br /><br />5.2 分配JSP初始化参数<br />给JSP页面提供初始化参数在三个方面不同于给servlet提供初始化参数。<br />1）使用jsp-file而不是servlet-class。因此，WEB-INF/web.xml文件的servlet元素如下所示：<br />&lt;servlet&gt;<br />&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;<br />&lt;jsp-file&gt;/RealPage.jsp&lt;/jsp-file&gt;<br />&lt;init-param&gt;<br />&lt;param-name&gt;...&lt;/param-name&gt;<br />&lt;param-value&gt;...&lt;/param-value&gt;<br />&lt;/init-param&gt;<br />...<br />&lt;/servlet&gt;<br />2) 几乎总是分配一个明确的URL模式。对servlet，一般相应地使用以</font>
		<a href="http://host/webAppPrefix/servlet/" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/servlet/</font>
				</font>
		</a>
		<font size="2">开始的缺省URL。只需记住，使用注册名而不是原名称即可。这对于JSP页面在技术上也是合法的。例如，在上面给出的例子中，可用URL </font>
		<a href="http://host/webAppPrefix/servlet/PageName" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/webAppPrefix/servlet/PageName</font>
				</font>
		</a>
		<font size="2">访问RealPage.jsp的对初始化参数具有访问权的版本。但在用于JSP页面时，许多用户似乎不喜欢应用常规的servlet的URL。此外，如果 JSP页面位于服务器为其提供了目录清单的目录中（如，一个既没有index.html也没有index.jsp文件的目录），则用户可能会连接到此 JSP页面，单击它，从而意外地激活未初始化的页面。因此，好的办法是使用url-pattern（5.3节）将JSP页面的原URL与注册的 servlet名相关联。这样，客户机可使用JSP页面的普通名称，但仍然激活定制的版本。例如，给定来自项目1的servlet定义，可使用下面的 servlet-mapping定义：<br />&lt;servlet-mapping&gt;<br />&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;<br />&lt;url-pattern&gt;/RealPage.jsp&lt;/url-pattern&gt;<br />&lt;/servlet-mapping&gt;<br />3）JSP页使用jspInit而不是init。自动从JSP页面建立的servlet或许已经使用了inti方法。因此，使用JSP声明提供一个init方法是不合法的，必须制定jspInit方法。<br />为了说明初始化JSP页面的过程，程序清单5-9给出了一个名为InitPage.jsp的JSP页面，它包含一个jspInit方法且放置于 deployDemo Web应用层次结构的顶层。一般，</font>
		<a href="http://host/deployDemo/InitPage.jsp" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://host/deployDemo/InitPage.jsp</font>
				</font>
		</a>
		<font size="2">形式的URL将激活此页面的不具有初始化参数访问权的版本，从而将对firstName和emailAddress变量显示null。但是， web.xml文件（程序清单5-10）分配了一个注册名，然后将该注册名与URL模式/InitPage.jsp相关联。<br /><br />程序清单5-9 InitPage.jsp<br />&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"&gt;<br />&lt;HTML&gt;<br />&lt;HEAD&gt;&lt;TITLE&gt;JSP Init Test&lt;/TITLE&gt;&lt;/HEAD&gt;<br />&lt;BODY BGCOLOR="#FDF5E6"&gt;<br />&lt;H2&gt;Init Parameters:&lt;/H2&gt;<br />&lt;UL&gt;<br />&lt;LI&gt;First name: &lt;%= firstName %&gt;<br />&lt;LI&gt;Email address: &lt;%= emailAddress %&gt;<br />&lt;/UL&gt;<br />&lt;/BODY&gt;&lt;/HTML&gt;<br />&lt;%!<br />private String firstName, emailAddress;<br /><br />public void jspInit() {<br />ServletConfig config = getServletConfig();<br />firstName = config.getInitParameter("firstName");<br />emailAddress = config.getInitParameter("emailAddress");<br />}<br />%&gt;<br /><br /><br />程序清单5-10 web.xml（说明JSP页面的init参数的摘录）<br />&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br />&lt;!DOCTYPE web-app<br />PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br />"</font>
		<a href="http://java.sun.com/dtd/web-app_2_3.dtd" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://java.sun.com/dtd/web-app_2_3.dtd</font>
				</font>
		</a>
		<font size="2">"&gt;<br /><br />&lt;web-app&gt;<br />&lt;!-- ... --&gt;<br />&lt;servlet&gt;<br />&lt;servlet-name&gt;InitPage&lt;/servlet-name&gt;<br />&lt;jsp-file&gt;/InitPage.jsp&lt;/jsp-file&gt;<br />&lt;init-param&gt;<br />&lt;param-name&gt;firstName&lt;/param-name&gt;<br />&lt;param-value&gt;Bill&lt;/param-value&gt;<br />&lt;/init-param&gt;<br />&lt;init-param&gt;<br />&lt;param-name&gt;emailAddress&lt;/param-name&gt;<br />&lt;param-value&gt;</font>
		<a href="mailto:gates@oracle.com">
				<font color="#1d58d1">
						<font size="2">gates@oracle.com</font>
				</font>
		</a>
		<font size="2">&lt;/param-value&gt;<br />&lt;/init-param&gt;<br />&lt;/servlet&gt;<br />&lt;!-- ... --&gt; <br />&lt;servlet-mapping&gt;<br />&lt;servlet-name&gt; InitPage&lt;/servlet-name&gt;<br />&lt;url-pattern&gt;/InitPage.jsp&lt;/url-pattern&gt;<br />&lt;/servlet-mapping&gt;<br />&lt;!-- ... --&gt;<br />&lt;/web-app&gt;<br /><br /><br />5.3 提供应用范围内的初始化参数<br />一般，对单个地servlet或JSP页面分配初始化参数。指定的servlet或JSP页面利用ServletConfig的getInitParameter方法读取这些参数。但是，在某些情形下，希望提供可由任意servlet或JSP页面借助ServletContext的getInitParameter方法读取的系统范围内的初始化参数。<br />可利用context-param元素声明这些系统范围内的初始化值。context-param元素应该包含param-name、param-value以及可选的description子元素，如下所示：<br />&lt;context-param&gt;<br />&lt;param-name&gt;support-email&lt;/param-name&gt;<br />&lt;param-value&gt;</font>
		<a href="mailto:blackhole@mycompany.com">
				<font color="#1d58d1">
						<font size="2">blackhole@mycompany.com</font>
				</font>
		</a>
		<font size="2">&lt;/param-value&gt;<br />&lt;/context-param&gt;<br />可回忆一下，为了保证可移植性，web.xml内的元素必须以正确的次序声明。但这里应该注意，context-param元素必须出现任意与文档有关的元素（icon、display-name或description）之后及filter、filter-mapping、listener或 servlet元素之前。<br />5.4 在服务器启动时装载servlet<br />假如servlet或JSP页面有一个要花很长时间执行的init （servlet）或jspInit（JSP）方法。例如，假如init或jspInit方法从某个数据库或ResourceBundle查找产量。这种情况下，在第一个客户机请求时装载servlet的缺省行为将对第一个客户机产生较长时间的延迟。因此，可利用servlet的load-on- startup元素规定服务器在第一次启动时装载servlet。下面是一个例子。<br />&lt;servlet&gt;<br />&lt;servlet-name&gt; … &lt;/servlet-name&gt;<br />&lt;servlet-class&gt; … &lt;/servlet-class&gt; &lt;!-- Or jsp-file --&gt;<br />&lt;load-on-startup/&gt;<br />&lt;/servlet&gt;<br />可以为此元素体提供一个整数而不是使用一个空的load-on-startup。想法是服务器应该在装载较大数目的servlet或JSP页面之前装载较少数目的servlet或JSP页面。例如，下面的servlet项（放置在Web应用的WEB-INF目录下的web.xml文件中的web-app元素内）将指示服务器首先装载和初始化SearchServlet，然后装载和初始化由位于Web应用的result目录中的index.jsp文件产生的 servlet。<br />&lt;servlet&gt;<br />&lt;servlet-name&gt;Search&lt;/servlet-name&gt;<br />&lt;servlet-class&gt;myPackage.SearchServlet&lt;/servlet-class&gt; &lt;!-- Or jsp-file --&gt;<br />&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;<br />&lt;/servlet&gt;<br />&lt;servlet&gt;<br />&lt;servlet-name&gt;Results&lt;/servlet-name&gt;<br />&lt;servlet-class&gt;/results/index.jsp&lt;/servlet-class&gt; &lt;!-- Or jsp-file --&gt;<br />&lt;load-on-startup&gt;2&lt;/load-on-startup&gt;<br />&lt;/servlet&gt;<br /><br />6 声明过滤器<br /><br />servlet版本2.3引入了过滤器的概念。虽然所有支持servlet API版本2.3的服务器都支持过滤器，但为了使用与过滤器有关的元素，必须在web.xml中使用版本2.3的DTD。<br />过滤器可截取和修改进入一个servlet或JSP页面的请求或从一个servlet或JSP页面发出的相应。在执行一个servlet或JSP页面之前，必须执行第一个相关的过滤器的doFilter方法。在该过滤器对其FilterChain对象调用doFilter时，执行链中的下一个过滤器。如果没有其他过滤器，servlet或JSP页面被执行。过滤器具有对到来的ServletRequest对象的全部访问权，因此，它们可以查看客户机名、查找到来的cookie等。为了访问servlet或JSP页面的输出，过滤器可将响应对象包裹在一个替身对象（stand-in object）中，比方说把输出累加到一个缓冲区。在调用FilterChain对象的doFilter方法之后，过滤器可检查缓冲区，如有必要，就对它进行修改，然后传送到客户机。<br />例如，程序清单5-11帝国难以了一个简单的过滤器，只要访问相关的servlet或JSP页面，它就截取请求并在标准输出上打印一个报告（开发过程中在桌面系统上运行时，大多数服务器都可以使用这个过滤器）。<br /><br />程序清单5-11 ReportFilter.java<br />package moreservlets;<br /><br />import java.io.*;<br />import javax.servlet.*;<br />import javax.servlet.http.*;<br />import java.util.*;<br /><br />/** Simple filter that prints a report on the standard output <br />* whenever the associated servlet or JSP page is accessed.<br />* &lt;P&gt;<br />* Taken from More Servlets and JavaServer Pages<br />* from Prentice Hall and Sun Microsystems Press,<br />* </font>
		<a href="http://www.moreservlets.com/." target="_blank">
				<font color="#1d58d1">
						<font size="2">http://www.moreservlets.com/.</font>
				</font>
		</a>
		<br />
		<font size="2">* © 2002 Marty Hall; may be freely used or adapted.<br />*/<br /><br />public class ReportFilter implements Filter {<br />public void doFilter(ServletRequest request,<br />ServletResponse response,<br />FilterChain chain)<br />throws ServletException, IOException {<br />HttpServletRequest req = (HttpServletRequest)request;<br />System.out.println(req.getRemoteHost() +<br />" tried to access " +<br />req.getRequestURL() +<br />" on " + new Date() + ".");<br />chain.doFilter(request,response);<br />}<br /><br />public void init(FilterConfig config)<br />throws ServletException {<br />}<br /><br />public void destroy() {}<br />}<br /><br />一旦建立了一个过滤器，可以在web.xml中利用filter元素以及filter-name（任意名称）、file-class（完全限定的类名）和（可选的）init-params子元素声明它。请注意，元素在web.xml的web-app元素中出现的次序不是任意的；允许服务器（但不是必需的）强制所需的次序，并且实际中有些服务器也是这样做的。但这里要注意，所有filter元素必须出现在任意filter-mapping元素之前， filter-mapping元素又必须出现在所有servlet或servlet-mapping元素之前。<br />例如，给定上述的ReportFilter类，可在web.xml中作出下面的filter声明。它把名称Reporter与实际的类ReportFilter（位于moreservlets程序包中）相关联。<br />&lt;filter&gt;<br />&lt;filter-name&gt;Reporter&lt;/filter-name&gt;<br />&lt;filter-class&gt;moresevlets.ReportFilter&lt;/filter-class&gt;<br />&lt;/filter&gt;<br />一旦命名了一个过滤器，可利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。关于此项工作有两种选择。<br />首先，可使用filter-name和servlet-name子元素把此过滤器与一个特定的servlet名（此servlet名必须稍后在相同的 web.xml文件中使用servlet元素声明）关联。例如，下面的程序片断指示系统只要利用一个定制的URL访问名为SomeServletName 的servlet或JSP页面，就运行名为Reporter的过滤器。<br />&lt;filter-mapping&gt;<br />&lt;filter-name&gt;Reporter&lt;/filter-name&gt;<br />&lt;servlet-name&gt;SomeServletName&lt;/servlet-name&gt;<br />&lt;/filter-mapping&gt;<br />其次，可利用filter-name和url-pattern子元素将过滤器与一组servlet、JSP页面或静态内容相关联。例如，相面的程序片段指示系统只要访问Web应用中的任意URL，就运行名为Reporter的过滤器。<br />&lt;filter-mapping&gt;<br />&lt;filter-name&gt;Reporter&lt;/filter-name&gt;<br />&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br />&lt;/filter-mapping&gt;<br />例如，程序清单5-12给出了将ReportFilter过滤器与名为PageName的servlet相关联的web.xml文件的一部分。名字 PageName依次又与一个名为TestPage.jsp的JSP页面以及以模式http: //host/webAppPrefix/UrlTest2/ 开头的URL相关联。TestPage.jsp的源代码已经JSP页面命名的谈论在前面的3节"分配名称和定制的URL"中给出。事实上，程序清单5- 12中的servlet和servlet-name项从该节原封不动地拿过来的。给定这些web.xml项，可看到下面的标准输出形式的调试报告（换行是为了容易阅读）。<br />audit.irs.gov tried to access <br /></font>
		<a href="http://mycompany.com/deployDemo/UrlTest2/business/tax-plan.html" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://mycompany.com/deployDemo/UrlTest2/business/tax-plan.html</font>
				</font>
		</a>
		<br />
		<font size="2">on Tue Dec 25 13:12:29 EDT 2001.<br /><br />程序清单5-12 Web.xml（说明filter用法的摘录）<br />&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br />&lt;!DOCTYPE web-app<br />PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br />"</font>
		<a href="http://java.sun.com/dtd/web-app_2_3.dtd" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://java.sun.com/dtd/web-app_2_3.dtd</font>
				</font>
		</a>
		<font size="2">"&gt;<br /><br />&lt;web-app&gt;<br />&lt;filter&gt;<br />&lt;filter-name&gt;Reporter&lt;/filter-name&gt;<br />&lt;filter-class&gt;moresevlets.ReportFilter&lt;/filter-class&gt;<br />&lt;/filter&gt;<br />&lt;!-- ... --&gt;<br />&lt;filter-mapping&gt;<br />&lt;filter-name&gt;Reporter&lt;/filter-name&gt;<br />&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;<br />&lt;/filter-mapping&gt;<br />&lt;!-- ... --&gt;<br />&lt;servlet&gt;<br />&lt;servlet-name&gt;PageName&lt;/servlet-name&gt;<br />&lt;jsp-file&gt;/RealPage.jsp&lt;/jsp-file&gt;<br />&lt;/servlet&gt;<br />&lt;!-- ... --&gt;<br />&lt;servlet-mapping&gt;<br />&lt;servlet-name&gt; PageName &lt;/servlet-name&gt;<br />&lt;url-pattern&gt;/UrlTest2/*&lt;/url-pattern&gt;<br />&lt;/servlet-mapping&gt;<br />&lt;!-- ... --&gt;<br />&lt;/web-app&gt;<br /><br /><br />7 指定欢迎页<br /><br />假如用户提供了一个像http: //host/webAppPrefix/directoryName/ 这样的包含一个目录名但没有包含文件名的URL，会发生什么事情呢？用户能得到一个目录表？一个错误？还是标准文件的内容？如果得到标准文件内容，是 index.html、index.jsp、default.html、default.htm或别的什么东西呢？<br />Welcome-file-list 元素及其辅助的welcome-file元素解决了这个模糊的问题。例如，下面的web.xml项指出，如果一个URL给出一个目录名但未给出文件名，服务器应该首先试用index.jsp，然后再试用index.html。如果两者都没有找到，则结果有赖于所用的服务器（如一个目录列表）。<br />&lt;welcome-file-list&gt;<br />&lt;welcome-file&gt;index.jsp&lt;/welcome-file&gt;<br />&lt;welcome-file&gt;index.html&lt;/welcome-file&gt;<br />&lt;/welcome-file-list&gt;<br />虽然许多服务器缺省遵循这种行为，但不一定必须这样。因此，明确地使用welcom-file-list保证可移植性是一种良好的习惯。<br /><br />8 指定处理错误的页面<br /><br />现在我了解到，你在开发servlet和JSP页面时从不会犯错误，而且你的所有页面是那样的清晰，一般的程序员都不会被它们的搞糊涂。但是，是人总会犯错误的，用户可能会提供不合规定的参数，使用不正确的URL或者不能提供必需的表单字段值。除此之外，其它开发人员可能不那么细心，他们应该有些工具来克服自己的不足。<br />error-page元素就是用来克服这些问题的。它有两个可能的子元素，分别是：error-code和exception- type。第一个子元素error-code指出在给定的HTTP错误代码出现时使用的URL。第二个子元素excpetion-type指出在出现某个给定的Java异常但未捕捉到时使用的URL。error-code和exception-type都利用location元素指出相应的URL。此 URL必须以/开始。location所指出的位置处的页面可通过查找HttpServletRequest对象的两个专门的属性来访问关于错误的信息，这两个属性分别是：javax.servlet.error.status_code和javax.servlet.error.message。<br />可回忆一下，在web.xml内以正确的次序声明web-app的子元素很重要。这里只要记住，error-page出现在web.xml文件的末尾附近，servlet、servlet-name和welcome-file-list之后即可。<br /><br />8.1 error-code元素<br />为了更好地了解error-code元素的值，可考虑一下如果不正确地输入文件名，大多数站点会作出什么反映。这样做一般会出现一个404错误信息，它表示不能找到该文件，但几乎没提供更多有用的信息。另一方面，可以试一下在</font>
		<a href="http://www.microsoft.com/" target="_blank">
				<font color="#1d58d1">
						<font size="2">www.microsoft.com</font>
				</font>
		</a>
		<font size="2">、</font>
		<a href="http://www.ibm.com/" target="_blank">
				<font color="#1d58d1">
						<font size="2">www.ibm.com</font>
				</font>
		</a>
		<font size="2">处或者特别是在</font>
		<a href="http://www.bea.com/" target="_blank">
				<font color="#1d58d1">
						<font size="2">www.bea.com</font>
				</font>
		</a>
		<font size="2">处输出未知的文件名。这是会得出有用的消息，这些消息提供可选择的位置，以便查找感兴趣的页面。提供这样有用的错误页面对于Web应用来说是很有价值得。事实上rm-error-page子元素）。由form-login-page给出的HTML表单必须具有一个j_security_check的 ACTION属性、一个名为j_username的用户名文本字段以及一个名为j_password的口令字段。<br />例如，程序清单5-19指示服务器使用基于表单的验证。Web应用的顶层目录中的一个名为login.jsp的页面将收集用户名和口令，并且失败的登陆将由相同目录中名为login-error.jsp的页面报告。<br /><br />程序清单5-19 web.xml（说明login-config的摘录）<br />&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br />&lt;!DOCTYPE web-app<br />PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br />"</font>
		<a href="http://java.sun.com/dtd/web-app_2_3.dtd" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://java.sun.com/dtd/web-app_2_3.dtd</font>
				</font>
		</a>
		<font size="2">"&gt;<br /><br />&lt;web-app&gt;<br />&lt;!-- ... --&gt;<br />&lt;security-constraint&gt; ... &lt;/security-constraint&gt;<br />&lt;login-config&gt;<br />&lt;auth-method&gt; FORM &lt;/auth-method&gt;<br />&lt;form-login-config&gt;<br />&lt;form-login-page&gt;/login.jsp&lt;/form-login-page&gt;<br />&lt;form-error-page&gt;/login-error.jsp&lt;/form-error-page&gt;<br />&lt;/form-login-config&gt;<br />&lt;/login-config&gt;<br />&lt;!-- ... --&gt;<br />&lt;/web-app&gt;<br /><br /><br />9.2 限制对Web资源的访问<br />现在，可以指示服务器使用何种验证方法了。"了不起，"你说道，"除非我能指定一个来收到保护的 URL，否则没有多大用处。"没错。指出这些URL并说明他们应该得到何种保护正是security-constriaint元素的用途。此元素在 web.xml中应该出现在login-config的紧前面。它包含是个可能的子元素，分别是：web-resource-collection、 auth-constraint、user-data-constraint和display-name。下面各小节对它们进行介绍。<br />l web-resource-collection<br />此元素确定应该保护的资源。所有security-constraint元素都必须包含至少一个web-resource-collection项。此元素由一个给出任意标识名称的web-resource-name元素、一个确定应该保护的URL的url-pattern元素、一个指出此保护所适用的 HTTP命令（GET、POST等，缺省为所有方法）的http-method元素和一个提供资料的可选description元素组成。例如，下面的 Web-resource-collection项（在security-constratint元素内）指出Web应用的proprietary目录中所有文档应该受到保护。<br />&lt;security-constraint&gt;<br />&lt;web-resource-coolection&gt;<br />&lt;web-resource-name&gt;Proprietary&lt;/web-resource-name&gt;<br />&lt;url-pattern&gt;/propritary/*&lt;/url-pattern&gt;<br />&lt;/web-resource-coolection&gt;<br />&lt;!-- ... --&gt;<br />&lt;/security-constraint&gt;<br />重要的是应该注意到，url-pattern仅适用于直接访问这些资源的客户机。特别是，它不适合于通过MVC体系结构利用 RequestDispatcher来访问的页面，或者不适合于利用类似jsp:forward的手段来访问的页面。这种不匀称如果利用得当的话很有好处。例如，servlet可利用MVC体系结构查找数据，把它放到bean中，发送请求到从bean中提取数据的JSP页面并显示它。我们希望保证决不直接访问受保护的JSP页面，而只是通过建立该页面将使用的bean的servlet来访问它。url-pattern和auth-contraint元素可通过声明不允许任何用户直接访问JSP页面来提供这种保证。但是，这种不匀称的行为可能让开发人员放松警惕，使他们偶然对应受保护的资源提供不受限制的访问。 <br />l auth-constraint<br />尽管web-resource-collention元素质出了哪些URL应该受到保护，但是auth-constraint元素却指出哪些用户应该具有受保护资源的访问权。此元素应该包含一个或多个标识具有访问权限的用户类别role- name元素，以及包含（可选）一个描述角色的description元素。例如，下面web.xml中的security-constraint元素部门规定只有指定为Administrator或Big Kahuna（或两者）的用户具有指定资源的访问权。<br />&lt;security-constraint&gt;<br />&lt;web-resource-coolection&gt; ... &lt;/web-resource-coolection&gt;<br />&lt;auth-constraint&gt;<br />&lt;role-name&gt;administrator&lt;/role-name&gt;<br />&lt;role-name&gt;kahuna&lt;/role-name&gt;<br />&lt;/auth-constraint&gt;<br />&lt;/security-constraint&gt;<br />重要的是认识到，到此为止，这个过程的可移植部分结束了。服务器怎样确定哪些用户处于任何角色以及它怎样存放用户的口令，完全有赖于具体的系统。<br />例如，Tomcat使用install_dir/conf/tomcat-users.xml将用户名与角色名和口令相关联，正如下面例子中所示，它指出用户joe（口令bigshot）和jane（口令enaj）属于administrator和kahuna角色。<br />&lt;tomcat-users&gt;<br />&lt;user name="joe" password="bigshot" roles="administrator,kahuna" /&gt;<br />&lt;user name="jane" password="enaj" roles="kahuna" /&gt;<br />&lt;/tomcat-users&gt;<br />l user-data-constraint<br />这个可选的元素指出在访问相关资源时使用任何传输层保护。它必须包含一个transport-guarantee子元素（合法值为NONE、 INTEGRAL或CONFIDENTIAL），并且可选地包含一个description元素。transport-guarantee为NONE值将对所用的通讯协议不加限制。INTEGRAL值表示数据必须以一种防止截取它的人阅读它的方式传送。虽然原理上（并且在未来的HTTP版本中），在 INTEGRAL和CONFIDENTIAL之间可能会有差别，但在当前实践中，他们都只是简单地要求用SSL。例如，下面指示服务器只允许对相关资源做 HTTPS连接：<br />&lt;security-constraint&gt;<br />&lt;!-- ... --&gt;<br />&lt;user-data-constraint&gt;<br />&lt;transport-guarantee&gt;CONFIDENTIAL&lt;/transport-guarantee&gt;<br />&lt;/user-data-constraint&gt;<br />&lt;/security-constraint&gt;<br />l display-name<br />security-constraint的这个很少使用的子元素给予可能由GUI工具使用的安全约束项一个名称。<br />9.3 分配角色名<br />迄今为止，讨论已经集中到完全由容器（服务器）处理的安全问题之上了。但servlet以及JSP页面也能够处理它们自己的安全问题。<br />例如，容器可能允许用户从bigwig或bigcheese角色访问一个显示主管人员额外紧贴的页面，但只允许bigwig用户修改此页面的参数。完成这种更细致的控制的一种常见方法是调用HttpServletRequset的isUserInRole方法，并据此修改访问。<br />Servlet的 security-role-ref子元素提供出现在服务器专用口令文件中的安全角色名的一个别名。例如，假如编写了一个调用 request.isUserInRole（"boss"）的servlet，但后来该servlet被用在了一个其口令文件调用角色manager而不是boss的服务器中。下面的程序段使该servlet能够使用这两个名称中的任何一个。<br />&lt;servlet&gt;<br />&lt;!-- ... --&gt;<br />&lt;security-role-ref&gt;<br />&lt;role-name&gt;boss&lt;/role-name&gt; &lt;!-- New alias --&gt;<br />&lt;role-link&gt;manager&lt;/role-link&gt; &lt;!-- Real name --&gt;<br />&lt;/security-role-ref&gt;<br />&lt;/servlet&gt;<br />也可以在web-app内利用security-role元素提供将出现在role-name元素中的所有安全角色的一个全局列表。分别地生命角色使高级IDE容易处理安全信息。<br /><br />10 控制会话超时<br /><br />如果某个会话在一定的时间内未被访问，服务器可把它扔掉以节约内存。可利用HttpSession的setMaxInactiveInterval方法直接设置个别会话对象的超时值。如果不采用这种方法，则缺省的超时值由具体的服务器决定。但可利用session-config和session- timeout元素来给出一个适用于所有服务器的明确的超时值。超时值的单位为分钟，因此，下面的例子设置缺省会话超时值为三个小时（180分钟）。<br />&lt;session-config&gt;<br />&lt;session-timeout&gt;180&lt;/session-timeout&gt;<br />&lt;/session-config&gt;<br /><br />11 Web应用的文档化<br /><br />越来越多的开发环境开始提供servlet和JSP的直接支持。例子有Borland Jbuilder Enterprise Edition、Macromedia UltraDev、Allaire JRun Studio（写此文时，已被Macromedia收购）以及IBM VisuaAge for Java等。<br />大量的web.xml元素不仅是为服务器设计的，而且还是为可视开发环境设计的。它们包括icon、display-name和discription等。<br />可回忆一下，在web.xml内以适当地次序声明web-app子元素很重要。不过，这里只要记住icon、display-name和description是web.xml的web-app元素内的前三个合法元素即可。<br />l icon<br />icon元素指出GUI工具可用来代表Web应用的一个和两个图像文件。可利用small-icon元素指定一幅16 x 16的GIF或JPEG图像，用large-icon元素指定一幅32 x 32的图像。下面举一个例子： <br />&lt;icon&gt;<br />&lt;small-icon&gt;/images/small-book.gif&lt;/small-icon&gt;<br />&lt;large-icon&gt;/images/tome.jpg&lt;/large-icon&gt;<br />&lt;/icon&gt;<br />l display-name<br />display-name元素提供GUI工具可能会用来标记此Web应用的一个名称。下面是个例子。<br />&lt;display-name&gt;Rare Books&lt;/display-name&gt;<br />l description<br />description元素提供解释性文本，如下所示：<br />&lt;description&gt;<br />This Web application represents the store developed for<br />rare-books.com, an online bookstore specializing in rare<br />and limited-edition books.<br />&lt;/description&gt;<br /><br />12 关联文件与MIME类型<br /><br />服务器一般都具有一种让Web站点管理员将文件扩展名与媒体相关联的方法。例如，将会自动给予名为mom.jpg的文件一个image/jpeg的MIME 类型。但是，假如你的Web应用具有几个不寻常的文件，你希望保证它们在发送到客户机时分配为某种MIME类型。mime-mapping元素（具有 extension和mime-type子元素）可提供这种保证。例如，下面的代码指示服务器将application/x-fubar的MIME类型分配给所有以.foo结尾的文件。<br />&lt;mime-mapping&gt;<br />&lt;extension&gt;foo&lt;/extension&gt;<br />&lt;mime-type&gt;application/x-fubar&lt;/mime-type&gt;<br />&lt;/mime-mapping&gt;<br />或许，你的Web应用希望重载（override）标准的映射。例如，下面的代码将告诉服务器在发送到客户机时指定.ps文件作为纯文本（text/plain）而不是作为PostScript（application/postscript）。<br />&lt;mime-mapping&gt;<br />&lt;extension&gt;ps&lt;/extension&gt;<br />&lt;mime-type&gt;application/postscript&lt;/mime-type&gt;<br />&lt;/mime-mapping&gt;<br /><br /><br />13 定位TLD<br /><br />JSP taglib元素具有一个必要的uri属性，它给出一个TLD（Tag Library Descriptor）文件相对于Web应用的根的位置。TLD文件的实际名称在发布新的标签库版本时可能会改变，但我们希望避免更改所有现有JSP页面。此外，可能还希望使用保持taglib元素的简练性的一个简短的uri。这就是部署描述符文件的taglib元素派用场的所在了。Taglib包含两个子元素：taglib-uri和taglib-location。taglib-uri元素应该与用于JSP taglib元素的uri属性的东西相匹配。Taglib-location元素给出TLD文件的实际位置。例如，假如你将文件chart-tags- 1.3beta.tld放在WebApp/WEB-INF/tlds中。现在，假如web.xml在web-app元素内包含下列内容。<br />&lt;taglib&gt;<br />&lt;taglib-uri&gt;/charts.tld&lt;/taglib-uri&gt;<br />&lt;taglib-location&gt;<br />/WEB-INF/tlds/chart-tags-1.3beta.tld<br />&lt;/taglib-location&gt;<br />&lt;/taglib&gt;<br />给出这个说明后，JSP页面可通过下面的简化形式使用标签库。<br />&lt;%@ taglib uri="/charts.tld" prefix="somePrefix" %&gt;<br /><br />14 指定应用事件监听程序<br /><br />应用事件监听器程序是建立或修改servlet环境或会话对象时通知的类。它们是servlet规范的版本2.3中的新内容。这里只简单地说明用来向Web应用注册一个监听程序的web.xml的用法。<br />注册一个监听程序涉及在web.xml的web-app元素内放置一个listener元素。在listener元素内，listener-class元素列出监听程序的完整的限定类名，如下所示：<br />&lt;listener&gt;<br />&lt;listener-class&gt;package.ListenerClass&lt;/listener-class&gt;<br />&lt;/listener&gt;<br />虽然listener元素的结构很简单，但请不要忘记，必须正确地给出web-app元素内的子元素的次序。listener元素位于所有的servlet 元素之前以及所有filter-mapping元素之后。此外，因为应用生存期监听程序是serlvet规范的2.3版本中的新内容，所以必须使用 web.xml DTD的2.3版本，而不是2.2版本。<br />例如，程序清单5-20给出一个名为ContextReporter的简单的监听程序，只要Web应用的Servlet-Context建立（如装载Web应用）或消除（如服务器关闭）时，它就在标准输出上显示一条消息。程序清单5-21给出此监听程序注册所需要的web.xml文件的一部分。<br /><br />程序清单5-20 ContextReporterjava<br />package moreservlets;<br /><br />import javax.servlet.*;<br />import java.util.*;<br /><br />/** Simple listener that prints a report on the standard output <br />* when the ServletContext is created or destroyed.<br />* &lt;P&gt;<br />* Taken from More Servlets and JavaServer Pages<br />* from Prentice Hall and Sun Microsystems Press,<br />* </font>
		<a href="http://www.moreservlets.com/." target="_blank">
				<font color="#1d58d1">
						<font size="2">http://www.moreservlets.com/.</font>
				</font>
		</a>
		<br />
		<font size="2">* © 2002 Marty Hall; may be freely used or adapted.<br />*/<br /><br />public class ContextReporter implements ServletContextListener {<br />public void contextInitialized(ServletContextEvent event) {<br />System.out.println("Context created on " +<br />new Date() + ".");<br />}<br /><br />public void contextDestroyed(ServletContextEvent event) {<br />System.out.println("Context destroyed on " +<br />new Date() + ".");<br />}<br />}<br /><br />程序清单5-21 web.xml（声明一个监听程序的摘录）<br />&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br />&lt;!DOCTYPE web-app<br />PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"<br />"</font>
		<a href="http://java.sun.com/dtd/web-app_2_3.dtd" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://java.sun.com/dtd/web-app_2_3.dtd</font>
				</font>
		</a>
		<font size="2">"&gt;<br /><br />&lt;web-app&gt;<br />&lt;!-- ... --&gt;<br />&lt;filter-mapping&gt; … &lt;/filter-mapping&gt;<br />&lt;listener&gt;<br />&lt;listener-class&gt;package.ListenerClass&lt;/listener-class&gt;<br />&lt;/listener&gt;<br />&lt;servlet&gt; ... &lt;/servlet&gt;<br />&lt;!-- ... --&gt;<br />&lt;/web-app&gt;<br /><br />15 J2EE元素<br /><br />本节描述用作J2EE环境组成部分的Web应用的web.xml元素。这里将提供一个简明的介绍，详细内容可以参阅</font>
		<a href="http://java.sun.com/j2ee/j2ee-1_3-fr-spec.pdf" target="_blank">
				<font color="#1d58d1">
						<font size="2">http://java.sun.com/j2ee/j2ee-1_3-fr-spec.pdf</font>
				</font>
		</a>
		<font size="2">的Java 2 Plantform Enterprise Edition版本1.3规范的第5章。<br />l distributable<br />distributable 元素指出，Web应用是以这样的方式编程的：即，支持集群的服务器可安全地在多个服务器上分布Web应用。例如，一个可分布的应用必须只使用 Serializable对象作为其HttpSession对象的属性，而且必须避免用实例变量（字段）来实现持续性。distributable元素直接出现在discription元素之后，并且不包含子元素或数据，它只是一个如下的标志。<br />&lt;distributable /&gt;<br />l resource-env-ref<br />resource -env-ref元素声明一个与某个资源有关的管理对象。此元素由一个可选的description元素、一个resource-env-ref- name元素（一个相对于java:comp/env环境的JNDI名）以及一个resource-env-type元素（指定资源类型的完全限定的类），如下所示：<br />&lt;resource-env-ref&gt;<br />&lt;resource-env-ref-name&gt;<br />jms/StockQueue<br />&lt;/resource-env-ref-name&gt;<br />&lt;resource-env-ref-type&gt;<br />javax.jms.Queue<br />&lt;/resource-env-ref-type&gt;<br />&lt;/resource-env-ref&gt;<br />l env-entry<br />env -entry元素声明Web应用的环境项。它由一个可选的description元素、一个env-entry-name元素（一个相对于java: comp/env环境JNDI名）、一个env-entry-value元素（项值）以及一个env-entry-type元素（java.lang程序包中一个类型的完全限定类名，java.lang.Boolean、java.lang.String等）组成。下面是一个例子：<br />&lt;env-entry&gt;<br />&lt;env-entry-name&gt;minAmout&lt;/env-entry-name&gt;<br />&lt;env-entry-value&gt;100.00&lt;/env-entry-value&gt;<br />&lt;env-entry-type&gt;minAmout&lt;/env-entry-type&gt;<br />&lt;/env-entry&gt;<br />l ejb-ref<br />ejb -ref元素声明对一个EJB的主目录的应用。它由一个可选的description元素、一个ejb-ref-name元素（相对于java: comp/env的EJB应用）、一个ejb-ref-type元素（bean的类型，Entity或Session）、一个home元素（bean的主目录接口的完全限定名）、一个remote元素（bean的远程接口的完全限定名）以及一个可选的ejb-link元素（当前bean链接的另一个 bean的名称）组成。<br />l ejb-local-ref<br />ejb-local-ref元素声明一个EJB的本地主目录的引用。除了用local-home代替home外，此元素具有与ejb-ref元素相同的属性并以相同的方式使用</font>
<img src ="http://www.blogjava.net/ASONG/aggbug/47970.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ASONG/" target="_blank">ASONG</a> 2006-05-25 10:01 <a href="http://www.blogjava.net/ASONG/articles/47970.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>