﻿<?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-JAVA学习笔记-随笔分类-JAVA</title><link>http://www.blogjava.net/lingy/category/39869.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 30 Jul 2010 03:58:42 GMT</lastBuildDate><pubDate>Fri, 30 Jul 2010 03:58:42 GMT</pubDate><ttl>60</ttl><item><title>《设计模式》之Java解读--桥接Bridge </title><link>http://www.blogjava.net/lingy/archive/2010/07/29/327422.html</link><dc:creator>林光炎</dc:creator><author>林光炎</author><pubDate>Thu, 29 Jul 2010 05:56:00 GMT</pubDate><guid>http://www.blogjava.net/lingy/archive/2010/07/29/327422.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1、核心意图： 将抽象部分和实现部分分离，使它们都可以独立的变化。该模式的目标是通过把高层的抽象和底层的实现分开，分别构建自己的类层次结构，并通过实现部分的接口将两部分进行桥接，从而达到高层抽象和底层实现可以独立的方便扩展的目的。其核心是分离，和委托。&nbsp;2、身边实例： Java语言的一个非常重要的特点是平台的无关性，对于一般的高级语言所编写的程序，如果要在不同的平台上...&nbsp;&nbsp;<a href='http://www.blogjava.net/lingy/archive/2010/07/29/327422.html'>阅读全文</a><img src ="http://www.blogjava.net/lingy/aggbug/327422.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lingy/" target="_blank">林光炎</a> 2010-07-29 13:56 <a href="http://www.blogjava.net/lingy/archive/2010/07/29/327422.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Class Loader</title><link>http://www.blogjava.net/lingy/archive/2009/08/30/293221.html</link><dc:creator>林光炎</dc:creator><author>林光炎</author><pubDate>Sun, 30 Aug 2009 12:54:00 GMT</pubDate><guid>http://www.blogjava.net/lingy/archive/2009/08/30/293221.html</guid><description><![CDATA[态库、动态连接库
<p>程序编制一般需经编辑、编译、连接、加载和运行几个步骤。在我们的应用中，有一些公共代码是需要反复使用，就把这些代码编译为&#8220;库&#8221;文件；在连接步骤中，连接器将从库文件取得所需的代码，复制到生成的可执行文件中。这种库称为静态库，其特点是可执行文件中包含了库代码的一份完整拷贝；缺点就是被多次使用就会有多份冗余拷贝。</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，了解其原理也有助于更好地部署自己的应用。&nbsp;</p>
<p><strong>ClassLoader Tree &amp; Delegation Model</strong></p>
<p>当你决定创建你自己的ClassLoader时，需要继承java.lang.ClassLoader或者它的子类。在实例化每个ClassLoader对象时，需要指定一个父对象；如果没有指定的话，系统自动指定ClassLoader.getSystemClassLoader()为父对象。如下图：</p>
<p><img src="http://www.theserverside.com/articles/content/ClassLoading/Figure2.gif"  alt="" /></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">原话</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/lingy/aggbug/293221.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lingy/" target="_blank">林光炎</a> 2009-08-30 20:54 <a href="http://www.blogjava.net/lingy/archive/2009/08/30/293221.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TOMCAT源码总结一</title><link>http://www.blogjava.net/lingy/archive/2009/08/30/293216.html</link><dc:creator>林光炎</dc:creator><author>林光炎</author><pubDate>Sun, 30 Aug 2009 12:05:00 GMT</pubDate><guid>http://www.blogjava.net/lingy/archive/2009/08/30/293216.html</guid><description><![CDATA[<p style="text-indent: 28px">首先不得不说这个类org.apache.tomcat.util.net.JIoEndpoint，它负责所有的TCP请求连接，实现了一个服务器模式，启用一个后台监听线程，负责接收到来的socket，然后从线程池中取出响应的worker，扔给worker进行处理，自己继续监听。其次worker是一个负责处理socket的一个线程，就是它带着用户的请求开始进入Tomcat世界的，默认的worker总共有200个，即：最多200个线程。当处理完一个请求的时候，这个线程并不会销毁，而是进入wait阻塞，这个线程的对象也不会销毁，是进入了一个栈里面，对应workstack那个数据结构。每当接收线程拿到一个socket的时候，就先从栈里面拿出一个已有的线程对象，然后就利用该对象的assign方法，将这个socket给它，并调用notify重新唤醒这个worker的处理线程。以后我们做小型服务器的时候，可以借鉴它的实现方式。正在研究多线程的朋友，这个类绝对让你可以学的透彻！</p>
<p style="text-indent: 28px">相对应的还有一个org.apache.tomcat.util.net.NioEndpoint，这个和前面那个功能差不多，但是用了NIO包里的API，有一个最大的区别就是，接收线程接收一个socket之后，可能会将这个socket先放入缓存池，然后worker从池里面拿socket去处理，比前面那个类看起来功能和性能都会提升很多，不过代码行有2K多，相当复杂，设计不够精巧，有兴趣可以去研究下。</p>
<p style="text-indent: 28px">org.apache.tomcat.util.buf.MessageBytes：这是一个接近底层的字符串处理类，为什么说是接近底层，是因为socket接收进来的都是字节类型，而java用的是char或者String，这之间的转换涉及到编码问题和性能问题，所以凡是socket收进来的信息，全部都用这个类表示，只有当要输出字符串的时候，才会将里面的字节进行转换，实现一种延迟加载的懒模式，被Tomcat底层所使用的Request类，就是大量使用了这个类来存放数据。我们来小小窥视一下，Request类：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline"><span style="color: green">private</span><span style="color: gray"> </span><span style="color: blue">MessageBytes</span><span style="color: gray"> </span><span style="color: blue">methodMB</span><span style="color: gray"> = </span><span style="color: blue">MessageBytes</span><span style="color: gray">.</span><span style="color: blue">newInstance</span><span style="color: olive">()</span><span style="color: gray">;</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: green">private</span><span style="color: gray">&nbsp;</span><span style="color: blue">MessageBytes</span><span style="color: gray"> </span><span style="color: blue">unparsedURIMB</span><span style="color: gray"> = </span><span style="color: blue">MessageBytes</span><span style="color: gray">.</span><span style="color: blue">newInstance</span><span style="color: olive">()</span><span style="color: gray">;</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: green">private</span><span style="color: gray">&nbsp;</span><span style="color: blue">MessageBytes</span><span style="color: gray"> </span><span style="color: blue">uriMB</span><span style="color: gray"> = </span><span style="color: blue">MessageBytes</span><span style="color: gray">.</span><span style="color: blue">newInstance</span><span style="color: olive">()</span><span style="color: gray">;</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: green">private</span><span style="color: gray">&nbsp;</span><span style="color: blue">MessageBytes</span><span style="color: gray"> </span><span style="color: blue">decodedUriMB</span><span style="color: gray"> = </span><span style="color: blue">MessageBytes</span><span style="color: gray">.</span><span style="color: blue">newInstance</span><span style="color: olive">()</span><span style="color: gray">;</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: green">private</span><span style="color: gray">&nbsp;</span><span style="color: blue">MessageBytes</span><span style="color: gray"> </span><span style="color: blue">queryMB</span><span style="color: gray"> = </span><span style="color: blue">MessageBytes</span><span style="color: gray">.</span><span style="color: blue">newInstance</span><span style="color: olive">()</span><span style="color: gray">;</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: green">private</span><span style="color: gray">&nbsp;</span><span style="color: blue">MessageBytes</span><span style="color: gray"> </span><span style="color: blue">protoMB</span><span style="color: gray"> = </span><span style="color: blue">MessageBytes</span><span style="color: gray">.</span><span style="color: blue">newInstance</span><span style="color: olive">()</span><span style="color: gray">;</span>
    <li><span style="color: gray">&nbsp;</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: #ffa500">// remote address/host</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: green">private</span><span style="color: gray">&nbsp;</span><span style="color: blue">MessageBytes</span><span style="color: gray"> </span><span style="color: blue">remoteAddrMB</span><span style="color: gray"> = </span><span style="color: blue">MessageBytes</span><span style="color: gray">.</span><span style="color: blue">newInstance</span><span style="color: olive">()</span><span style="color: gray">;</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: green">private</span><span style="color: gray">&nbsp;</span><span style="color: blue">MessageBytes</span><span style="color: gray"> </span><span style="color: blue">localNameMB</span><span style="color: gray"> = </span><span style="color: blue">MessageBytes</span><span style="color: gray">.</span><span style="color: blue">newInstance</span><span style="color: olive">()</span><span style="color: gray">;</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: green">private</span><span style="color: gray">&nbsp;</span><span style="color: blue">MessageBytes</span><span style="color: gray"> </span><span style="color: blue">remoteHostMB</span><span style="color: gray"> = </span><span style="color: blue">MessageBytes</span><span style="color: gray">.</span><span style="color: blue">newInstance</span><span style="color: olive">()</span><span style="color: gray">;</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: green">private</span><span style="color: gray">&nbsp;</span><span style="color: blue">MessageBytes</span><span style="color: gray"> </span><span style="color: blue">localAddrMB</span><span style="color: gray"> = </span><span style="color: blue">MessageBytes</span><span style="color: gray">.</span><span style="color: blue">newInstance</span><span style="color: olive">()</span><span style="color: gray">;</span>
    <li><span style="color: gray">&nbsp; &nbsp;&nbsp; </span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: green">private</span><span style="color: gray">&nbsp;</span><span style="color: blue">MimeHeaders</span><span style="color: gray"> </span><span style="color: blue">headers</span><span style="color: gray"> = </span><span style="color: green">new</span><span style="color: gray"> </span><span style="color: blue">MimeHeaders</span><span style="color: olive">()</span><span style="color: gray">;</span> </li>
</ol>
</div>
<p style="text-indent: 28px">或许大家会觉得，构造出这么多的类，性能会高到哪里去，其实不是这样的，不停的构造和销毁对象的确会损耗相当的性能，但是一个对象被构造出来，可以重复利用，那就相当完美了，这个类就是如此的设计，其中有一个回收资源的方法，叫recycle()，这个方法可以清空里面的数组，清空里面的对象，而不会销毁自己本身，因为使用它的对象，只要调用recycle，以后又可以重复使用了。</p>
<p style="text-indent: 28px">MessageBytes其实内置了2个重要的类，org.apache.tomcat.util.buf.ByteChunk和org.apache.tomcat.util.buf.CharChunk，这2个类带我们回到了C时代，为什么这么说？因为它简直就是一个字符串处理类，一些眼熟的算法全部映入眼帘，比如字符转匹配算法，indexOf，startsWith，判断字符转是否相等，查找字符，等等，比之JDK提供的性能更好，功能更强大（这句话说过了，呵呵）</p>
<p style="text-indent: 28px">还有一个实用的值得学习的数据结构是，org.apache.tomcat.util.buf.Ascii，如果知道表驱动的朋友们，一定对这个类很熟悉了，判断大小写？判断是不是英文单词？判断是不是空白符？判断是不是数字，将字节类型转换为int、long类型，大小写转换，等等。这些都是大学计算机课程的课后练习题</p>
<img src ="http://www.blogjava.net/lingy/aggbug/293216.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lingy/" target="_blank">林光炎</a> 2009-08-30 20:05 <a href="http://www.blogjava.net/lingy/archive/2009/08/30/293216.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何处理HTTP POST/GET请求</title><link>http://www.blogjava.net/lingy/archive/2009/08/29/293078.html</link><dc:creator>林光炎</dc:creator><author>林光炎</author><pubDate>Sat, 29 Aug 2009 03:56:00 GMT</pubDate><guid>http://www.blogjava.net/lingy/archive/2009/08/29/293078.html</guid><description><![CDATA[期：2005-11-14 11:44:00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Host01.Com]&nbsp;&nbsp; 访问：<font color="#c0c0c0"> <script src="http://www.host01.com/Click.asp?NewsID=0511141226326140"></script><iframe id="clickframe" border="0" marginwidth="0" framespacing="0" marginheight="0" src="/clickJs.asp?NewsID=0511141226326140" frameborder="0" width="50" scrolling="no" height="12"></iframe></font>　　　　[<a href="http://www.host01.com/Print.Html?38371" target="_blank"><strong>全屏查看全文</strong></a>]
<div id="artcontent">
<div id="googlead"><img id="alimamatmpf0.5671811678460966" style="border-top-width: 0px; padding-right: 0px; padding-left: 0px; border-left-width: 0px; border-bottom-width: 0px; padding-bottom: 0px; margin: 0px; vertical-align: baseline; width: 0px; padding-top: 0px; height: 0px; border-right-width: 0px" src="http://z.alimama.com/alimamal.php?i=mm_10032417_125151_3793733&amp;w=336&amp;h=280&amp;re=1280x800&amp;sz=36&amp;cg=b6f22af0c6357dd70c95485f40811cc4&amp;prl=83259294&amp;cas=prl&amp;cah=774&amp;caw=1280&amp;ccd=32&amp;ctz=8&amp;chl=0&amp;cja=1&amp;cpl=0&amp;cmm=0&amp;cf=10.0&amp;u=http%3A%2F%2Fwww.host01.com%2Farticle%2Fexam%2F01300005%2F05111412263220025.htm&amp;r=http%3A%2F%2Fwww.google.cn%2Fsearch%3Fhl%3Dzh-CN%26newwindow%3D1%26q%3Dhttp%2Bresponse%2B%25E6%25B5%2581%26aq%3Df%26oq%3D&amp;sx=677&amp;sy=418&amp;cbw=1270&amp;cbh=3705" border="0" name="alimamatmpf0.5671811678460966"  alt="" /><iframe id="alimamaf0.5671811678460966" style="width: 336px; height: 280px" border="0" name="alimamaf0.5671811678460966" marginwidth="0" marginheight="0" src="http://z.alimama.com/alimama.php?i=mm_10032417_125151_3793733&amp;w=336&amp;h=280&amp;re=1280x800&amp;sz=36&amp;cg=b6f22af0c6357dd70c95485f40811cc4&amp;prl=83259294&amp;cas=prl&amp;cah=774&amp;caw=1280&amp;ccd=32&amp;ctz=8&amp;chl=0&amp;cja=1&amp;cpl=0&amp;cmm=0&amp;cf=10.0&amp;dx=2&amp;ac=8431&amp;cbh=3461&amp;cbw=1270&amp;iss=0&amp;t=2&amp;tc=0000FF&amp;dc=000000&amp;bgc=FFFFFF&amp;bdc=E6E6E6&amp;lc=008000&amp;bmc=FFFFFF&amp;as=0&amp;bgp=0&amp;ic=0&amp;pf=1&amp;sx=433&amp;sy=3455&amp;u=http%3A%2F%2Fwww.host01.com%2Farticle%2Fexam%2F01300005%2F05111412263220025.htm&amp;k=http+response+%E6%B5%81&amp;tt=%E3%80%90%E8%BD%AF%E8%80%83%E3%80%91%E5%A6%82%E4%BD%95%E5%A4%84%E7%90%86HTTP%20POST%2FGET%E8%AF%B7%E6%B1%82%20-%20%E8%BD%AF%E4%BB%B6%E6%B0%B4%E5%B9%B3%E8%80%83%E8%AF%95&amp;r=http%3A%2F%2Fwww.google.cn%2Fsearch%3Fhl%3Dzh-CN%26newwindow%3D1%26q%3Dhttp%2Bresponse%2B%25E6%25B5%2581%26aq%3Df%26oq%3D&amp;fu=-1" frameborder="0" scrolling="no"></iframe></div>
<font size="2">Servlet通过下面的方法来提供服务： </font>
<ul>
    <li>实现service方法。<br />
    <li>实现HttpServlet的doMethod方法（doGet、doDelete、doOptions、 doPost、doPut、doTrace）。</li>
</ul>
&nbsp;&nbsp;&nbsp;&nbsp;通常，service方法用来从客户请求（request）中提取信息，访问扩展资源，并基于上面的信息提供响应（response）。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;对于HTTP Servlets，正确提供响应的过程是首先填写响应（response）的头信息，然后从响应（response）中得到输出流，最后向输出流中写入内容信息。响应（response）头信息必须最先设置。下面将描述如何从请求（request）中获得信息和产生HTTP响应（response）。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;
<li>取得客户端请求 <br />
&nbsp;&nbsp;&nbsp;&nbsp;一个HttpServletRequest对象提供到达HTTP 头部数据，也允许你获取客户端的数据。怎样获取这些数据取决于HTTP端请求方法。不管用任何HTTP方式，你都可以用 getParameterValues方法返回特定名称的参数值。对于用 HTTP GET 请求的方式，这个getQueryString方法将会返回一个可以用来分析的值。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;客户端请求（request）包含了从客户端传递到Servlet的数据。所有的请求（request）都实现了ServletRequest接口。这个接口定义了一些方法访问下面的信息，如表14-1所示。 <br />
<br />
<ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt" bgcolor="#e6e6e6">
            <pre><ccid_code>表14-1  ServletRequest接口方法
            类 型 描 述	                         对 应 方 法
            参数，用来在客户端和Servlet之间传送信息 getAttribute(String name)
            getAttributeNames()
            getInputStream()
            getParameter(String name)
            getParameterMap()
            getParameterNames()
            getParameterValues(String name)
            对象值属性，用来在Servlet容器和Servlet
            之间，或者协作的Servlet之间传递信息	    removeAttribute(String name)
            setAttribute(String name, Object o)
            有关请求使用的协议信息，
            客户端和服务器在请求中的调用	   getContentLength()
            getContentType()
            getProtocol()
            getReader()
            getRealPath(String path)
            getRemoteAddr()
            getRemoteHost()
            getRequestDispatcher(String path)
            有关请求使用的协议信息，
            客户端和服务器在请求中的调用       getScheme()
            getServerName()
            getServerPort()
            isSecure()
            有关localization的信息	       getCharacterEncoding()
            getLocale()
            getLocales()
            setCharacterEncoding(String env)</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</ccid_nobr><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;下面的代码段示范了如何使用request中的方法获得客户端信息。 <br />
<br />
<ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt" bgcolor="#e6e6e6">
            <pre><ccid_code>Enumeration params = request.getParameterNames();
            String paramName = null;
            String[] paramValues = null;
            while (params.hasMoreElements()) {
            paramName = (String) params.nextElement();
            paramValues = request.getParameterValues(paramName);
            System.out.println("\nParameter name is " + paramName);
            for (int i = 0; i &lt; paramValues.length; i++) {
            System.out.println(", value " + i + " is " + paramValues[i].toString());
            }
            }</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</ccid_nobr><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;HTTP Servlets使用HTTP request对象（HttpServletRequest），它包含了request URL、HTTP头信息、查询字符串，等等。HTTP request URL 包括几个部分：<br />
&nbsp;&nbsp;&nbsp;&nbsp;http://<host>:
<port><request path>?<query string> <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;一般情况下： <br />
<br />
<ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt" bgcolor="#e6e6e6">
            <pre><ccid_code>requestURI = contextPath + servletPath + pathInfo
            Context path：通过getContextPath方法获得。
            Servlet Path：通过getServletPath方法获得。
            PathInfo：通过getPathInfo方法获得。</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</ccid_nobr><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;如表14-2所示。 <br />
<br />
<ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt" bgcolor="#e6e6e6">
            <pre><ccid_code>表14-2  路径的对应
            Request Path	            Path Elements
            /catalog/help/feedback.jsp	ContextPath: /catalog ServletPath:
            /help/feedback.jsp PathInfo: null</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</ccid_nobr><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;
<li>提供HTTP响应 <br />
&nbsp;&nbsp;&nbsp;&nbsp;响应（response）包含了在服务器和客户端之间传递的数据。所有的响应（response）都实现了ServletResponse接口。这个接口定义了一些方法提供给开发人员使用，如表14-3所示。 <br />
<br />
<ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt" bgcolor="#e6e6e6">
            <pre><ccid_code>表14-3  ServletResponse接口方法
            类 型 描 述	                          对 应 方 法
            获得向客户端发送数据的输出流	 发送字符流：getWriter()
            发送字节流：getOutputStream()
            指示响应返回的内容类型（例如：text/html）
            已经注册的内容类型名称保存在IANA
            （Internet Assigned Numbers Authority） setContentType(java.lang.String type)
            指出是否是缓冲输出。默认情况下写入输出的
            内容被立即发送到客户端。使用缓冲后写入输出的内容先
            不发送到客户端，这样Servlet有更多的时间设置相应的
            状态码和头信息，或者转移到其他的Web资源	 flushBuffer()
            getBufferSize()
            isCommitted()
            reset()
            resetBuffer()
            setBufferSize(int size)
            setContentLength(int len)
            设置localization信息	            getCharacterEncoding()
            getLocale()
            setLocale(java.util.Locale loc)</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</ccid_nobr><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;HTTP response类（HttpServletResponse）有一些代表HTTP头信息的域：<br />
&nbsp;&nbsp;&nbsp;&nbsp;
<li>状态码用来指出响应（response）失败的原因。 <br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;
<li>Cookies在客户端存储应用相关的信息，有时cookies用来维护和标识用户的session。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;Servlet首先设置响应（response）头信息，包括响应（response）的内容类别和缓冲区大小，然后在doGet方法中从响应（response）获得PrintWriter ，最后向输出中写入HTML代码，调用close()方法提交这次对客户端的响应（response）。示范代码如下： <br />
<br />
<ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt" bgcolor="#e6e6e6">
            <pre><ccid_code>public void doGet (HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException
            {
            // 设置头信息
            response.setContentType("text/html");
            response.setBufferSize(8192);
            PrintWriter out = response.getWriter();
            // 向response中输出
            out.println("&lt;html&gt;" +
            "&lt;head&gt;&lt;title&gt;+
            messages.getString("TitleBookDescription")
            +&lt;/title&gt;&lt;/head&gt;");
            ...
            out.println("&lt;/body&gt;&lt;/html&gt;");
            // 关闭输出流
            out.close();
            }</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</li>
</div>
<img src ="http://www.blogjava.net/lingy/aggbug/293078.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lingy/" target="_blank">林光炎</a> 2009-08-29 11:56 <a href="http://www.blogjava.net/lingy/archive/2009/08/29/293078.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TOMCAT源码分析(消息处理)</title><link>http://www.blogjava.net/lingy/archive/2009/08/26/292716.html</link><dc:creator>林光炎</dc:creator><author>林光炎</author><pubDate>Wed, 26 Aug 2009 12:53:00 GMT</pubDate><guid>http://www.blogjava.net/lingy/archive/2009/08/26/292716.html</guid><description><![CDATA[：前言
<p style="text-indent: 21pt"><span style="font-family: 宋体">我们知道了tomcat的整体框架了， 也明白了里面都有些什么组件， 以及各个组件是干什么用的了。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">http://www.csdn.net/Develop/read_article.asp?id=27225</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">我想，接下来我们应该去了解一下 tomcat 是如何处理jsp和servlet请求的。</span></p>
<p><span style="font-family: 宋体">1. &nbsp;我们以一个具体的例子，来跟踪</span>TOMCAT<span style="font-family: 宋体">，</span><span style="font-family: 宋体">看看它是如何把</span>Request<span style="font-family: 宋体">一层一层地递交给下一个容器，</span><span style="font-family: 宋体">并最后交给</span>Wrapper<span style="font-family: 宋体">来处理的。</span></p>
<p style="margin-left: 21pt"><span style="font-family: 宋体">以</span><u><span style="color: blue">http://localhost:8080/web/login.jsp</span></u><span style="font-family: 宋体">为例子</span></p>
<p style="margin-left: 21pt"><span style="font-family: 宋体">（以下例子，</span><span style="font-family: 宋体">都是以</span>tomcat4 <span style="font-family: 宋体">源码为参考）</span></p>
<p style="margin-left: 21pt"><span style="font-family: 宋体">这篇心得主要分为3个部分： 前期， 中期， 和末期。</span></p>
<p style="margin-left: 21pt">&nbsp;<span style="font-family: 宋体">前期：讲解了在浏览器里面输入一个URL，是怎么被tomcat抓住的。</span></p>
<p style="margin-left: 21pt"><span style="font-family: 宋体">中期：讲解了被tomcat抓住后，又是怎么在各个容器里面穿梭， 最后到达最后的处理地点。</span></p>
<p style="margin-left: 21pt"><span style="font-family: 宋体">末期：讲解到达最后的处理地点后，又是怎么具体处理的。</span></p>
<p style="margin-left: 21pt">2<span style="font-family: 宋体">、</span>&nbsp;<span style="font-family: 宋体">前期 Request的born.</span></p>
<p style="margin-left: 21pt; text-indent: -21pt; tab-stops: 21.0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; 在这里我先简单讲一下request这个东西。</span></p>
<p style="margin-left: 21pt; text-indent: -21pt; tab-stops: 21.0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp; 我们先看着这个URL：</span><u><span style="color: blue">http://localhost:8080/web/login.jsp</span></u><span style="color: black; font-family: 宋体">&nbsp;它是动用了8080端口来进行socket通讯的。</span></p>
<p style="margin-left: 21pt; text-indent: -21pt; tab-stops: 21.0pt"><span style="color: black; font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp; 我们知道, 通过 </span></p>
<p style="margin-left: 21pt; text-indent: -21pt; tab-stops: 21.0pt"><span style="color: black; font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InputStream in = socket.getInputStream() 和</span></p>
<p style="margin-left: 21pt; text-indent: -21pt; tab-stops: 21.0pt"><span style="color: black; font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OutputStream out = socket.getOutputStream() </span></p>
<p style="text-indent: -1.05pt; tab-stops: 21.0pt"><span style="color: black; font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;就可以实现消息的来来往往了。</span></p>
<p style="margin-left: 21pt; text-indent: -21pt; tab-stops: 21.0pt"><span style="color: black; font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp; 但是如果把Stream给应用层看，显然操作起来不方便。 </span></p>
<p style="margin-left: 21pt; text-indent: -21pt; tab-stops: 21.0pt"><span style="color: black; font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp; 所以，在tomcat 的Connector里面， socket被封装成了Request和Response这两个对象。</span></p>
<p style="margin-left: 21pt; text-indent: -21pt; tab-stops: 21.0pt"><span style="color: black; font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp; 我们可以简单地把Request看成管发到服务器来的数据，把Response看成想发出服务器的数据。</span></p>
<p style="margin-left: 21pt; text-indent: -21pt; tab-stops: 21.0pt"><span style="color: black; font-family: 宋体">&nbsp;&nbsp;&nbsp; &nbsp;</span></p>
<p style="margin-left: 21pt; text-indent: -21pt; tab-stops: 21.0pt"><span style="color: black; font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp; 但是这样又有其他问题了啊？ Request这个对象是把socket封装起来了， 但是他提供的又东西太多了。</span></p>
<p style="margin-left: 21pt; text-indent: -21pt; tab-stops: 21.0pt"><span style="color: black; font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp; 诸如Request.getAuthorization(), Request.getSocket()。&nbsp;像Authorization这种东西开发人员拿来基本上用不太着，而像socket这种东西，暴露给开发人员又有潜在的危险。 而且啊， 在Servlet Specification里面标准的通信类是ServletRequest和HttpServletRequest，而非这个Request类。 So, So, So. Tomcat必须得捣持捣持Request才行。 最后tomcat选择了使用捣持模式（应该叫适配器模式）来解决这个问题。它把org.apache.catalina.Request 捣持成了 org.apache.coyote.tomcat4.CoyoteRequest。 而CoyoteRequest又实现了ServletRequest和HttpServletRequest 这两种接口。 这样就提供给开发人员需要且刚刚需要的方法了。</span></p>
<p style="margin-left: 21pt; text-indent: -21pt; tab-stops: 21.0pt"><span style="color: black; font-family: 宋体">&nbsp;</span></p>
<p style="margin-left: 21pt; text-indent: -21pt; tab-stops: 21.0pt">&nbsp;&nbsp;&nbsp; ok, <span style="font-family: 宋体">让</span><span style="font-family: 宋体">我们在 tomcat的顶层容器 - </span>StandardEngin <span style="font-family: 宋体">的invoke()方法这里设置一个断点， 然后访问</span></p>
<p style="margin-left: 21pt; text-indent: -21pt; tab-stops: 21.0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; </span><u><span style="color: blue">http://localhost:8080/web/login.jsp</span></u><span style="color: black; font-family: 宋体">， 我们来看看在前期都会路过哪些地方：</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">1. </span>run(): 536, java.lang.Thread, Thread.java</p>
<p><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; CurrentThread</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.&nbsp;run():666, org.apache.tomcat.util.threads.ThreadPool$ControlRunnable, ThreadPool.java</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;ThreadPool</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.&nbsp;runIt():589, org.apache.tomcat.util.net.TcpWorkerThread, PoolTcpEndpoint.java</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;ThreadWorker</p>
<p style="margin-left: 42pt; text-indent: -20.25pt; tab-stops: list 42.0pt">4.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>processConnection(): &nbsp;549</p>
<p style="margin-left: 21.75pt; text-indent: 20.25pt">org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler, Http11Protocol.java</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; http protocol parser</p>
<p>&nbsp; &nbsp;&nbsp; &nbsp;5.&nbsp;Process(): 781, org.apache.coyote.http11.Http11Processor, Http11Processor.java</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; http request processor</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6. service(): 193, org.apache.coyote.tomcat4.CoyoteAdapter,CoyoteAdapter.java</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;adapter</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7. invoke(): 995, org.apache.catalina.core.ContainerBase, ContainerBase.java</p>
<p style="text-indent: 21pt">&nbsp;&nbsp; StandardEngin</p>
<p><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; 1. 主线程</span></p>
<p><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; 2. 启动线程池.</span></p>
<p><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; 3. 调出线程池里面空闲的工作线程。</span></p>
<p><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; 4. 把8080端口传过来由httpd协议封装的数据，解析成Request和Response对象。</span></p>
<p><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; 5. 使用</span>Http11Processor<span style="font-family: 宋体">来处理request</span></p>
<p><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; 6. 在</span>Http11Processor<span style="font-family: 宋体">里面， 又会call </span>CoyoteAdapter<span style="font-family: 宋体">来进行适配处理，把Request适配成实现了ServletRequest和HttpServletRequest接口的</span>CoyoteRequest.</p>
<p style="text-indent: 21pt">7<span style="font-family: 宋体">. 到了这里，前期的去毛拔皮工作就基本上搞定，可以交给</span>StandardEngin <span style="font-family: 宋体">做核心的处理工作了。</span></p>
<p><span style="font-family: 宋体">3. 中期。 在各个容器间的穿梭。</span></p>
<p><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; Request在各个容器里面的穿梭大致是这样一种方式：</span></p>
<p><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; 每个容器里面都有一个管道（pipline）， 专门用来传送Request用的。</span></p>
<p><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; 管道里面又有好几个阀门（valve）， 专门用来过滤Request用的。</span></p>
<p><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; 在管道的低部通常都会放上一个默认的阀们。 这个阀们至少会做一件事情，就是把Request交给子容器。</span></p>
<p><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; 让我们来想象一下：</span></p>
<p><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp; 当一个Request进入一个容器后， 它就在管道里面流动，波罗~ 波罗~ 波罗~ 地穿过各个阀门。在流到最后一个阀门的时候，吧唧~ 那个该死的阀门就把它扔给了子容器。 然后又开始 波罗~ 波罗~ 波罗~ ... 吧唧~....&nbsp;波罗~&nbsp;波罗~ 波罗~ ....吧唧~.... </span></p>
<p><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; 就是通过这种方式， Request 走完了所有的容器。（ 感觉有点像消化系统，最后一个地方有点像那里~&nbsp;）</span></p>
<p><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; OK， 让我们具体看看都有些什么容器， 各个容器里面又都有些什么阀门，这些阀们都对我们的Request做了些什么吧：</span></p>
<p style="text-indent: 21pt">3.1 StandardEngin <span style="font-family: 宋体">的pipeline里面放的是：</span>StandardEnginValve</p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">在这里，</span>VALVE<span style="font-family: 宋体">做了三件事：</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt">1.&nbsp;&nbsp; <span style="font-family: 宋体">验证传递过来的</span>request<span style="font-family: 宋体">是不是</span>httpservletRequest.</p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt">2&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">验证传递过来的</span> request <span style="font-family: 宋体">是否携带了</span>host header<span style="font-family: 宋体">信息</span>.</p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt">3&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体">选择相应的</span>host<span style="font-family: 宋体">去处理它。（一般我们都只有一个host:</span>localhost<span style="font-family: 宋体">，也就是</span>127.0.0.1<span style="font-family: 宋体">）。</span></p>
<p style="text-indent: 21.3pt"><span style="font-family: 宋体">到了这个地方，</span><span style="font-family: 宋体">我们的</span>request<span style="font-family: 宋体">就已经完成了在</span>Engin<span style="font-family: 宋体">这个部分的历史使命，</span><span style="font-family: 宋体">通向前途未卜的下一站：</span> host<span style="font-family: 宋体">了。</span></p>
<p style="margin-left: 21pt">3.2 StandardHost <span style="font-family: 宋体">的pipline里面放的是：</span> StandardHostValve</p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt">1.&nbsp;&nbsp; <span style="font-family: 宋体">验证传递过来的</span>request<span style="font-family: 宋体">是不是</span>httpservletRequest.</p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt">2.&nbsp;&nbsp; <span style="font-family: 宋体">根据</span>Request<span style="font-family: 宋体">来确定哪个</span>Context<span style="font-family: 宋体">来处理。</span></p>
<p style="margin-left: 39pt">Context<span style="font-family: 宋体">其实就是</span>webapp<span style="font-family: 宋体">，</span><span style="font-family: 宋体">比如</span><u><span style="color: blue">http://localhost:8080/web/login.jsp</span></u></p>
<p style="margin-left: 39pt"><span style="font-family: 宋体">这里</span>web<span style="font-family: 宋体">就是</span>Context<span style="font-family: 宋体">罗！</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt">3.&nbsp;&nbsp; <span style="font-family: 宋体">既然确定了是哪个</span>Context<span style="font-family: 宋体">了，那么就应该把那个</span>Context<span style="font-family: 宋体">的</span>classloader<span style="font-family: 宋体">付给当前线程了。</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.currentThread().setContextClassLoader(context.getLoader().getClassLoader());</p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt">&nbsp;&nbsp; <span style="font-family: 宋体">这样</span>request<span style="font-family: 宋体">就只看得见指定的</span>context<span style="font-family: 宋体">下面的</span>classes<span style="font-family: 宋体">啊，</span> jar<span style="font-family: 宋体">啊这些，</span><span style="font-family: 宋体">而看不见</span>tomcat<span style="font-family: 宋体">本身的类，</span><span style="font-family: 宋体">什么</span>Engin<span style="font-family: 宋体">啊，</span> Valve<span style="font-family: 宋体">啊。</span><span style="font-family: 宋体">不然还得了啊！</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">4. 既然request到了这里了，看来用户是准备访问web这个web app了，咋们得更新一下这个用户的session不是！ Ok , 就由manager更新一下用户的session信息</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">5. 交给具体的Context 容器去继续处理Request.</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">6. Context处理完毕了，把</span>classloader<span style="font-family: 宋体">还回来。</span></p>
<p style="margin-left: 21pt">3.3 StandardContext <span style="font-family: 宋体">的pipline里面放的是：</span> StandardContextValve</p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt">1.&nbsp;&nbsp; <span style="font-family: 宋体">验证传递过来的</span>request<span style="font-family: 宋体">是不是</span>httpservletRequest.</p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt">2.&nbsp;&nbsp; <span style="font-family: 宋体">如果</span>request<span style="font-family: 宋体">意图不轨，想要访问</span>/meta-inf, /web-inf<span style="font-family: 宋体">这些目录下的东西，呵呵，没有用D!</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt">3.&nbsp;&nbsp; <span style="font-family: 宋体">这个时候就会根据</span>Request<span style="font-family: 宋体">到底是</span>Servlet<span style="font-family: 宋体">，</span><span style="font-family: 宋体">还是</span>jsp<span style="font-family: 宋体">，</span><span style="font-family: 宋体">还是静态资源来决定到底用哪种</span>Wrapper<span style="font-family: 宋体">来处理这个</span>Reqeust<span style="font-family: 宋体">了。</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt">4.&nbsp;&nbsp; <span style="font-family: 宋体">一旦决定了到底用哪种</span>Wrapper<span style="font-family: 宋体">，</span>OK<span style="font-family: 宋体">，交给那个</span>Wrapper<span style="font-family: 宋体">处理。</span></p>
<p style="margin-left: 39pt; text-indent: -39pt; tab-stops: 39.0pt"><span style="font-family: 宋体">4. 末期。 不同的需求是怎么处理的.</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">StandardWrapper</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">之前对Wrapper没有做过讲解，其实它是这样一种东西。</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">我们在处理Request的时候，可以分成3种。</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">处理静态的： org.apache.catalina.servlets.DefaultServlet&nbsp;&nbsp; </span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">处理jsp的：org.apache.jasper.servlet.JspServlet </span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">处理servlet的：org.apache.catalina.servlets.InvokerServlet</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">不同的request就用这3种不同的servlet去处理。</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">Wrapper就是对它们的一种简单的封装，有了Wrapper后，我们就可以轻松地拦截每次的Request。也可以容易地调用servlet的init()和destroy()方法， 便于管理嘛！</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">具体情况是这么滴：</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">&nbsp;&nbsp; 如果request是找jsp文件，StandardWrapper里面就会封装一个org.apache.jasper.servlet.JspServlet去处理它。</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">&nbsp;&nbsp; 如果request是找 静态资源 ，StandardWrapper里面就会封装一个org.apache.jasper.servlet.DefaultServlet&nbsp;去处理它。</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">&nbsp;&nbsp; 如果request是找servlet ，StandardWrapper里面就会封装一个org.apache.jasper.servlet.InvokerServlet 去处理它。</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">StandardWrapper同样也是容器，既然是容器， 那么里面一定留了一个管道给request去穿，管道低部肯定也有一个阀门(注1)，用来做最后一道拦截工作.</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">在这最底部的阀门里，其实就主要做了两件事:</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">&nbsp;&nbsp; 一是启动过滤器，让request在N个过滤器里面筛一通，如果OK！ 那就PASS。 否则就跳到其他地方去了。</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">&nbsp;&nbsp; 二是servlet.service((HttpServletRequest) request,(HttpServletResponse) response); 这个方法.</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp; 如果是 JspServlet， 那么先把jsp文件编译成servlet_xxx, 再invoke servlet_xxx的servie()方法。</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp; 如果是 DefaultServlet， 就直接找到静态资源，取出内容， 发送出去。</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp; 如果是 InvokerServlet， 就调用那个具体的servlet的service()方法。</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">&nbsp;&nbsp; ok! 完毕。</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">注1: StandardWrapper 里面的阀门是最后一道关口了。 如果这个阀门欲意把request交给StandardWrapper 的子容器处理。 对不起， 在设计考虑的时候， Wrapper就被考虑成最末的一个容器， 压根儿就不会给Wrapper添加子容器的机会！ 如果硬是要调用addChild(), 立马抛出IllegalArgumentException！</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="font-family: 宋体">参考：</span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><font color="#0a0011">&nbsp;<span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; </span><span style="color: #333333; font-family: Arial"></font><font color="#0a0011">&lt;</font><a href="http://jakarta.apache.org/tomcat/"><font color="#0a0011">http://jakarta.apache.org/tomcat/</font></a><font color="#0a0011">&gt;<br />
&nbsp;&nbsp;&nbsp;&lt;</font><a href="http://www.onjava.com/pub/a/onjava/2003/05/14/java_webserver.html"><font color="#0a0011">http://www.onjava.com/pub/a/onjava/2003/05/14/java_webserver.html</font></a><font color="#0a0011">&gt;</font></span></p>
<p style="margin-left: 39pt; text-indent: -18pt; tab-stops: 39.0pt"><span style="color: #333333; font-family: Arial"><font color="#0a0011">&nbsp;</font></span></p>
<br />
<div style="font-size: 14px; line-height: 25px"><strong>作者Blog：</strong><a id="ArticleContent1_ArticleContent1_AuthorBlogLink" href="http://blog.csdn.net/ThomasHuang/" target="_blank">http://blog.csdn.net/ThomasHuang/</a></div>
<div style="font-size: 14px; color: #900; line-height: 25px"><strong>相关文章</strong></div>
<table id="ArticleContent1_ArticleContent1_RelatedArticles" cellspacing="0" border="0">
    <tbody>
        <tr>
            <td><a href="http://dev.csdn.net/article/28/28075.shtm">TOMCAT源码分析(消息处理)</a> </td>
        </tr>
        <tr>
            <td><a href="http://dev.csdn.net/article/27/27225.shtm">TOMCAT源码分析(启动框架)</a> </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/lingy/aggbug/292716.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lingy/" target="_blank">林光炎</a> 2009-08-26 20:53 <a href="http://www.blogjava.net/lingy/archive/2009/08/26/292716.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TOMCAT源码分析(启动框架</title><link>http://www.blogjava.net/lingy/archive/2009/08/26/292715.html</link><dc:creator>林光炎</dc:creator><author>林光炎</author><pubDate>Wed, 26 Aug 2009 12:49:00 GMT</pubDate><guid>http://www.blogjava.net/lingy/archive/2009/08/26/292715.html</guid><description><![CDATA[<table cellpadding="4" width="100%" border="0">
    <tbody>
        <tr>
            <td height="19">
            <table cellspacing="3" cellpadding="3" width="100%" bgcolor="#eeeeee" border="0">
                <tbody>
                    <tr>
                        <td><strong><span id="ArticleTitle1_ArticleTitle1_lblTitle">TOMCAT源码分析(启动框架)</span></strong>&nbsp;&nbsp;&nbsp;&nbsp; 选择自 <a id="ArticleTitle1_ArticleTitle1_AuthorLink" href="http://dev.csdn.net/user/ThomasHuang">ThomasHuang</a> 的 Blog </td>
                    </tr>
                    <tr>
                        <td align="center" bgcolor="#003399" height="16"><font color="#ffffff">关键字</font></td>
                        <td width="500">&nbsp; <span id="ArticleTitle1_ArticleTitle1_lblKeywords">tomcat 源代码 源码 source code architecture</span></td>
                    </tr>
                    <tr>
                        <td align="center" bgcolor="#003399" height="16"><font color="#ffffff">出处</font></td>
                        <td>&nbsp; </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
        <tr>
            <td width="10"></td>
            <td><span id="ArticleContent1_ArticleContent1_lblContent">
            <p>TOMCAT源码分析(启动框架)<br />
            前言：<br />
            &nbsp;&nbsp; 本文是我阅读了TOMCAT源码后的一些心得。 主要是讲解TOMCAT的系统框架， 以及启动流程。若有错漏之处，敬请批评指教！<br />
            建议：<br />
            &nbsp;&nbsp; 毕竟TOMCAT的框架还是比较复杂的， 单是从文字上理解， 是不那么容易掌握TOMCAT的框架的。 所以得实践、实践、再实践。 建议下载一份TOMCAT的源码， 调试通过， 然后单步跟踪其启动过程。 如果有不明白的地方， 再来查阅本文， 看是否能得到帮助。 我相信这样效果以及学习速度都会好很多！<br />
            &nbsp;&nbsp; <br />
            1. Tomcat的整体框架结构<br />
            &nbsp;&nbsp; Tomcat的基本框架， 分为4个层次。<br />
            &nbsp;&nbsp; Top Level Elements:<br />
            &nbsp;&nbsp;&nbsp; Server<br />
            &nbsp;&nbsp;&nbsp; Service&nbsp;&nbsp; <br />
            &nbsp;&nbsp; Connector<br />
            &nbsp;&nbsp;&nbsp; HTTP<br />
            &nbsp;&nbsp;&nbsp; AJP<br />
            &nbsp;&nbsp; Container<br />
            &nbsp;&nbsp; Engine<br />
            &nbsp;&nbsp;&nbsp;&nbsp; Host<br />
            &nbsp;&nbsp; Context<br />
            &nbsp;&nbsp; Component&nbsp; <br />
            &nbsp;&nbsp;&nbsp; manager<br />
            &nbsp;&nbsp; logger<br />
            &nbsp;&nbsp; loader<br />
            &nbsp;&nbsp; pipeline<br />
            &nbsp;&nbsp; valve<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
            &nbsp;&nbsp; 站在框架的顶层的是Server和Service<br />
            &nbsp;&nbsp; Server:&nbsp; 其实就是BackGroud程序， 在Tomcat里面的Server的用处是启动和监听服务端事件（诸如重启、关闭等命令。 在tomcat的标准配置文件：server.xml里面， 我们可以看到&#8220;&lt;Server port="8005" shutdown="SHUTDOWN" debug="0"&gt;&#8221;这里的"SHUTDOWN"就是server在监听服务端事件的时候所使用的命令字）<br />
            &nbsp;&nbsp; Service： 在tomcat里面， service是指一类问题的解决方案。&nbsp; 通常我们会默认使用tomcat提供的：Tomcat-Standalone 模式的service。 在这种方式下的service既给我们提供解析jsp和servlet的服务， 同时也提供给我们解析静态文本的服务。<br />
            &nbsp;&nbsp; <br />
            &nbsp;&nbsp; Connector: Tomcat都是在容器里面处理问题的， 而容器又到哪里去取得输入信息呢？<br />
            Connector就是专干这个的。 他会把从socket传递过来的数据， 封装成Request, 传递给容器来处理。<br />
            &nbsp;&nbsp; 通常我们会用到两种Connector,一种叫http connectoer， 用来传递http需求的。 另一种叫AJP， 在我们整合apache与tomcat工作的时候， apache与tomcat之间就是通过这个协议来互动的。 （说到apache与tomcat的整合工作， 通常我们的目的是为了让apache 获取静态资源， 而让tomcat来解析动态的jsp或者servlet。）<br />
            &nbsp;&nbsp; Container: 当http connector把需求传递给顶级的container: Engin的时候， 我们的视线就应该移动到Container这个层面来了。<br />
            &nbsp;&nbsp; 在Container这个层， 我们包含了3种容器： Engin, Host, Context.<br />
            &nbsp;&nbsp; Engin: 收到service传递过来的需求， 处理后， 将结果返回给service( service 是通过 connector 这个媒介来和Engin互动的 ).<br />
            &nbsp;&nbsp; Host: Engin收到service传递过来的需求后，不会自己处理， 而是交给合适的Host来处理。<br />
            Host在这里就是虚拟主机的意思， 通常我们都只会使用一个主机，既&#8220;localhost&#8221;本地机来处理。 <br />
            &nbsp;&nbsp; Context: Host接到了从Host传过来的需求后， 也不会自己处理， 而是交给合适的Context来处理。 <br />
            &nbsp;&nbsp; 比如： &lt;<a href="http://127.0.0.1:8080/foo/index.jsp">http://127.0.0.1:8080/foo/index.jsp</a>&gt;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;<a href="http://127.0.1:8080/bar/index.jsp">http://127.0.1:8080/bar/index.jsp</a>&gt;<br />
            &nbsp;&nbsp; 前者交给foo这个Context来处理， 后者交给bar这个Context来处理。<br />
            &nbsp;&nbsp; 很明显吧！ context的意思其实就是一个web app的意思。<br />
            &nbsp;&nbsp; 我们通常都会在server.xml里面做这样的配置<br />
            &nbsp;&nbsp; &lt;Context path="/foo" docBase="D:/project/foo/web" /&gt;<br />
            &nbsp;&nbsp; 这个context容器，就是用来干我们该干的事儿的地方的。<br />
            &nbsp;&nbsp; <br />
            &nbsp;&nbsp; Compenent: 接下来， 我们继续讲讲component是干什么用的。<br />
            &nbsp;&nbsp; 我们得先理解一下容器和组件的关系。<br />
            &nbsp;&nbsp; 需求被传递到了容器里面， 在合适的时候， 会传递给下一个容器处理。<br />
            &nbsp;&nbsp; 而容器里面又盛装着各种各样的组件， 我们可以理解为提供各种各样的增值服务。<br />
            &nbsp;&nbsp; manager: 当一个容器里面装了manager组件后，这个容器就支持session管理了， 事实上在tomcat里面的session管理， 就是靠的在context里面装的manager component.<br />
            &nbsp;&nbsp; logger: 当一个容器里面装了logger组件后， 这个容器里所发生的事情， 就被该组件记录下来啦！ 我们通常会在logs/ 这个目录下看见 catalina_log.time.txt 以及 localhost.time.txt 和localhost_examples_log.time.txt。 这就是因为我们分别为：engin, host以及context(examples)这三个容器安装了logger组件， 这也是默认安装， 又叫做标配 ：）<br />
            &nbsp;&nbsp; loader: loader这个组件通常只会给我们的context容器使用， loader是用来启动context以及管理这个context的classloader用的。<br />
            &nbsp;&nbsp;&nbsp; pipline: pipeline是这样一个东西， 当一个容器决定了要把从上级传递过来的需求交给子容器的时候， 他就把这个需求放进容器的管道(pipeline)里面去。 而需求傻呼呼得在管道里面流动的时候， 就会被管道里面的各个阀门拦截下来。 比如管道里面放了两个阀门。 第一个阀门叫做&#8220;access_allow_vavle&#8221;， 也就是说需求流过来的时候，它会看这个需求是哪个IP过来的， 如果这个IP已经在黑名单里面了， sure, 杀！ 第二个阀门叫做&#8220;defaul_access_valve&#8221;它会做例行的检查， 如果通过的话，OK， 把需求传递给当前容器的子容器。 就是通过这种方式， 需求就在各个容器里面传递，流动， 最后抵达目的地的了。<br />
            &nbsp;&nbsp;&nbsp; valve: 就是上面所说的阀门啦。<br />
            &nbsp;&nbsp; Tomcat里面大概就是这么些东西， 我们可以简单地这么理解tomcat的框架，它是一种自上而下， 容器里又包含子容器的这样一种结构。<br />
            2. Tomcat的启动流程<br />
            &nbsp;&nbsp; 这篇文章是讲tomcat怎么启动的，既然我们大体上了解了TOMCAT的框架结构了， 那么我们可以望文生意地就猜到tomcat的启动， 会先启动父容器，然后逐个启动里面的子容器。 启动每一个容器的时候， 都会启动安插在他身上的组件。 当所有的组件启动完毕， 所有的容器启动完毕的时候， tomcat本身也就启动完毕了。<br />
            &nbsp;&nbsp; 顺理成章地， 我们同样可以猜到， tomcat的启动会分成两大部分， 第一步是装配工作。 第二步是启动工作。 <br />
            &nbsp;&nbsp; 装配工作就是为父容器装上子容器， 为各个容器安插进组件的工作。 这个地方我们会用到digester模式， 至于digester模式什么， 有什么用， 怎么工作的. 请参考 &lt;<a href="http://software.ccidnet.com/pub/article/c322_a31671_p2.html">http://software.ccidnet.com/pub/article/c322_a31671_p2.html</a>&gt;<br />
            &nbsp;&nbsp; 启动工作是在装配工作之后， 一旦装配成功了， 我们就只需要点燃最上面的一根导线， 整个tomcat就会被激活起来。 这就好比我们要开一辆已经装配好了的汽车的时候一样，我们只要把钥匙插进钥匙孔，一拧，汽车的引擎就会发动起来，空调就会开起来， 安全装置就会生效， 如此一来，汽车整个就发动起来了。（这个过程确实和TOMCAT的启动过程不谋而和， 让我们不得不怀疑 TOMCAT的设计者是在GE做JAVA开发的）。<br />
            2.1 一些有意思的名称：<br />
            &nbsp;&nbsp; Catalina<br />
            &nbsp;&nbsp; Tomcat<br />
            &nbsp;&nbsp; Bootstrap<br />
            &nbsp;&nbsp; Engin<br />
            &nbsp;&nbsp; Host<br />
            &nbsp;&nbsp; Context<br />
            &nbsp;&nbsp; 他们的意思很有意思：<br />
            &nbsp;&nbsp; Catalina: 远程轰炸机<br />
            &nbsp;&nbsp; Tomcat: 熊猫轰炸机 -- 轰炸机的一种（这让我想起了让国人引以为豪的熊猫手机，是不是英文可以叫做tomcat??? ， 又让我想起了另一则广告： 波导-手机中的战斗机、波音-客机中的战斗机 ）<br />
            &nbsp;&nbsp; Bootstap: 引导<br />
            &nbsp;&nbsp; Engin: 发动机<br />
            &nbsp;&nbsp; Host: 主机，领土<br />
            &nbsp;&nbsp; Context: 内容， 目标， 上下文<br />
            &nbsp;&nbsp; <br />
            &nbsp;&nbsp; ... 在许多许多年后， 现代人类已经灭绝。 后现代生物发现了这些单词零落零落在一块。 一个自以为聪明的家伙把这些东西翻译出来了： <br />
            &nbsp;&nbsp; 在地勤人员的引导(bootstrap)下， 一架轰炸架(catalina)腾空跃起， 远看是熊猫轰炸机(tomcat)， 近看还是熊猫轰炸机！ 凭借着优秀的发动机技术(engin)， 这架熊猫轰炸机飞临了敌国的领土上空(host)， 对准目标(context)投下了毁天灭地的核弹头，波~ 现代生物就这么隔屁了~<br />
            &nbsp;<br />
            &nbsp;&nbsp; 综上所述， 这又不得不让人联想到GE是不是也参与了军事设备的生产呢？<br />
            &nbsp;&nbsp; 反对美帝国主义！ 反对美霸权主义！ 和平万岁！ 自由万岁！<br />
            &nbsp;&nbsp; <br />
            2.2&nbsp; 历史就是那么惊人的相似！ tomcat的启动就是从org.apache.catalina.startup.Bootstrap这个类悍然启动的！<br />
            &nbsp;&nbsp; 在Bootstrap里做了两件事：<br />
            &nbsp;&nbsp; 1. 指定了3种类型classloader:<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; commonLoader: common/classes、common/lib、common/endorsed<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catalinaLoader: server/classes、server/lib、commonLoader<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sharedLoader：&nbsp; shared/classes、shared/lib、commonLoader<br />
            &nbsp;&nbsp; 2. 引导Catalina的启动。<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 用Reflection技术调用org.apache.catalina.startup.Catalina的process方法， 并传递参数过去。<br />
            &nbsp;&nbsp; <br />
            2.3 Catalina.java<br />
            &nbsp;&nbsp; Catalina完成了几个重要的任务：<br />
            &nbsp;&nbsp; 1. 使用Digester技术装配tomcat各个容器与组件。<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.1&nbsp;装配工作的主要内容是安装各个大件。 比如server下有什么样的servcie。 Host会容纳多少个context。 Context都会使用到哪些组件等等。 <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.2&nbsp;同时呢， 在装配工作这一步， 还完成了mbeans的配置工作。 在这里，我简单地但不十分精确地描述一下mbean是什么，干什么用的。<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们自己生成的对象， 自己管理， 天经地义！ 但是如果我们创建了对象了， 想让别人来管， 怎么办呢？ 我想至少得告诉别人我们都有什么， 以及通过什么方法可以找到&nbsp; 吧！ JMX技术给我们提供了一种手段。 JMX里面主要有3种东西。Mbean, agent, connector.<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Mbean： 用来映射我们的对象。也许mbean就是我们创建的对象， 也许不是， 但有了它， 就可以引用到我们的对象了。<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Agent:&nbsp; 通过它， 就可以找到mbean了。<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Connector: 连接Agent的方式。 可以是http的， 也可以是rmi的，还可以直接通过socket。<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 发生在tomcat 装配过程中的事情:&nbsp; GlobalResourcesLifecycleListener 类的初始化会被触发：<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected static Registry registry = MBeanUtils.createRegistry();&nbsp; 会运行<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MBeanUtils.createRegistry()&nbsp; 会依据/org/apache/catalina/mbeans/mbeans-descriptors.xml这个配置文件创建 mbeans. Ok, 外界就有了条途径访问tomcat中的各个组件了。（有点像后门儿）<br />
            &nbsp;&nbsp; 2. 为top level 的server 做初始化工作。 实际上就是做通常会配置给service的两条connector.(http, ajp)<br />
            &nbsp;&nbsp; 3. 从server这个容器开始启动， 点燃整个tomcat.<br />
            &nbsp;&nbsp; 4. 为server做一个hook程序， 检测当server shutdown的时候， 关闭tomcat的各个容器用。<br />
            &nbsp;&nbsp; 5. 监听8005端口， 如果发送"SHUTDOWN"（默认培植下字符串）过来， 关闭8005serverSocket。<br />
            2.4 启动各个容器<br />
            &nbsp;&nbsp; 1. Server<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 触发Server容器启动前(before_start)， 启动中(start)， 启动后(after_start)3个事件， 并运行相应的事件处理器。<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 启动Server的子容器：Servcie. <br />
            &nbsp;&nbsp; 2. Service<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 启动Service的子容器：Engin<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 启动Connector<br />
            &nbsp;&nbsp; 3. Engin<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 到了Engin这个层次，以及以下级别的容器， Tomcat就使用了比较一致的启动方式了。<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 首先，&nbsp; 运行各个容器自己特有一些任务<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 随后，&nbsp; 触发启动前事件<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 立即，&nbsp; 设置标签，就表示该容器已经启动<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 接着，&nbsp; 启动容器中的各个组件： loader, logger, manager等等<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 再接着，启动mapping组件。（注1）<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 紧跟着，启动子容器。<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 接下来，启动该容器的管道(pipline)<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 然后，&nbsp; 触发启动中事件<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 最后，&nbsp; 触发启动后事件。<br />
            &nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Engin大致会这么做， Host大致也会这么做， Context大致还是会这么做。 那么很显然地， 我们需要在这里使用到代码复用的技术。 tomcat在处理这个问题的时候， 漂亮地使用了抽象类来处理。 ContainerBase. 最后使得这部分完成复杂功能的代码显得干净利落， 干练爽快， 实在是令人觉得叹为观止， 细细品来， 直觉如享佳珍， 另人齿颊留香， 留恋往返啊！<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Engin的触发启动前事件里， 会激活绑定在Engin上的唯一一个Listener：EnginConfig。<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个EnginConfig类基本上没有做什么事情， 就是把EnginConfig的调试级别设置为和Engin相当。 另外就是输出几行文本， 表示Engin已经配置完毕， 并没有做什么实质性的工作。<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注1: mapping组件的用处是， 当一个需求将要从父容器传递到子容器的时候， 而父容器又有多个子容器的话， 那么应该选择哪个子容器来处理需求呢？ 这个由mapping 组件来定夺。<br />
            &nbsp;&nbsp;&nbsp; <br />
            &nbsp;&nbsp; 4. Host<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 同Engin一样， 也是调用ContainerBase里面的start()方法， 不过之前做了些自个儿的任务,就是往Host这个容器的通道（pipline）里面， 安装了一个叫做<br />
            &nbsp;&#8220;org.apache.catalina.valves.ErrorReportValve&#8221;的阀门。<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个阀门的用处是这样的：&nbsp; 需求在被Engin传递给Host后， 会继续传递给Context做具体的处理。 这里需求其实就是作为参数传递的Request, Response。 所以在context把需求处理完后， 通常会改动response。 而这个org.apache.catalina.valves.ErrorReportValve的作用就是检察response是否包含错误， 如果有就做相应的处理。<br />
            &nbsp;&nbsp; 5. Context<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 到了这里， 就终于轮到了tomcat启动中真正的重头戏，启动Context了。<br />
            &nbsp;StandardContext.start() 这个启动Context容器的方法被StandardHost调用.<br />
            &nbsp;5.1 webappResources 该context所指向的具体目录<br />
            &nbsp;5.2 安装defaultContex, DefaultContext 就是默认Context。 如果我们在一个Host下面安装了DefaultContext，而且defaultContext里面又安装了一个数据库连接池资源的话。 那么其他所有的在该Host下的Context, 都可以直接使用这个数据库连接池， 而不用格外做配置了。<br />
            &nbsp; 5.3 指定Loader. 通常用默认的org.apache.catalina.loader.WebappLoader这个类。&nbsp;&nbsp; Loader就是用来指定这个context会用到哪些类啊， 哪些jar包啊这些什么的。<br />
            &nbsp;5.4 指定 Manager. 通常使用默认的org.apache.catalina.session. StandardManager 。 Manager是用来管理session的。<br />
            &nbsp;&nbsp;&nbsp;&nbsp; 其实session的管理也很好实现。 以一种简单的session管理为例。 当需求传递过来的时候， 在Request对象里面有一个sessionId 属性。 OK， 得到这个sessionId后， 我们就可以把它作为map的key，而value我们可以放置一个HashMap. HashMap里边儿， 再放我们想放的东西。<br />
            &nbsp;5.5 postWorkDirectory (). Tomcat下面有一个work目录。 我们把临时文件都扔在那儿去。 这个步骤就是在那里创建一个目录。 一般说来会在%CATALINA_HOME%/work/Standalone\localhost\ 这个地方生成一个目录。<br />
            5.6&nbsp; Binding thread。到了这里， 就应该发生 class Loader 互换了。 之前是看得见tomcat下面所有的class和lib. 接下来需要看得见当前context下的class。 所以要设置contextClassLoader, 同时还要把旧的ClassLoader记录下来，因为以后还要用的。<br />
            5.7&nbsp; 启动 Loader. 指定这个Context具体要使用哪些classes， 用到哪些jar文件。 如果reloadable设置成了true, 就会启动一个线程来监视classes的变化， 如果有变化就重新启动Context。<br />
            5.8&nbsp; 启动logger<br />
            5.9&nbsp; 触发安装在它身上的一个监听器。<br />
            &nbsp;lifecycle.fireLifecycleEvent(START_EVENT, null); <br />
            &nbsp;作为监听器之一，ContextConfig会被启动. ContextConfig就是用来配置web.xml的。 比如这个Context有多少Servlet， 又有多少Filter， 就是在这里给Context装上去的。<br />
            &nbsp;5.9.1 defaultConfig. 每个context都得配置 tomcat/conf/web.xml 这个文件。<br />
            &nbsp;5.9.2 applicationConfig 配置自己的 WEB-INF/web.xml 文件<br />
            5.9.3 validateSecurityRoles 权限验证。 通常我们在访问/admin 或者/manager的时候，需要用户要么是admin的要么是manager的， 才能访问。 而且我们还可以限制那些资源可以访问， 而哪些不能。 都是在这里实现的。<br />
            5.9.4 tldScan: 扫描一下， 需要用到哪些标签(tag lab)<br />
            5.10 启动 manager<br />
            5.11 postWelcomeFiles() 我们通常会用到的3个启动文件的名称：<br />
            index.html、index.htm、index.jsp 就被默认地绑在了这个context上<br />
            &nbsp;5.12 listenerStart 配置listener<br />
            &nbsp;5.13 filterStart 配置 filter<br />
            &nbsp;5.14 启动带有&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;的Servlet.<br />
            &nbsp; 顺序是从小到大： 1,2,3&#8230; 最后是0<br />
            &nbsp; 默认情况下， 至少会启动如下3个的Servlet: <br />
            &nbsp; org.apache.catalina.servlets.DefaultServlet&nbsp;&nbsp; <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 处理静态资源的Servlet. 什么图片啊， html啊， css啊， js啊都找他<br />
            &nbsp; org.apache.catalina.servlets.InvokerServlet<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 处理没有做Servlet Mapping的那些Servlet.<br />
            &nbsp; org.apache.jasper.servlet.JspServlet <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 处理JSP文件的.<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.15&nbsp; 标识context已经启动完毕。<br />
            &nbsp;走了多少个步骤啊， Context总算是启动完毕喽。<br />
            &nbsp;&nbsp;&nbsp; OK! 走到了这里， 每个容器以及组件都启动完毕。 Tomcat终于不辞辛劳地为人民服务了！<br />
            3. 参考文献：<br />
            &nbsp;&nbsp;&nbsp; &lt;<a href="http://jakarta.apache.org/tomcat/">http://jakarta.apache.org/tomcat/</a>&gt;<br />
            &nbsp;&nbsp;&nbsp; &lt;<a href="http://www.onjava.com/pub/a/onjava/2003/05/14/java_webserver.html">http://www.onjava.com/pub/a/onjava/2003/05/14/java_webserver.html</a>&gt;<br />
            &nbsp;&nbsp;&nbsp; <br />
            4. 后记<br />
            &nbsp;&nbsp;&nbsp; 这篇文章是讲解tomcat启动框架的，还有篇文章是讲解TOMCAT里面的消息处理流程的细节的。 文章内容已经写好了， 现在正在整理阶段。 相信很快就可以做出来， 大家共同研究共同进步。<br />
            &nbsp;&nbsp;&nbsp; 这篇文章是独自分析TOMCAT源码所写的， 所以一定有地方是带有个人主观色彩， 难免会有片面之处。若有不当之处敬请批评指教，这样不仅可以使刚开始研究TOMCAT的兄弟们少走弯路， 我也可以学到东西。<br />
            &nbsp;&nbsp;&nbsp; email: <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#111;&#106;&#97;&#110;&#95;&#106;&#97;&#118;&#97;&#64;&#121;&#97;&#104;&#111;&#111;&#46;&#99;&#111;&#109;&#46;&#99;&#110;">sojan_java@yahoo.com.cn</a></p>
            <p>5. <a href="http://www.csdn.net/develop/article/28/28075.shtm">tomcat源码分析(消息处理)</a></p>
            </span></td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/lingy/aggbug/292715.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lingy/" target="_blank">林光炎</a> 2009-08-26 20:49 <a href="http://www.blogjava.net/lingy/archive/2009/08/26/292715.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Digester简化XML文档处理</title><link>http://www.blogjava.net/lingy/archive/2009/08/26/292713.html</link><dc:creator>林光炎</dc:creator><author>林光炎</author><pubDate>Wed, 26 Aug 2009 12:27:00 GMT</pubDate><guid>http://www.blogjava.net/lingy/archive/2009/08/26/292713.html</guid><description><![CDATA[<span class="a14c" id="zoom">&nbsp;
<p style="text-indent: 2em">Digester框架属于Jakarta Commons，它以规则和模式为基础处理XML文档。与SAX和DOM之类的标准API相比，Digester不涉及太多的细节问题，非常适合于对XML文档进行简单的处理。
<p style="text-indent: 2em">在Java和XML开发中，一个常见的任务是把XML文档转换成对应的Java Bean对象的层次结构。人们经常用标准的SAX和DOM API来完成这个任务。虽然这两种API都很强大和灵活，但对于某些简单的任务来说，它们显得操作层次太低，也就是说，涉及了太多的细节问题。Jakarta Digester框架能够很好地满足这类场合的需要。
<p style="text-indent: 2em">
<center><font color="#000099"><strong>Digester框架简介 </strong></font></center>
<p style="text-indent: 2em">Jakarta的Digester框架从Struts框架发展而来，原先被用来处理struts-config.xml配置文件，但很快人们认识到它有着更广泛的用途，把它转入了Jakarta Commons项目。Jakarta Commons的目标是提供一个&#8220;可重用Java组件的仓库&#8221;。Digester最新的版本是1.3，于2002年8月13日发布。
<p style="text-indent: 2em">Digester框架允许开发者指定一组动作，当解析器在XML文档中发现某些特定的简单模式时动作被执行。Digester框架带有10个预定义的规则（Rule），涵盖了unmarshalling XML（例如创建Bean或设置Bean属性）的大多数需求（ marshalling的原意是指&#8220;配制整齐，编组列车&#8221;，marshalling是在内存中为Java对象生成XML描述文档的过程，而unmarshalling是指把XML形式的描述转换到可用Java代码操作的对象的过程，我们称之为&#8220;反配制&#8221;），但必要时用户可以定义和实现自己的规则。
<p style="text-indent: 2em">在本文的例子中，我们将反配制下面这个XML文档：
<p style="text-indent: 2em"><ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre><ccid_code>&lt;?xml version="1.0"?&gt;
            &lt;catalog library="somewhere"&gt;
            &lt;book&gt;
            &lt;author&gt;Author 1&lt;/author&gt;
            &lt;title&gt;Title 1&lt;/title&gt;
            &lt;/book&gt;
            &lt;book&gt;
            &lt;author&gt;Author 2&lt;/author&gt;
            &lt;title&gt;His One Book&lt;/title&gt;
            &lt;/book&gt;
            &lt;magazine&gt;
            &lt;name&gt;Mag Title 1&lt;/name&gt;
            &lt;article page="5"&gt;
            &lt;headline&gt;Some Headline&lt;/headline&gt;
            &lt;/article&gt;
            &lt;article page="9"&gt;
            &lt;headline&gt;Another Headline&lt;/headline&gt;
            &lt;/article&gt;
            &lt;/magazine&gt;
            &lt;book&gt;
            &lt;author&gt;Author 2&lt;/author&gt;
            &lt;title&gt;His Other Book&lt;/title&gt;
            &lt;/book&gt;
            &lt;magazine&gt;
            &lt;name&gt;Mag Title 2&lt;/name&gt;
            &lt;article page="17"&gt;
            &lt;headline&gt;Second Headline&lt;/headline&gt;
            &lt;/article&gt;
            &lt;/magazine&gt;
            &lt;/catalog&gt;</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</ccid_nobr>
<p style="text-indent: 2em">下面是Bean的代码。注意使用Digester框架时，Bean类必须定义成public。
<p style="text-indent: 2em"><ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre><ccid_code>import java.util.Vector;
            public class Catalog {
            private Vector books;
            private Vector magazines;
            public Catalog() {
            books = new Vector();
            magazines = new Vector();
            }
            public void addBook( Book rhs ) {
            books.addElement( rhs );
            }
            public void addMagazine( Magazine rhs ) {
            magazines.addElement( rhs );
            }
            public String toString() {
            String newline = System.getProperty( "line.separator" );
            StringBuffer buf = new StringBuffer();
            buf.append( "--- Books ---" ).append( newline );
            for( int i=0; i&lt;books.size(); i++ ){
            buf.append( books.elementAt(i) ).append( newline );
            }
            buf.append( "--- Magazines ---" ).append( newline );
            for( int i=0; i&lt;magazines.size(); i++ ){
            buf.append( magazines.elementAt(i) ).append( newline );
            }
            return buf.toString();
            }
            }
            //===================================================
            public class Book {
            private String author;
            private String title;
            public Book() {}
            public void setAuthor( String rhs ) { author = rhs; }
            public void setTitle(  String rhs ) { title  = rhs; }
            public String toString() {
            return "Book: Author='" + author + "' Title='" + title + "'";
            }
            }
            //===================================================
            import java.util.Vector;
            public class Magazine {
            private String name;
            private Vector articles;
            public Magazine() {
            articles = new Vector();
            }
            public void setName( String rhs ) { name = rhs; }
            public void addArticle( Article a ) {
            articles.addElement( a );
            }
            public String toString() {
            StringBuffer buf = new StringBuffer( "Magazine: Name='" + name + "' ");
            for( int i=0; i&lt;articles.size(); i++ ){
            buf.append( articles.elementAt(i).toString() );
            }
            return buf.toString();
            }
            }
            //===================================================
            public class Article {
            private String headline;
            private String page;
            public Article() {}
            public void setHeadline( String rhs ) { headline = rhs; }
            public void setPage(     String rhs ) { page     = rhs; }
            public String toString() {
            return "Article: Headline='" + headline + "' on page='" + page + "' ";
            }
            }</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</ccid_nobr>
<p align="center"><font color="#ff0000">1</font> <a class="content01" href="http://software.ccidnet.com/art/322/20021125/31671_2.html">2</a> <span class="content01"><a class="content01" href="http://software.ccidnet.com/art/322/20021125/31671_2.html">下一页&gt;&gt;</a></span> <br />
</p>
<p style="text-indent: 2em">Digester框架以模式（Pattern）和规则（Rule）为基础处理输入的XML。模式必须与XML元素匹配，包括其名字和在文档树内的位置。描述匹配模式的语法类似于XPath匹配模式，例如：catalog模式匹配顶层的<ccid_code>&lt;catalog&gt;元素，catalog/book模式匹配直接嵌套在&lt;catalog&gt;元素内的&lt;book&gt;元素（但不匹配文档内其他位置的&lt;book&gt;元素）</ccid_code>。
<p style="text-indent: 2em">所有的模式都必须指定其完整名称——从根元素开始的完整路径。唯一的例外是包含通配符（&#8220;*&#8221;）的模式，例如*/name模式匹配XML文档内任何位置的<ccid_code>&lt;name&gt;</ccid_code>元素。但是根元素不必特别指出，因为所有的路径都是从根元素开始的绝对路径。
<p style="text-indent: 2em">当Digester发现一个指定的模式，它就执行关联的任务。由此可见，Digester框架显然与SAX解析器有着密切的关系（实际上，Digester类实现了org.xml.sax.ContentHandler，并维护着解析栈）。所有在Digester中使用的规则必须扩展org.apache.commons.digester.Rule，后者本身提供了一些类似于SAX的ContentHandler回调函数的方法。例如，当遇到匹配元素的开始标记和结束标记时，begin()方法和end()方法将分别被调用。
<p style="text-indent: 2em">一旦遇到匹配元素的内容，body()方法被调用；最后被调用的方法是finish()，这个方法在匹配元素的结束标记处理完毕之后被调用，用来执行可能需要的事后清理任务。然而，大多数时候我们不必关注这些方法，因为框架提供的标准规则很可能已经提供了所有必需的功能。
<p style="text-indent: 2em">要反配制一个文档，首先创建一个org.apache.commons.digester.Digester类的实例，如果必要的话，进行一些配置操作，指定必需的模式和规则，最后向parse()方法传递一个XML文件的引用。下面的DigesterDriver示范了这一处理过程（必须在命令行上指定输入XML文档的名称）。
<p style="text-indent: 2em"><ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre><ccid_code>import org.apache.commons.digester.*;
            import java.io.*;
            import java.util.*;
            public class DigesterDriver {
            public static void main( String[] args ) {
            try {
            Digester digester = new Digester();
            digester.setValidating( false );
            digester.addObjectCreate( "catalog", Catalog.class );
            digester.addObjectCreate( "catalog/book", Book.class );
            digester.addBeanPropertySetter( "catalog/book/author", "author" );
            digester.addBeanPropertySetter( "catalog/book/title", "title" );
            digester.addSetNext( "catalog/book", "addBook" );
            digester.addObjectCreate( "catalog/magazine", Magazine.class );
            digester.addBeanPropertySetter( "catalog/magazine/name", "name" );
            digester.addObjectCreate( "catalog/magazine/article", Article.class );
            digester.addSetProperties( "catalog/magazine/article", "page", "page" );
            digester.addBeanPropertySetter( "catalog/magazine/article/headline" );
            digester.addSetNext( "catalog/magazine/article", "addArticle" );
            digester.addSetNext( "catalog/magazine", "addMagazine" );
            File input = new File( args[0] );
            Catalog c = (Catalog)digester.parse( input );
            System.out.println( c.toString() );
            } catch( Exception exc ) {
            exc.printStackTrace();
            }
            }
            }</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</ccid_nobr>
<p style="text-indent: 2em">在上面的代码中，我们首先创建了Digester类的一个实例digester，然后指定它不要用DTD验证XML文档的合法性——这是因为我们没有为XML文档定义DTD。接下来，我们指定了模式和关联的规则：ObjectCreateRule创建指定类的一个实例，并将它压入解析栈。SetPropertiesRule把Bean属性设置成当前XML元素的属性值——规则的第一个参数是XML属性的名称，第二个参数是Bean属性的名称。
<p style="text-indent: 2em">SetPropertiesRule获取的是XML属性的值，而BeanPropertySetterRule获取的是位于当前元素内的原始字符数据值。使用BeanPropertySetterRule时不必指定要设置的Bean属性名字，默认是当前XML元素的名称。在上面的例子中，在匹配catalog/magazine/article/headline模式的规则定义中使用的就是默认值。最后，SetNextRule弹出解析栈顶部的对象，并把该对象传递给它下面对象的指定名称的方法——通常用来把一个配置完毕的Bean插入父对象。
<p style="text-indent: 2em">注意，我们可以为同一个模式注册多个规则。如果注册了多个规则，则这些规则按照它们被加入到Digester的次序执行，例如，如果要处理catalog/magazine/article的<article>元素，我们首先创建合适的article Bean，然后设置page属性，最后弹出完成后的article Bean，并把它插入magazine。
<p style="text-indent: 2em">
<center><font color="#000099"><strong>调用任意方法 </strong></font></center>
<p style="text-indent: 2em">我们不仅可以设置Bean的属性，而且还可以调用堆栈内对象的任意方法。这通过CallMethodRule完成，我们只需指定方法名字，如有必要，再说明调用的参数类型和数量。CallParamRule用来定义传递给被调用函数的参数值，参数值可以从当前XML元素的命名的属性获取，也可以从当前元素包含的原始字符数据获取。例如，在前面实现DigesterDriver的例子中，我们可以不用BeanPropertySetterRule，而是通过显式调用属性的set方法达到同样的目的：
<p style="text-indent: 2em"><ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre><ccid_code>digester.addCallMethod( "catalog/book/author", "setAuthor", 1 );
            digester.addCallParam( "catalog/book/author", 0 );</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</ccid_nobr>
<p style="text-indent: 2em">上面的第一行代码给出了要调用的方法（即setAuthor()），以及该调用需要的参数数量（即1）。第二行代码的意思是从<author>元素包含的字符数据获取函数参数的值，把它作为参数数组的第一个传入（即索引是0的数组元素）。如果我们指定了XML元素属性的名称（例如digester.addCallParam( "catalog/book/author", 0, "author" );），则参数值将从当前元素的相应属性值获取。
<p style="text-indent: 2em">这里必须注意的是，&#8220;digester.addCallMethod( "pattern", "methodName", 0 );&#8221;这个语句不是指定了一个不带参数的方法调用，而是指定了带有一个参数的方法调用，它的值就是当前XML元素的字符数据！这样，我们又有了另一种替代BeanPropertySetterRule的办法：
<p style="text-indent: 2em"><ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre><ccid_code>digester.addCallMethod( "catalog/book/author", "setAuthor", 0 );</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</ccid_nobr>
<p style="text-indent: 2em">如果要调用一个确实没有参数的方法，必须采用如下形式：digester.addCallMethod( "pattern", "methodName" );。
<p style="text-indent: 2em">
<center><font color="#000099"><strong>标准规则概要 </strong></font></center>
<p style="text-indent: 2em">下面简要说明所有标准规则。
<p style="text-indent: 2em"><strong>创建 </strong>
<p style="text-indent: 2em">ObjectCreateRule：利用指定类的默认构造函数，创建该类的一个对象，并把对象压入栈。当元素处理结束时，对象被弹出。被实例化的类可通过class对象或类的全称给出。
<p style="text-indent: 2em">FactoryCreateRule：利用指定的工厂类创建一个对象，把对象压入栈。对于没有提供默认构造函数的类，这一规则很有用。用于该规则的工厂类必须实现org.apache.commons.digester.ObjectCreationFactory接口。
<p style="text-indent: 2em"><strong>设置属性 </strong>
<p style="text-indent: 2em">SetPropertiesRule：利用指定名称的XML元素属性值，设置顶层Bean的一个或者多个指定名称的属性。XML元素的属性名称和Bean的属性名称以String[]数组形式传入该规则（通常用来处理<article page="10">之类的结构）。
<p style="text-indent: 2em">BeanPropertySetterRule：把顶层Bean的指定名称的属性设置成当前XML元素包含的字符数据。（通常用来处理<ccid_code>&lt;page&gt;10&lt;/page&gt;</ccid_code>之类的结构）。
<p style="text-indent: 2em">SetPropertyRule：设置顶层Bean的一个属性。无论是Bean属性的名称，还是赋予该属性的值，都在当前XML元素中以属性的形式指定，例如：<ccid_code>&lt;article key="page" value="10" /&gt;</ccid_code>。
<p style="text-indent: 2em"><strong>管理父/子关系 </strong>
<p style="text-indent: 2em">SetNextRule：弹出栈顶的对象，把它传递给紧接其下的另一个对象的指定名称的方法。通常用来把一个已经初始化的Bean插入到父对象。
<p style="text-indent: 2em">SetTopRule：把栈里面上数第二的对象传递给顶层的对象。当子对象提供了一个setParenet方法时，这一规则很有用。
<p style="text-indent: 2em">SetRootRule：调用栈底对象的一个方法，并把栈顶的对象作为参数传入。
<p style="text-indent: 2em"><strong>调用任意方法 </strong>
<p style="text-indent: 2em">CallMethodRule：调用顶层Bean的指定名称的方法。被调用的方法可以有任意多个参数，参数的值通过后继的CallParamRule给出。
<p style="text-indent: 2em">CallParamRule：表示方法调用的参数。参数的值或者取自指定名称的XML元素的属性，或者是当前元素包含的原始字符数据。这个规则要求用一个整数指定它在参数列表中的位置。
<p style="text-indent: 2em">
<center><font color="#000099"><strong>通过XML指定规则 </strong></font></center>
<p style="text-indent: 2em">在前面的内容中，我们用程序代码的方式指定模式和规则，这些模式和规则都是在编译的时候就已经确定，虽然从概念上来讲比较简单，但却不能说尽善尽美：Digester框架的总体目标是在运行时识别和处理各种数据结构，但如果我们用编程的方法指定模式和规则，则所有行为在编译时已经固定！如果Java源程序中包含了大量固定的字符串，通常意味着程序在执行某些配置操作，这部分操作可以被（或许是应该被）延迟到运行时进行。
<p style="text-indent: 2em">org.apache.commons.digester.xmlrules包解决了这个问题。这个包提供了一个DigesterLoader类，它能够从XML文档读取模式/规则对，返回配置好的Digester对象。用来配置Digester对象的XML文档必须遵从digester-rules.dtd，这个DTD是xmlrules包的一部分。
<p style="text-indent: 2em">下面就是本文例子的配置文件rules.xml。有几点必须说明。
<p style="text-indent: 2em">首先，模式可以用两种方式指定：或者使用<ccid_code>&lt;pattern&gt;</ccid_code>元素，或者通过代表规则的XML元素的属性。这两种办法可以混合使用，且<ccid_code>&lt;pattern&gt;</ccid_code>元素是可以嵌套的。其次，<ccid_code>&lt;alias&gt;元素和&lt;set-properties-rule&gt;</ccid_code>一起使用，用来把XML属性映射到Bean属性。最后，就当前发行的Digester软件包而言，我们不能在配置文件中指定BeanPropertySetterRule，正如前面所介绍的，我们用CallMethodRule来达到同样的目标。
<p style="text-indent: 2em"><ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre><ccid_code>&lt;?xml version="1.0"?&gt;
            &lt;digester-rules&gt;
            &lt;object-create-rule pattern="catalog" classname="Catalog" /&gt;
            &lt;set-properties-rule pattern="catalog" &gt;
            &lt;alias attr-name="library" prop-name="library" /&gt;
            &lt;/set-properties-rule&gt;
            &lt;pattern value="catalog/book"&gt;
            &lt;object-create-rule classname="Book" /&gt;
            &lt;call-method-rule pattern="author" methodname="setAuthor"
            paramcount="0" /&gt;
            &lt;call-method-rule pattern="title" methodname="setTitle"
            paramcount="0" /&gt;
            &lt;set-next-rule methodname="addBook" /&gt;
            &lt;/pattern&gt;
            &lt;pattern value="catalog/magazine"&gt;
            &lt;object-create-rule classname="Magazine" /&gt;
            &lt;call-method-rule pattern="name" methodname="setName" paramcount="0" /&gt;
            &lt;pattern value="article"&gt;
            &lt;object-create-rule classname="Article" /&gt;
            &lt;set-properties-rule&gt;
            &lt;alias attr-name="page" prop-name="page" /&gt;
            &lt;/set-properties-rule&gt;
            &lt;call-method-rule pattern="headline" methodname="setHeadline"
            paramcount="0" /&gt;
            &lt;set-next-rule methodname="addArticle" /&gt;
            &lt;/pattern&gt;
            &lt;set-next-rule methodname="addMagazine" /&gt;
            &lt;/pattern&gt;
            &lt;/digester-rules&gt;</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</ccid_nobr>
<p style="text-indent: 2em">现在，所有实际的操作都转移到了Digester和DigesterLoader类，XmlRulesDriver类就变得相当简单。运行下面的XmlRulesDriver时，在第一个命令行参数中指定目录文档的名字，在第二个参数中指定rules.xml（注意，DigesterLoader不是从File或者org.xml.sax.InputSource读取rules.xml文件，而是要求指定一个URL，因此，下面代码中File引用被转换成了等价的URL）。
<p style="text-indent: 2em"><ccid_nobr>
<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="black" border="1">
    <tbody>
        <tr>
            <td class="code" bgcolor="#e6e6e6">
            <pre><ccid_code>import org.apache.commons.digester.*;
            import org.apache.commons.digester.xmlrules.*;
            import java.io.*;
            import java.util.*;
            public class XmlRulesDriver {
            public static void main( String[] args ) {
            try {
            File input = new File( args[0] );
            File rules = new File( args[1] );
            Digester digester = DigesterLoader.createDigester( rules.toURL() );
            Catalog catalog = (Catalog)digester.parse( input );
            System.out.println( catalog.toString() );
            } catch( Exception exc ) {
            exc.printStackTrace();
            }
            }
            }</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</ccid_nobr>
<p style="text-indent: 2em">结束语：本文对Jakarta Commons Digester的介绍就到这里结束。当然，还有许多内容这里尚未涉及。其中一个在这里忽略的主题是XML名称空间：Digester允许把规则定义成只能对某一个名称空间内定义的元素起作用。
<p style="text-indent: 2em">另外，我们简单地提及了通过扩展Rule类开发定制规则的问题。按照习惯，Digester类提供了push()、peek()和pop()方法，使得开发者能够自由地直接操作解析栈。
<p style="text-indent: 2em"><strong>参考：</strong>
<p style="text-indent: 2em">Jakarta Commons Digester Homepage
<p style="text-indent: 2em">Jakarta Struts Homepage </p>
</span>
<img src ="http://www.blogjava.net/lingy/aggbug/292713.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lingy/" target="_blank">林光炎</a> 2009-08-26 20:27 <a href="http://www.blogjava.net/lingy/archive/2009/08/26/292713.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ThreadLocal与synchronized </title><link>http://www.blogjava.net/lingy/archive/2009/08/25/292559.html</link><dc:creator>林光炎</dc:creator><author>林光炎</author><pubDate>Tue, 25 Aug 2009 12:36:00 GMT</pubDate><guid>http://www.blogjava.net/lingy/archive/2009/08/25/292559.html</guid><description><![CDATA[<p>相关文章:&nbsp;&nbsp; <br />
正确理解ThreadLocal <br />
ThreadLocal与synchronized </p>
<p>推荐圈子: Pipboy <br />
更多相关推荐 <br />
昨天上Java版块逛了一圈，一个2万5千人浏览的帖子引起了偶滴注意 ThreadLocal与synchronized ，9页以上的回复，足见大家对这个问题的兴趣。 </p>
<p>老实说，从看到这个帖子的题目开始，就觉得帖子的作者估计是在概念上有所混淆了,于是乎想写个咚咚，同大家分享一下自己的心得。 </p>
<p>帖子上，讨论的人很多，高手不乏，各抒己见，但不知新手们看明白没有，因此，这里偶以最简洁列表方式来说一说相关问题。 </p>
<p>1．区别ThreadLocal 与 synchronized </p>
<p>ThreadLocal是一个线程隔离(或者说是线程安全)的变量存储的管理实体（注意：不是存储用的），它以Java类方式表现； <br />
synchronized是Java的一个保留字，只是一个代码标识符，它依靠JVM的锁机制来实现临界区的函数、变量在CPU运行访问中的原子性。 <br />
两者的性质、表现及设计初衷不同，因此没有可比较性。 </p>
<p>2.理解ThreadLocal中提到的变量副本 <br />
事实上，我们向ThreadLocal中set的变量不是由ThreadLocal来存储的，而是Thread线程对象自身保存。当用户调用ThreadLocal对象的set(Object o)时，该方法则通过Thread.currentThread()获取当前线程，将变量存入Thread中的一个Map内，而Map的Key就是当前的ThreadLocal实例。请看源码，这是最主要的两个函数，能看出ThreadLocal与Thread的调用关系： </p>
<p>Java代码 <br />
public void set(T value) {&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread t = Thread.currentThread();&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ThreadLocalMap map = getMap(t);&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (map != null)&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; map.set(this, value);&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createMap(t, value);&nbsp;&nbsp;&nbsp; <br />
}&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp; <br />
ThreadLocalMap getMap(Thread t) {&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return t.threadLocals;&nbsp;&nbsp;&nbsp; <br />
}&nbsp;&nbsp; <br />
public void set(T value) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread t = Thread.currentThread();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ThreadLocalMap map = getMap(t);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (map != null)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; map.set(this, value);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createMap(t, value);<br />
}</p>
<p>ThreadLocalMap getMap(Thread t) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return t.threadLocals;<br />
}</p>
<p>（有兴趣的朋友可以阅读Java的ThreadLocal源码）因此，我们可以知道，所谓的变量副本，即是对Object Reference（对象引用）的拷贝。 </p>
<p>3.理解Thread和 ThreadLocal对变量的引用关系 <br />
实际上Thread和ThreadLocal对变量引用关系就像是坐标系中的X轴和Y轴，是从两个维度上来组织对变量的引用的。 </p>
<p>首先说Thread。 我们知道一个ThreadOne的执行会贯穿多个方法MethodA、MethodB、MethodC这些方法可能分布于不同的类实例。假设，这些方法分别使用了ThreadLocalA、ThreadLocalB、ThreadLocalC来保存线程本地变量，那么这些变量都存于ThreadOne的Map中，并使用各自的ThreadLocal实例作为key。 因此，可以认为，借助ThreanLocal的set方法，在X轴上，Thread横向关联同一线程上下文中来自多个Method的变量引用副本。 </p>
<p><br />
&nbsp;</p>
<p><br />
接着说ThreadLocal。 一个MethodA中的X变量将被多个线程ThreadOne、ThreadTwo、ThreadThree所访问。假设MethodA使用ThreadLocal存储X，通过set方法，以ThreadLocal作为key值，将不同线程来访时的不同的变量值引用保存于ThreadOne、ThreadTwo、ThreadThree的各自线程上下文中，确保每个线程有自己的一个变量值。因此，可以认为，ThreadLocal是以Method为Y轴，纵向关联了处于同一方法中的不同线程上的变量。 </p>
<p><br />
&nbsp;</p>
<p>希望能对大家有所帮助，这样可以少走很多弯路哦。 </p>
<p>本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/yangairong1984/archive/2008/04/15/2294572.aspx</p>
<img src ="http://www.blogjava.net/lingy/aggbug/292559.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lingy/" target="_blank">林光炎</a> 2009-08-25 20:36 <a href="http://www.blogjava.net/lingy/archive/2009/08/25/292559.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ant+cactus+tomcat5.5容器内单元测试</title><link>http://www.blogjava.net/lingy/archive/2009/06/22/283575.html</link><dc:creator>林光炎</dc:creator><author>林光炎</author><pubDate>Mon, 22 Jun 2009 07:06:00 GMT</pubDate><guid>http://www.blogjava.net/lingy/archive/2009/06/22/283575.html</guid><description><![CDATA[　一、下载并解压缩cactus<br />
　　下载地址为http://Java.chinaitlab.com/tools/45970.Html 。将cactus的lib目录下的cactus-ant-1.7.1.jar复制到ant的lib目录。<br />
　　二、配置cactus<br />
　　cactus的配置很简单，新建一个cactus.properties文件，并把它放在ant脚本中的cactus任务的classpath下，文件中包括如下内容<br />
　　cactus.sysproperties=cactus.contextURL<br highlighted="1" />
　　#cactus-sample-servlet-cactified就是你的测试应用所在路径，8080是<zmkey style="border-bottom: #ff6c00 2px dotted; float: none; cursor: pointer; font-weight: bold; margin-right: 3px; cssfloat: none" class="zoomino-searchword" offset="52" path="body > div:eq(0) > div:eq(3) > table:eq(2) > tbody:eq(0) > tr:eq(0) > td:eq(0) > div:eq(0) > #content:eq(0) > br:eq(4)" anchorType="previous" jQuery1245654007453="6">端口号<img style="border-bottom: medium none; border-left: medium none; padding-bottom: 0px; margin: 0px; padding-left: 0px; width: 12px; padding-right: 0px; display: inline; background-position: -18px -23px; float: none; height: 14px; border-top: medium none; border-right: medium none; padding-top: 0px; cssfloat: none" class="zoominoBgImage" src="http://static.zoomino.cn/static-ox/images/blank.gif" width="1" height="1"  alt="" /></zmkey><br />
　　cactus.contextURL = http://localhost:8080/cactus-sample-servlet-cactified<br />
　　cactus.servletReDirectorName = ServletRedirector<br />
　　cactus.jspRedirectorName = JspRedirector<br />
　　cactus.filterRedirectorName = FilterRedirector <br />
　　具体的做法结合ant脚本再进一步解释。<br />
　　三、运行ant脚本<br />
　　 ant脚本主要执行以下任务<br />
　　1、设定classpath<br />
　　&lt;path id="project.classpath"&gt;<br />
　　 &lt;fileset dir="${lib.dir}"&gt;<br />
　　 &lt;include name="*.jar"/&gt;<br />
　　 &lt;/fileset&gt;<br />
　　 &lt;!-- cactus.properties文件就需要放在lib.dir所对应的路径中 --&gt;<br />
　　 &lt;pathelement location="${lib.dir}"/&gt;<br />
　　 &lt;pathelement location="${tomcat.home}/common/lib/jsp-api.jar"/&gt;<br />
　　 &lt;pathelement location="${tomcat.home}/common/lib/servlet-api.jar"/&gt;<br />
　　 &lt;/path&gt;<br />
　　2、定义相关任务<br />
　　&lt;taskdef resource="cactus.tasks" classpathref="project.classpath"/&gt;<br />
　　 &lt;taskdef name="runservertests" classname="org.apache.cactus.integration.ant.RunServerTestsTask"&gt;<br />
　　 &lt;classpath&gt;<br />
　　 &lt;path refid="project.classpath"/&gt;<br />
　　 &lt;/classpath&gt;<br />
　　 &lt;/taskdef&gt;<br />
　　3、编译应用的类文件和测试的类文件<br />
　　4、打包整个应用为war文件<br />
　　需要注重的是，不仅要打包应用类，测试类也要打包<br />
　　&lt;target name="war" depends="compile.java"<br />
　　 description="Generate the runtime war"&gt;<br />
　　 &lt;war warfile="${target.dir}/${project.name}.war"<br />
　　 webXML="${src.webapp.dir}/WEB-INF/web.xml"&gt;<br />
　　 &lt;fileset dir="${src.webapp.dir}"&gt;<br />
　　 &lt;exclude name="cactus-report.xsl"/&gt;<br />
　　 &lt;exclude name="WEB-INF/cactus-web.xml"/&gt;<br />
　　 &lt;exclude name="WEB-INF/web.xml"/&gt;<br />
　　 &lt;/fileset&gt;<br />
　　 &lt;classes dir="${target.classes.java.dir}"/&gt;<br />
　　 &lt;!-- 别忘了打包测试类 --&gt;<br />
　　 &lt;classes dir="${target.classes.test.dir}"/&gt;<br />
　　 &lt;!-- 别忘了打包各种相关的jar文件 --&gt;<br />
　　 &lt; lib dir="project.classpath"/&gt;<br />
　　 &lt;/war&gt;<br />
　　 &lt;/target&gt;<br />
　　5、在应用的web.xml文件中添加测试所需的各种映射<br />
　　cactus提供了两个task来完成这个工作，CactifyWar和WebXmlMerge。<br />
　　CactifyWar的功能是自动在已经打包的应用的web.xml文件中添加所需的映射。WebXmlMerge是提供合并两个web.xml文件的功能。<br />
　　&lt;target name="test.prepare"<br />
　　 depends="war, compile.cactus, test.prepare.logging"&gt;<br />
　　 &lt;!-- Cactify the web-app archive --&gt;<br />
　　 &lt;cactifywar srcfile="${target.dir}/${project.name}.war"<br />
　　 destfile="${tomcat.home}/webapps/${project.name}-cactified.war"<br />
　　 &gt;<br />
　　 &lt;classes dir="${target.classes.java.dir}"/&gt;<br />
　　 &lt;classes dir="${target.classes.test.dir}"/&gt;<br />
　　 &lt;lib dir="project.classpath"/&gt;<br />
　　 &lt;/cactifywar&gt;<br />
　　&lt;/target&gt;<br />
　　6、运行测试<br />
　　cactus提供了cactus和RunServerTests两个task来运行测试。<br highlighted="1" />
　　"cactus" task是通过复制容器服务器的最小文件并运行来运行测试，因此需要制定容器服务器的类型，启动速度稍快点，另外配置比较方便，但是无法测试象tomcat<zmkey style="border-bottom: #ff6c00 2px dotted; float: none; cursor: pointer; font-weight: bold; margin-right: 3px; cssfloat: none" class="zoomino-searchword" offset="84" path="body > div:eq(0) > div:eq(3) > table:eq(2) > tbody:eq(0) > tr:eq(0) > td:eq(0) > div:eq(0) > #content:eq(0) > br:eq(64)" anchorType="previous" jQuery1245654007453="7">连接池<img style="border-bottom: medium none; border-left: medium none; padding-bottom: 0px; margin: 0px; padding-left: 0px; width: 12px; padding-right: 0px; display: inline; background-position: -18px -23px; float: none; height: 14px; border-top: medium none; border-right: medium none; padding-top: 0px; cssfloat: none" class="zoominoBgImage" src="http://static.zoomino.cn/static-ox/images/blank.gif" width="1" height="1"  alt="" /></zmkey>等资源。另外对tomcat5.5的支持也不好。<br />
　　"RunServerTests"是通过直接启动容器服务起来运行测试，因此速度稍慢，且配置较麻烦，但能测试各种资源。<br />
　　&lt;target name="test" depends="test.prepare"<br />
　　 description="Run tests on Tomcat "&gt;<br />
　　 &lt;!-- Start the servlet engine, wait for it to be started, run the<br />
　　 unit tests, stop the servlet engine, wait for it to be stopped.<br />
　　 The servlet engine is stopped if the tests fail for any reason --&gt;<br />
　　 &lt;!-- 8080是服务器的端口号，${project.name}-cactified是项目的路径，和上一步的cactifywar 的destfile相对应 --&gt;<br />
　　 &lt;runservertests<br />
　　 testURL="http://localhost:8080/${project.name}-cactified/ServletRedirector?Cactus_Service=RUN_TEST"<br />
　　 startTarget="_StartTomcat"<br />
　　 stopTarget="_StopTomcat"<br />
　　 testTarget="_Test"/&gt;<br />
　　 &lt;/target&gt;<br />
　　&lt;!-- _Test就是一个普通的junit任务 --&gt;<br />
　　 &lt;target name="_Test"&gt;<br />
　　 &lt;junit printsummary="yes" fork="yes"&gt;<br />
　　 &lt;classpath&gt;<br />
　　 &lt;path refid="project.classpath"/&gt;<br />
　　 &lt;pathelement location="${target.classes.java.dir}"/&gt;<br />
　　 &lt;pathelement location="${target.classes.test.dir}"/&gt;<br />
　　 &lt;/classpath&gt;<br />
　　 &lt;formatter type="brief" usefile="false"/&gt;<br />
　　 &lt;formatter type="xml"/&gt;<br />
　　 &lt;batchtest&gt;<br />
　　 &lt;fileset dir="${src.test.dir}"&gt;<br />
　　 &lt;!-- Due to some Cactus synchronization bug, the 'unit' tests need<br />
　　 to run before the 'sample' tests --&gt;<br />
　　 &lt;include name="**/Test*.java"/&gt;<br />
　　 &lt;exclude name="**/Test*All.java"/&gt;<br />
　　 &lt;/fileset&gt;<br />
　　 &lt;/batchtest&gt;<br />
　　 &lt;/junit&gt;<br />
　　 &lt;/target&gt;<br />
　　文章来源: baike.duba.net
<img src ="http://www.blogjava.net/lingy/aggbug/283575.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lingy/" target="_blank">林光炎</a> 2009-06-22 15:06 <a href="http://www.blogjava.net/lingy/archive/2009/06/22/283575.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>给servlet写单元测试的总结</title><link>http://www.blogjava.net/lingy/archive/2009/06/22/283551.html</link><dc:creator>林光炎</dc:creator><author>林光炎</author><pubDate>Mon, 22 Jun 2009 05:39:00 GMT</pubDate><guid>http://www.blogjava.net/lingy/archive/2009/06/22/283551.html</guid><description><![CDATA[<p>&nbsp;给servlet写单元测试的总结收藏<br />
servlet的测试一般来说需要容器的支持，不是像通常的java类的junit测试一样简单，<br />
&nbsp;<br />
下面通过对HelloWorld代码的测试阐述了几种servlet测试方法。<br />
&nbsp;<br />
被测试的HelloWorld类的代码如下：<br />
&nbsp;<br />
/**<br />
&nbsp;* 被测试的servlet<br />
&nbsp;*/</p>
<p>import java.io.IOException;<br />
&nbsp;<br />
import javax.servlet.http.HttpServlet;<br />
import javax.servlet.http.HttpServletRequest;<br />
import javax.servlet.http.HttpServletResponse;<br />
import org.apache.cactus.WebRequest; <br />
import org.apache.cactus.server.HttpServletRequestWrapper;<br />
&nbsp;<br />
public class HelloWorld extends HttpServlet{<br />
&nbsp;<br />
&nbsp;public void saveToSession(HttpServletRequest request) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.getSession().setAttribute("testAttribute",request.getParameter("testparam"));</p>
<p>&nbsp;}<br />
&nbsp;<br />
&nbsp;public void doGet(HttpServletRequest request,HttpServletResponse response) throws IOException{</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String username=request.getParameter("username");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.getWriter().write(username+":Hello World!");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;}<br />
&nbsp;<br />
&nbsp;public boolean authenticate(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br />
&nbsp;<br />
&nbsp;}</p>
<p>}<br />
&nbsp;<br />
以HelloWorld为例，我总结了Servlet的多种测试方法如下：<br />
&nbsp;<br />
一.使用HttpUnit测试<br />
&nbsp;<br />
import com.meterware.httpunit.GetMethodWebRequest;<br />
import com.meterware.httpunit.WebRequest;<br />
import com.meterware.httpunit.WebResponse;<br />
import com.meterware.servletunit.InvocationContext;<br />
import com.meterware.servletunit.ServletRunner;<br />
import com.meterware.servletunit.ServletUnitClient;<br />
import junit.framework.Assert;<br />
import junit.framework.TestCase;<br />
&nbsp;<br />
public class HttpUnitTestHelloWorld extends TestCase {<br />
&nbsp;<br />
&nbsp;protected void setUp() throws Exception {<br />
&nbsp; super.setUp();<br />
&nbsp;}<br />
&nbsp;<br />
&nbsp;protected void tearDown() throws Exception {<br />
&nbsp; super.tearDown();<br />
&nbsp;}<br />
&nbsp;<br />
&nbsp;public void testHelloWorld() {<br />
&nbsp; <br />
&nbsp; try {</p>
<p>&nbsp;&nbsp; // 创建Servlet的运行环境</p>
<p>&nbsp;&nbsp; ServletRunner sr = new ServletRunner();</p>
<p>&nbsp;&nbsp; // 向环境中注册Servlet</p>
<p>&nbsp;&nbsp; sr.registerServlet("HelloWorld", HelloWorld.class.getName());<br />
&nbsp;<br />
&nbsp;&nbsp; // 创建访问Servlet的客户端</p>
<p>&nbsp;&nbsp; ServletUnitClient sc = sr.newClient();</p>
<p>&nbsp;&nbsp; // 发送请求</p>
<p>&nbsp;&nbsp; WebRequest request = new GetMethodWebRequest("http://localhost/HelloWorld");<br />
&nbsp;&nbsp; request.setParameter("username", "testuser");</p>
<p>&nbsp;&nbsp; InvocationContext ic = sc.newInvocation(request);</p>
<p>&nbsp;&nbsp; HelloWorld is = (HelloWorld) ic.getServlet();<br />
&nbsp;<br />
&nbsp;&nbsp; // 测试servlet的某个方法</p>
<p>&nbsp;&nbsp; Assert.assertTrue(is.authenticate());</p>
<p>&nbsp;&nbsp; // 获得模拟服务器的信息</p>
<p>&nbsp;&nbsp; WebResponse response = sc.getResponse(request);</p>
<p>&nbsp;&nbsp; // 断言</p>
<p>&nbsp;&nbsp; Assert.assertTrue(response.getText().equals("testuser:Hello World!"));</p>
<p>&nbsp; } catch (Exception e) {</p>
<p>&nbsp;&nbsp; e.printStackTrace();</p>
<p>&nbsp; }</p>
<p>&nbsp;}<br />
&nbsp;<br />
}<br />
&nbsp;<br />
上述例子其实是junit的一个测试例子，在其中使用了httpunit模拟的servlet环境,使用上述方法测试<br />
&nbsp;<br />
servlet可以脱离容器，容易把该测试写入ant或maven脚本，让测试进行。<br />
&nbsp;<br />
httpunit网址：http://httpunit.sourceforge.net/<br />
&nbsp;<br />
使用该种方法测试的弱点就是：如果要使用request(response)的setCharercterEncoding方法时，测试会出现一些问题,<br />
&nbsp;<br />
而且httpunit在测试servlet行为时，采用的是完全模拟浏览器，有时测试比较难写。<br />
&nbsp;<br />
二 使用cactus测试<br />
&nbsp;<br />
/**<br />
&nbsp;* cactus测试servlet的例子<br />
&nbsp;* 必须要有tomcat的支持<br />
&nbsp;* <br />
&nbsp;*/<br />
&nbsp;<br />
import junit.framework.Test;<br />
import junit.framework.TestSuite;<br />
import org.apache.cactus.ServletTestCase; <br />
import org.apache.cactus.WebRequest;<br />
import org.apache.cactus.WebResponse;<br />
public class CactusHelloWorld extends ServletTestCase{<br />
&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; HelloWorld servlet;<br />
&nbsp;&nbsp;&nbsp;&nbsp; public CactusHelloWorld(String theName) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super(theName);<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; protected void setUp() throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.setUp();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; servlet = new HelloWorld();<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; protected void tearDown() throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.tearDown();<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 测试方法测试参数在此设置<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @param webrequest<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; public void beginSaveToSessionOK(WebRequest request) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.addParameter("testparam", "it works!");<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 测试方法测试参数在此设置<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @param webrequest<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; public void beginDoGet(WebRequest request) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.addParameter("username", "testuser");<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 调用servlet的测试方法<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; public void testSaveToSessionOK() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; servlet.saveToSession(request);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertEquals("it works!", session.getAttribute("testAttribute"));<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; public void testDoGet() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; servlet.doGet(request, response);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (Exception e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 此方法可以判断测试方法的输出，会传递测试方法的reponse给end***,并且格式化为cactus<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 的WebResponse或者可以跟httpunit集成，格式化为httpunit的response<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @param response<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; public void endDoGet(WebResponse response) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String content;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content = response.getText();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertEquals("testuser:Hello World!", content);<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
}<br />
&nbsp;<br />
cactus具备丰富灵活的测试功能，如要测试doGet方法，分为beginDoGet(模拟测试参数设置)、DoGet(执行测试)、endDoGet(状态结果验证)<br />
&nbsp;<br />
相比httpunit来说，写测试更为容易，测试servlet更为专业,流程更为清晰，但是cactus需要容器支持，使得测试不可以自动进行，但是<br />
&nbsp;<br />
如果使用一个嵌入式的容器，测试就可以自动了。<br />
&nbsp;<br />
cactus是一个servlet和jsp的测试框架:http://jakarta.apache.org/cactus/getting_started.html<br />
&nbsp;<br />
三 使用Jetty作为嵌入式容器测试servlet.<br />
&nbsp;<br />
/**<br />
&nbsp;* 一个关于嵌入式jetty测试的例子，jetty作为stubs的一个例子<br />
&nbsp;* <br />
&nbsp;*/<br />
package com.easyjf.testexample;<br />
&nbsp;<br />
import org.mortbay.jetty.Connector;<br />
import org.mortbay.jetty.Server;<br />
import org.mortbay.jetty.bio.SocketConnector;<br />
import org.mortbay.jetty.servlet.ServletHandler;<br />
&nbsp;<br />
import com.meterware.httpunit.WebClient;<br />
import com.meterware.httpunit.WebConversation;<br />
import com.meterware.httpunit.WebResponse;<br />
&nbsp;<br />
import junit.framework.Assert;<br />
import junit.framework.TestCase;<br />
&nbsp;<br />
public class JettySampleTest extends TestCase {<br />
&nbsp;<br />
&nbsp;Server server;<br />
&nbsp;protected void setUp() throws Exception {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //通过代码设置并启动一个服务器，该服务器是servlet的测试容器<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.setUp();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server = new Server();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Connector connector=new SocketConnector();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; connector.setPort(80);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server.setConnectors(new Connector[]{connector});<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServletHandler handler=new ServletHandler();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server.setHandler(handler);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; handler.addServletWithMapping("HelloWorld", "/");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server.start();<br />
&nbsp;}<br />
&nbsp;<br />
&nbsp;protected void tearDown() throws Exception {<br />
&nbsp; super.tearDown();<br />
&nbsp; server.stop();<br />
&nbsp;}<br />
&nbsp;<br />
&nbsp;public void testHellWorld() {<br />
&nbsp; try {<br />
&nbsp;&nbsp; WebConversation wc = new WebConversation();<br />
&nbsp;&nbsp; WebResponse web = wc.getResponse("http://127.0.0.1/HelloWorld");<br />
&nbsp;&nbsp; String result=web.getText();<br />
&nbsp;&nbsp; Assert.assertEquals(result,"it works!");<br />
&nbsp; } catch (Exception e) {<br />
&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp; }<br />
&nbsp;}<br />
}<br />
&nbsp;<br />
可以发现，jetty可以充当一个servlet的容器，方便的是,jetty支持嵌入式服务，即可以通过代码来启动，<br />
&nbsp;<br />
所以要写自动测试的例子很方便，可以结合httpunit或者cactus进行servlet测试。<br />
&nbsp;<br />
jetty主页：http://docs.codehaus.org/display/JETTY/Embedding+Jetty</p>
<p><br />
四 使用mock对象，此处使用easymock<br />
&nbsp;<br />
import java.io.PrintWriter;<br />
import java.io.Writer;<br />
import javax.servlet.ServletConfig;<br />
import javax.servlet.ServletContext;<br />
import javax.servlet.http.HttpServletRequest;<br />
import javax.servlet.http.HttpServletResponse;<br />
&nbsp;<br />
import junit.framework.Assert;<br />
import junit.framework.TestCase;<br />
import static org.easymock.EasyMock.*;<br />
public class MockTestServlet extends TestCase {<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp; public void testService() throws Exception {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("service");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpServletRequest request = createMock(HttpServletRequest.class);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpServletResponse response = createMock(HttpServletResponse.class);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Creating the ServletConfig mock here</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServletConfig servletConfig = createMock(ServletConfig.class);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Creating the ServletContext mock here</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ServletContext servletContext = createMock(ServletContext.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Create the target object&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HelloWorld4 instance = new HelloWorld();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //初始化servlet,一般由容器承担，一般调用servletConfig作为参数初始化，此处模拟容器行为</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; instance.init(servletConfig);<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //在某些方法被调用时设置期望的返回值，如下这样就不会去实际调用servletConfig的getServletContext方法，而是直接返回<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //servletContext,由于servletConfig是mock出来的，所以可以完全控制。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; expect(servletConfig.getServletContext()).andReturn(servletContext).anyTimes();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; expect(request.getParameter("username")).andReturn("testuser");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PrintWriter pw=new PrintWriter(System.out,true);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; expect(response.getWriter()).andReturn(pw).anyTimes();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //以上均是录制，下面为重放，该种机制为easymock测试机制，要理解请看easymock测试的一些资料<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; replay(request);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; replay(response);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; replay(servletConfig);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; replay(servletContext);<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; instance.doGet(request, response);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pw.flush();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //验证结果是否预期，如果预期，则会在pw上写出testuser.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; verify(request);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; verify(response);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; verify(servletConfig);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; verify(servletContext);<br />
&nbsp;&nbsp; }<br />
}<br />
&nbsp;<br />
mock测试注重行为，mock对象其实都是模拟的对象，方法一般直接给出一个返回值，没有具体的对象逻辑，mock对象<br />
&nbsp;<br />
是用来帮助测试要测试的类的。比如要测试servlet的内部行为，又不想要容器等环境，就可以采用mock测试。<br />
&nbsp;<br />
easymock是mock测试的一个框架：http://www.easymock.org/<br />
&nbsp;<br />
发表于 @ 2007年02月10日 22:13:00|评论(2)</p>
<p>新一篇: 设计模式之创建模式 | 旧一篇: 服务定位器模式(service locator)wldandanpig 发表于2007年5月24日 10:09:40&nbsp; IP:举报<br />
请问楼主<br />
public String saveInfo()<br />
{<br />
String reqInfo = request.getParameter("reqInfo");<br />
String sessInfo = (String)request.getSession().getAttribute("sessInfo");</p>
<p>request.setAttribute("reqInfo" , "response:"+reqInfo);<br />
request.getSession().setAttribute("sessInfo", "response:"+reqInfo);</p>
<p>return "SUCCESS";<br />
}<br />
这个方法怎么测试啊cz_hyf 发表于2007年5月24日 17:20:23&nbsp; IP:举报<br />
如果用httpunit的话</p>
<p>public void testHelloWorld() {</p>
<p>try {</p>
<p>// 创建Servlet的运行环境</p>
<p>ServletRunner sr = new ServletRunner();</p>
<p>// 向环境中注册Servlet</p>
<p>sr.registerServlet("HelloWorld", HelloWorld.class.getName());</p>
<p>// 创建访问Servlet的客户端</p>
<p>ServletUnitClient sc = sr.newClient();</p>
<p>// 发送请求</p>
<p>WebRequest request = new GetMethodWebRequest("http://localhost/HelloWorld");<br />
request.setParameter("reqInfo", "......");</p>
<p>InvocationContext ic = sc.newInvocation(request);</p>
<p>HelloWorld is = (HelloWorld) ic.getServlet();</p>
<p>// 测试servlet的某个方法</p>
<p>Assert.assertEquals(is.saveInfo,"SUCCESS");</p>
<p>} catch (Exception e) {</p>
<p>e.printStackTrace();</p>
<p>}</p>
<p>}</p>
<p>如果还不放心，不妨把request和request.session中的值取出来看看是否是你放进去的</p>
<p><br />
本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/cz_hyf/archive/2007/02/10/1507211.aspx</p>
<img src ="http://www.blogjava.net/lingy/aggbug/283551.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lingy/" target="_blank">林光炎</a> 2009-06-22 13:39 <a href="http://www.blogjava.net/lingy/archive/2009/06/22/283551.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jsp内建对象</title><link>http://www.blogjava.net/lingy/archive/2009/06/04/279972.html</link><dc:creator>林光炎</dc:creator><author>林光炎</author><pubDate>Thu, 04 Jun 2009 02:59:00 GMT</pubDate><guid>http://www.blogjava.net/lingy/archive/2009/06/04/279972.html</guid><description><![CDATA[<p>JSP的内建对象不需要由JSP编写人员实例化,它们均由容器管理和实现,在所有的JSP页面中都能使用.只在Scriptlet或者表达式中使用.它们是:</p>
<p>１．request对象:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 代表请求对象,被包装成HttpServletRequest接口,通过getParameter方法得到request参数,通过 GET,POST,HEAD等方法得到request的类型.通过Cookies,Referer等可以得到Http头.来自客户端的请求经 Servlet容器处理后,由request对象进行封装,作为jspService()方法的一个参数由容器传递给JSP页面.主要方法有:request.getAttribute("..");request.getParameter("xxx");request.getParameterValues("xxx");request.getHeader("xxx") 获取HTTP协议定义的文件头信息.request.getRequestURI()获取发出请求字符串的客户端地址.request.getRemoteHost()客户端名,request.getRemoteAddr()客户端IP地址.request.getServerName()<a class="channel_keylink" href="http://www.23book.net/Server/Index.htm">服务器</a>名字.request.getServletPath();客户端所请求的脚本文件的文件路径.request.getServerPort()获取<a class="channel_keylink" href="http://www.23book.net/Server/Index.htm">服务器</a>的端口号.request.setAttribute("xxx","");等.</p>
<p>２．response</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 响应请求对象,被包装成HttpServletResponse接口,与requst对象一样,是缓冲输出流,可以设置Http状态码和response 响应头.主要的方法有:response.addCookie(Cookie cookie);response.encodeURL();response.getOutputStream();response.sendRedirect("xxx") 把响应发到另一位置进行处理.</p>
<p>３．pageContext</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 被封装成javax.servlet.jsp.pageContext接口,它为JSP页面包装页面的上下文.由容器创建和初始化,管理对属于JSP中特殊可见部分中已命名对象的访问.可用getServletContext()返回Servlet Context对象(这个Servlet Context对所有的页面都是共享的.),forward("xxx")把页面重定向到另一页面或者Servlet组件上.</p>
<p>&nbsp;４．session对象</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 用来保存每个用户信息,以便跟踪每个用户的操作状态.其中session信息保存在容器中,session的ID保存在客户机的Cookiek .(一般情况中,用户每次登录系统时容器会给此用户分配一个唯一的标识session id,用于区别其它用户,当用户退出系统时,这个标识就会自动消失.其接口为HttpSession.主要的方法有:session.getId();getLastAccessedTime();setAttribute();</p>
<p>５．application</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 与<a class="channel_keylink" href="http://www.23book.net/Server/Index.htm">服务器</a>启动后创建.直到<a class="channel_keylink" href="http://www.23book.net/Server/Index.htm">服务器</a>关闭而停止,为多个应用程序保存信息.</p>
<p>６．out对象:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 被封装成javax.servlet.jsp.JspWriter接口,它表示为客户打开的输出流,PrintWriter使用它向客户端发送输出流.即简单说向客户端输出数据.方法:out.print(....),out.newLine();out.flush();out.close();等,并可获取输出缓冲区的相关信息.如果要在页面中真正输出一个空行,需要用out.println("&lt;br&gt;")来实现.</p>
<p>７．config</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 被封装为javax.servlet.ServletConfig接口,它表示了Servlet的配置.当一个Servlet初始化时,容器把某些信息通过 此对象传递给这个Servlet.,常用的方法有:getServletContext();</p>
<p>８．page</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 是java.lang.Object类的一个实例,它指的是JSP实现类的实例.</p>
<p>９．exception&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 是java.lang.Throwable类的一个实例,它指的是运行时的异常,也就是被调用的错误页面的结果,只有在错误页面(在指令里有isErrorPage=true的页面中)才可以使用</p>
<img src ="http://www.blogjava.net/lingy/aggbug/279972.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lingy/" target="_blank">林光炎</a> 2009-06-04 10:59 <a href="http://www.blogjava.net/lingy/archive/2009/06/04/279972.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>