﻿<?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-上善若水-随笔分类-Jetty</title><link>http://www.blogjava.net/DLevin/category/54908.html</link><description>In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation.
To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra</description><language>zh-cn</language><lastBuildDate>Wed, 12 Aug 2015 15:00:08 GMT</lastBuildDate><pubDate>Wed, 12 Aug 2015 15:00:08 GMT</pubDate><ttl>60</ttl><item><title>深入Jetty源码之Server和Container</title><link>http://www.blogjava.net/DLevin/archive/2014/05/24/414070.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sat, 24 May 2014 14:40:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/05/24/414070.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/414070.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/05/24/414070.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/414070.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/414070.html</trackback:ping><description><![CDATA[<p style="margin-top: 8.7pt; margin-bottom: 8.7pt; line-height: 15.6pt;"><span style="color: #ff6600; font-family: Georgia; font-size: 15.5pt; font-weight: bold; line-height: 15.6pt;">概述</span></p><p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">Server类是Jetty中最核心的是类，它即包含Connectors数据，有包含了Handler的集合，即它是Jetty中用于连接Connector和Handler的类。同时它还包含了一个Container，用于存储Jetty中核心类实例的关系发生变化时触发事件的Listener，接收者可以注册一个Listener以获取Jetty中某个类的关系发生变化。</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;"><br /></span><span style="color: #ff6600; font-weight: bold; font-size: 15.5pt; font-family: 宋体;">Server的实现</span></p><p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">Server继承自HandlerWrapper，因而它默认是一个Handler容器，另外它也包含一个Container字段，以及Connector数组字段，ThreadPool字段，另外它还包含了一些配置字段，如：attributes、sessionIdManager、sendServerVersion、sendDateHeader、stopAtShutdown（注册shutdown&nbsp;hook）、maxCookieVersion、dumpAfterStart、dumpBeforeStop等，这些只是用于配置信息，而且名称本身已经很清楚它的含义了。</span></p><p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; ">&nbsp;</p><p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">在使用Server时，我们需要向其添加Connector以及Handler，从而在启动时，它会首先注册ShutdownThread，即在JVM退出时会首先调用它的stop方法；如果没有手动设置ThreadPool，使用QueuedThreadPool初始化ThreadPool字段；然后启动所有Handler以及Connector。在stop时，它先close所有Connector，然后设置所有实现了Graceful接口的类的shutdown为true，并等待graceful时间后，stop所有Connector以及Handler。</span></p><p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; ">&nbsp;</p><p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">Server中有两个handle方法在HttpConnection请求解析完成后调用，其中handle方法用于在非ASYNC状态下的调用，它只是从HttpConnection中取得Request、Response实例以及PathInfo作为target，传递给Handler的handle方法；而handleAsync方法则是在ASYNC状态下调用，它的baseRequest从HttpConnection中获取，但是会将其原有的RequestURI、QueryString、ContextPath设置到其Attribute（javax.servlet.async.*）中，而将他们对应的属性包括PathInfo更新为当前AsyncContext中Request的值，并且request、response实例则是从AsyncContext中获取，然后调用Handler的handle方法。</span></p><p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; "><span style="color: #ff6600; font-weight: bold; font-size: 15.5pt; font-family: 宋体;">Container的实现</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="background: #ffffff;"><font face="宋体" color="#4b4b4b"><span style="font-size: 9.5pt;">Container用于生成父实例和子引用的关系发生变化时生成的时间，以提供其注册的Listener接收到相应的事件。Container通过内部接口Listener可以触发如下事件：</span></font><font face="Monaco"><span style="font-size: 15px;"><br /></span></font></span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;"></span></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Listener&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;EventListener&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;addBean(Object&nbsp;bean);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;removeBean(Object&nbsp;bean);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;add(Container.Relationship&nbsp;relationship);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;remove(Container.Relationship&nbsp;relationship);<br />&nbsp;&nbsp;&nbsp;&nbsp;}</div><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">其中Relationship表示一个父实例和一个子引用的关系，如handler、threadPool、errorHandler等。每当一个关系发生变化时，可以调用Container的update方法，而update方法内部会触发相应的remove和add事件。</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;"><br /></span></p><img src ="http://www.blogjava.net/DLevin/aggbug/414070.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-05-24 22:40 <a href="http://www.blogjava.net/DLevin/archive/2014/05/24/414070.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之ContextHandler</title><link>http://www.blogjava.net/DLevin/archive/2014/05/24/413817.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sat, 24 May 2014 14:17:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/05/24/413817.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/413817.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/05/24/413817.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/413817.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/413817.html</trackback:ping><description><![CDATA[<h2><span style="color: #ff6600;">概述</span></h2>
ContextHandler继承自ScopedHandler，它是Jetty中实现对一个Web Application的各种资源进行管理，并串联实现整个Servlet框架的类，比如它部分实现了ServletContext接口，并且在其doScope方法中为当前Request的执行提供了相应的环境，如设置servletPath、pathInfo、设置ServletContext到ThreadLocal中。在Jetty中，Servlet的执行流程和框架由ServletHandler实现，Security框架由SecurityHandler完成，而ContextHandler主要用于实现环境的配置，如Request状态的设置、ClassLoader的配置、ServletContext的实现等。在ContextHandler类中包含了一个Context的内部类，用于实现ServletContext，而ContextHandler中的很多字段和方法也是用于实现ServletContext，不细述。<br />
<h2><span style="color: #ff6600;">ContextHandler实现</span></h2>
ContextHandler中doStart方法实现：<br />
1. 使用ContextPath或DisplayName初始化logger字段；并设置当前线程的ContextClassLoader为配置的ClassLoader实例；初始化mimeType字段；设置Context的ThreadLocal变量。<br />
2. 初始化managedAttributes Map，并生成addBean事件；如果存在ErrorHandler，start它；生成contextInitialized事件。<br />
3. 初始化availability字段。<br />
4. 还原Context的ThreadLocal变量和ContextClassLoader回原有实例。<br />
<br />
ContextHandler中doStop方法实现：<br />
1. 设置availability字段为STOPPED状态；初始化Context的ThreadLocal变量和ContextClassLoader为当前Context实例以及设置的ClassLoader。<br />
2. 生成contextDestroyed事件，以及对managedAttributes，触发removeBean事件。<br />
3.&nbsp;还原Context的ThreadLocal变量和ContextClassLoader回原有实例。<br />
<br />
ContextHandler中doScope方法实现：<br />
1. 对REQUEST、ASYNC的DispatcherType，并且是第一次进入该ContextHandler，则如果compactPath为true，compact传入的path，即把"//", "///"等替换为"/"。<br />
2. 对当前请求做初步检查以决定是否需要继续执行该请求(返回false表示不需要继续执行该请求)：<br />
&nbsp; &nbsp; a. 检查availability状态，对UNAVAILABLE，发送503 Service Unavailable响应；对STOPPED、SHUTDOWN状态，或Request的handled字段为true，返回false。<br />
&nbsp; &nbsp; b. 对设置了vhosts，检查请求消息中的ServerName请求头是否和vhosts中的某个vhost相同或比配，如果不成立，则返回false。<br />
&nbsp; &nbsp; c. 检查设置的connectors数组，如果当前HttpConnection中的Connector.name不包含在这个设置的connectors数组中，返回false。<br />
&nbsp; &nbsp; d. 检查contextPath，如果target不以这个contextPath开头或者在target中contextPath之后的字符不是"/"，返回false；如果allowNullPathInfo设置为false，且target不以"/"结尾，发送"target + /"的重定向请求，返回false。<br />
&nbsp; &nbsp; e. 对其他情况，返回true，表示请求可以继续处理。<br />
3. 计算pathInfo以及target为contextPath之后的路径，并设置ContextClassLoader为当前设置的ClassLoader。<br />
4. 保留当前Request的contextPath、servletPath、pathInfo信息。<br />
5. 对任意非INCLUDE的DispatcherType，设置Request的contextPath、servletPath为null、pathInfo为传入的target中contextPath之后的路径。<br />
6. 执行nextScope的逻辑。<br />
7. 还原当前Request的contextPath、servletPath、pathInfo的信息。<br />
<br />
ContextHandler中doHandle方法实现：<br />
1. 对有新更新Context的Request实例，向当前Request添加注册的ServletRequestAttributeListener，如果注册了ServletRequestListener，生成requestInitialized事件。<br />
2. 对REQUEST类型的DispatcherType，如果该target为保护资源(isProctedTarget，如WEB-INF、META-INF目录下的文件)，抛出404 Not Found的HttpException。<br />
3. 执行nextHandle()逻辑。<br />
4. 如果注册了ServletRequestListener，生成requestDestroyed事件，并从Request中移除当前ContextHandler中添加的ServletRequestAttributeListener实例。<br />
<h2><span style="color: #ff6600;">ServletContextHandler实现</span></h2>
ServletContextHandler继承自ContextHandler类，它串连了SessionHandler、SecurityHandler和ServletHandler，在ServletContextHandler的start过程中，它会串连如下Handler：<br />
ServletContextHandler -....-&gt;SessionHandler-&gt;SecurityHandler-&gt;ServletHandler，由于ServletContextHandler、SessionHandler、ServletHandler都继承自ScopedHandler，因而他们的执行栈将会是：<br />
|-&gt;ServletContextHandler.doScope()<br />
&nbsp; |-&gt; SessionHandler.doScope()<br />
&nbsp; &nbsp; |-&gt; ServletHandler.doScope()<br />
&nbsp; &nbsp; &nbsp; |-&gt; ServletContextHandler.doHandle()<br />
&nbsp; &nbsp; &nbsp; &nbsp; |-&gt; .....handler.handle()<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |-&gt; SessionHandler.doHandle()<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |-&gt; SecurityHandler.handle()<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |-&gt; ServletHandler.doHandle()<br />
<br />
另外ServletContextHandler还提供了一个Decorator的扩展点，可以向ServletContextHandler注册多个Decorator，在ServletContextHandler启动时，它会对每个已注册的ServletHolder和FilterHolder执行一些额外的&#8220;装饰&#8221;逻辑，出了对ServletHolder和FilterHolder的装饰，它还可以装饰Filter、Servlet、Listener等，以及在销毁他们时加入一下自定义的逻辑：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Decorator&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;T&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Filter&gt;&nbsp;T&nbsp;decorateFilterInstance(T&nbsp;filter)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;T&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Servlet&gt;&nbsp;T&nbsp;decorateServletInstance(T&nbsp;servlet)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;T&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;EventListener&gt;&nbsp;T&nbsp;decorateListenerInstance(T&nbsp;listener)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;decorateFilterHolder(FilterHolder&nbsp;filter)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;decorateServletHolder(ServletHolder&nbsp;servlet)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;destroyServletInstance(Servlet&nbsp;s);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;destroyFilterInstance(Filter&nbsp;f);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;destroyListenerInstance(EventListener&nbsp;f);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</div>
<br />
Decorator扩展点的引入实现了两种方式对Servlet、Filter、EventListener的配置：Annotation方式(AnnotationDecorator)和Plus方式(PlusDecorator)，其中Annotation的方式的配置是Servlet 3.0规范中新加入的特性，而Plus方式则是Jetty提供的配置注入。<br />
其中AnnotationDecorator的实现采用AnnotationInstrospector，可以向它注册不同的InstrospectableAnnotationHandler，用以处理不同的Annotation逻辑，从而实现对动态注册的Servlet、Filter、EventListener，可以使用在它们之上的Annotation来做进一步的配置，以简化配置本身。在Jetty中实现了以下几个Annotation的InstrospectableAnnotationHandler：<br />
@Resource &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;=&gt; ResourceAnnotationHandler: 对类上的@Resource注解，将它作为一种资源绑定到当前Context或Server中，对Field或Method的@Resource注解，创建一个Injection实例放入Context的Attribute中。在PlusDecorator中会对注册的Injection实例做inject操作。<br />
@Resources &nbsp; &nbsp; &nbsp; &nbsp; =&gt; ResourcesAnnotationHandler: 对类上的@Resources注解中的每个@Resource注解作为一种资源绑定到当前Context或Server中。<br />
@RunAs &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;=&gt; RunAsAnnotationHandler: 关联Servlet上@RunAs注解的值到该ServletHolder中。<br />
@ServletSecurity =&gt; SecurityAnnotationHandler: 为@ServletSecurity注解的Servlet配置DataConstraint、Roles、methodOmission等。<br />
@PostConstruct &nbsp; &nbsp;=&gt; PostConstructAnnotationHandler: 将有该注解的方法注册PostConstructCallback回调类，在PlusDecorator中的decorate方法中会调用该callback。<br />
@PreDestroy &nbsp; &nbsp; &nbsp; &nbsp;=&gt; PreDestroyAnnotationHandler: 将有该注解的方法注册PreDestroyCallback回调类，在PlusDecorator中的decorate方法中会调用该callback。<br />
@MultipartConfig &nbsp;=&gt; MultipartConfigAnnotationHandler: 将有该注解的Servlet类注册配置的MultipartConfig信息。<br />
@DeclareRoles &nbsp; &nbsp; =&gt; DeclareRolesAnnotationHandler: 向SecurityHandler注册定义的Role集合。<br />
而PlusDecorator主要处理使用以上Annotation或PlusDescriptorProcessor注册的RunAsCollection、InjectionCollection、LifeCycleCallbackCollection的逻辑实现。其中RunAsCollection用于向注册的对应的ServletHolder注册RunAsRole信息；InjectionCollection实现从JNDI中查找对应JndiName的实例，并将其设置到Injection中指定的字段或方法中；LifeCycleCallbackCollection用于实现在Servlet、Filter、EventListener创建后或销毁前调用相应的有@PostConstruct注解或@PreDestroy注解的方法。<br />
<h2><span style="color: #ff6600;">WebAppContext实现</span></h2>
WebAppContext继承自ServletContextHandler，主要用于整合对ServletContextHandler的配置、配置WebAppClassLoader、设置war包路径、设置contextWhiteList、保存MetaData信息等。<br />
<br />
对WebAppContext的配置，Jetty使用Configuration接口类抽象这个行为，其接口定义如下（方法名称都比较直观）：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Configuration {<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;preConfigure&nbsp;(WebAppContext&nbsp;context)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;Exception;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;configure&nbsp;(WebAppContext&nbsp;context)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;Exception;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;postConfigure&nbsp;(WebAppContext&nbsp;context)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;Exception;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;deconfigure&nbsp;(WebAppContext&nbsp;context)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;Exception;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;destroy&nbsp;(WebAppContext&nbsp;context)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;Exception;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;cloneConfigure&nbsp;(WebAppContext&nbsp;template,&nbsp;WebAppContext&nbsp;context)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;Exception;<br />
}</div>
可以使用setConfigurationClasses或setConfigurations方法自定义当前支持的Configuration集合，Jetty默认添加集合有：WebInfConfiguration、WebXmlConfiguration、MetaInfConfiguration、FragmentConfiguration、JettyWebXmlConfiguration，另外Jetty内部默认实现的还有：AnnotationConfiguration、ContainerInitializerConfiguration、EnvConfiguration、PlusConfiguration、TagLigConfiguration等。<br />
<br />
在WebInfConfiguration实现中，在其preConfigure方法中，如果存在WEB-INF/work目录，先在该目录下创建一个名为Jetty_&lt;host&gt;_&lt;port&gt;__&lt;resourceBase&gt;_&lt;contextPath&gt;_&lt;virtualhost&gt;&lt;base36_hashcode_of_whole_string&gt;的临时目录，然后设置WebAppContext的临时目录：<br />
1. 可以手动设置。<br />
2. 可以使用javax.servlet.context.tempdir属性值设置。<br />
3. 可以设置为${jetty.home}/work/Jetty_&lt;host&gt;_&lt;port&gt;__&lt;resourceBase&gt;_&lt;contextPath&gt;_&lt;virtualhost&gt;&lt;base36_hashcode_of_whole_string&gt;<br />
4. 可以使用属性org.eclipse.jetty.we
<div style="display: inline-block;">pasting</div>
bapp.basetempdir指定的base，然后设置为${base}/Jetty_&lt;host&gt;_&lt;port&gt;__&lt;resourceBase&gt;_&lt;contextPath&gt;_&lt;virtualhost&gt;&lt;base36_hashcode_of_whole_string&gt;<br />
5. 可以设置为${java.io.tmpdir}/Jetty_&lt;host&gt;_&lt;port&gt;__&lt;resourceBase&gt;_&lt;contextPath&gt;_&lt;virtualhost&gt;&lt;base36_hashcode_of_whole_string&gt;<br />
6. 所有以上设置失败，则使用File.createTempFile("JettyContext", "")的目录来设置。<br />
对于war包，如果配置了extractWAR为true，则将war包解压到war包所在目录的war包名的目录或&lt;tempDir&gt;/webapp目录下，如果配置了copyWebDir，则将原本配置的BaseResource下的所有内容拷贝到&lt;tempDir&gt;/webapp目录下，使用新的web_app目录设置BaseResource目录；如果配置了copyWebInf为true，则将WEB-INF/lib, WEB-INF/classes的两个目录拷贝到&lt;tempDir&gt;/webinf/lib, &lt;tempDir&gt;/webinf/classes目录下，并更新BaseResource为原来webapp目录和&lt;tempDir&gt;/webinf两个目录的组合；设置org.eclipse.jetty.server.webapp.ContainerIncludeJarParttern属性，查找URLClassLoader中URL中对应的jar包(即WebAppContext中配置的extraClassPath值)，并添加到MetaData的containerJars集合中(如果不设置，则不会添加任何jar包)；使用org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern属性匹配WEB-INF/lib目录下的所有jar包，并添加到MetaData的webInfJars集合中（如果不设置，默认添加所有jar包）。<br />
在其configure()方法实现中，设置WebAppClassLoader的classPath为WEB-INF/classes, WEB-INF/lib目录下的所有jar包，并将这些jar包添加到WebAppClassLoader中；并且如果配置了org.eclipse.jetty.resources属性，则将配置的List&lt;Resource&gt;集合添加到WebAppContext的BaseResource中。<br />
<br />
WebXmlConfiguration的实现，在preConfigure中，它向MetaData注册webdefault.xml文件描述符；web.xml(默认为WEB-INF/web.xml)文件描述符；以及override-web.xml文件描述符；在注册过程中解析它们的absolute-ordering信息，将解析的结果合并到MetaData的ordering集合中。在configure方法实现中，它向MetaData添加StandardDescriptorProcessor。<br />
<br />
MetaInfConfiguration的实现，在preConfigure()方法中，扫描MetaData中在WebInfConfiguration中注册的所有containerJars和webInfJars
的jar包，将找到的META-INF/web-fragment.xml生成的Resource注册到org.eclipse.jetty.webFragments属性中，在FragmentConfiguration中会被进一步添加到MetaData中；将META-INF/resources/对应的Resource注册到org.eclipse.jetty.resources属性中，在WebInfConfiguration的configure方法中会将这些目录添加到BaseResource集合中；将所有*.tld文件对应的Resource注册到org.eclipse.jetty.tlds属性中，在TagLibConfiguration中，会对这些注册TLD文件做进一步的处理。<br />
<br />
FragmentConfiguration的实现，在其preConfigure方法中，将MetaInfConfiguration中找到的web-fragment.xml文件对应的Resource注册到MetaData中，在注册中首先解析它的ordering信息；在其configure方法中，它使用ordering中定义的顺序逻辑对注册的jar包进行排序。<br />
<br />
JettyWebConfiguration的实现，在其configure方法中，依次查找jetty8-web.xml, jetty-web.xml, web-jetty.xml文件，如果有找到任意一个，则使用XmlCofiguration对WebAppContext进行配置。XmlConfiguration的实现参考<a href="http://www.blogjava.net/DLevin/archive/2014/05/24/414061.html">《深入Jetty源码之XmlConfiguration实现》</a><br />
<br />
在AnnotationConfiguration的实现中，在其configure()方法中，它首先向WebAppContext中注册AnnotationDecorator；然后它创建AnnotationParser实例，然后向其注册WebServletAnnotationHandler、WebFilterAnnotationHandler、WebListenerAnnotationHandler、ClassInheritanceHandler、ContainerInitializerAnnotationHandler，它们都实现了DiscoverableAnnotationHandler（其中ClassInheritanceHandler实现的是ClassHandler接口）；最后扫瞄所有ClassPath下jar包、WEB-INF/classes以及WEB-INF/lib中的jar包下的每个类，对于所有注册为systemClasses，非serverClasses的类，使用ClassInheritanceHandler纪录所有类包含的直接子类以及所有接口包含的直接实现类，而WebFilterAnnotationHandler、WebServletAnnotationHandler、WebListenerAnnotationHandler用于注册相应的WebFilterAnnotation、WebServletAnnotation、WebListenerAnnotation，并添加到MetaData中DiscoveredAnnotation集合中，这些DiscoveredAnnotation在MetaData的resolve方法(WebAppContext.startContext()方法中被调用)调用时会向WebAppContext注册对应的FilterHolder、ServletHolder、EventListener，而ContainerInitializerAnnotationHandler则会将所有注册的注解修饰的类添加到注册的ContainerInitializer的annotatedTypeNames集合中，该集合在ContainerInitializerConfiguration将它自身以及它的所有子类、实现类添加到applicableTypeNames集合中，集合之前注册的interestedTypes的所有子类、实现类传递到ServletContainerInitializer的onStartup()方法中。<br />
<br />
在ContainerInitializerConfiguration会使用AnnotationConfiguration中注册ContainerInitializer实例列表，构建applicableTypeNames，并调用其ServletContainerInitializer的onStartup方法。<br />
<br />
EnvConfiguration实现，在preConfigure方法中使用XmlConfiguration以及WEB-INF/jetty-env.xml文件对WebAppContext进行配置，并且绑定JNDI环境。<br />
<br />
TagLibConfiguration实现，在preConfigure方法中向WebAppContext注册TagLibListener(ServletContextListener)，在TagLibListener的contextInitialized方法中，它首先查找所有能找到的web.xml中定义的*.tld文件、WEB-INF/*.tld文件、WEB-INF/tlds/*.tld文件、以及通过WebInfConfiguration在jar包中找到的*.tld文件，将每个tld文件解析成一个TldDescriptor，并且使用TldProcessor对它们进行解析成EventListener列表，并注册到WebAppContext中。<br />
<br />
PlusConfiguration实现，在preConfigure中，它向WebAppContext添加PlusDecorator；在configure方法中添加PlusDescriptorProcessor。 <br />
<br />
<p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">在WebAppContex启动时：</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;"><br />
</span>
</p><p><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">1.&nbsp;根据WebAppContext的allowDuplicateFragmentNames属性设置MetaData实例对应的属性。</span></p>
<p>&nbsp;</p>
<p style="margin-bottom: 0pt; margin-top: 0pt;"><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">2.&nbsp;</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">调用preConfigure方法，它加载所有Configuration实例（用户自定义或默认设置：WebInfConfiguration、WebXmlConfiguration、MetaInfConfiguration、FragmentConfiguration、JettyWebXmlConfiguration）；加载系统类规则集合（即不能被Web&nbsp;Application覆盖的类，他们必须使用System&nbsp;ClassLoader加载，可以通过Server属性org.eclipse.jetty.webapp.sysemClasses属性定义，或者使用默认值）以及Server类规则集合（不能被Web&nbsp;Application加载的类，他们需要使用System&nbsp;ClassLoader加载，可以使用Server属性org.eclipse.jetty.webapp.serverClasses定义，或者使用默认值，这两个的区别参考WebAppClassLoader的实现解析）；设置ClassLoader，默认为WebAppClassLoader；调用所有Configuration的preConfigure方法。</span></p>
<p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">3.&nbsp;</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">调用startContext方法，他会调用Configuration的configure方法，以及MetaData的resolve方法；在MetaData的resolve方法中，他首先设置WebAppContext的javax.servlet.context.orderedLibs属性，然后设置ServletContext的EffectiveMajorVersion和EffectiveMinorVersion，并遍历通过Configuration注册的DescriptorProcessor，对webDefaultRoots、webXmlRoot、webOverrideRoots等Descriptor进行处理，以读取Servlet、Filter、Listener等信息的定义，遍历在Configuration中注册的DiscoveredAnnotation，对所有找到的WebFilter、WebServlet、WebListener注解类进行解析并添加到WeAppContext中，最后对在FragmentConfiguration中注册的FragmentDescriptor以及DiscoveredAnnotation进行相应的处理已进一步配置WebAppContext。</span></p>
<p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">4.&nbsp;</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">调用postConfiguration方法，即调用所有注册的Configuration的postConfigure方法以做一些清理工作。</span></p>
<p style="margin-bottom:8.7000pt; margin-top:8.7000pt; text-autospace:ideograph-other; line-height:15.6000pt; "><span style="color: #ff6600; font-weight: bold; font-size: 15.5pt; font-family: Georgia;">WebApp</span><span style="color: #ff6600; font-weight: bold; font-size: 15.5pt; font-family: 宋体;">ClassLoader</span><span style="color: #ff6600; font-weight: bold; font-size: 15.5pt; font-family: Georgia;">实现</span></p>
<p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">WebAppClassLoader是Jetty中用于对Servlet规范的ClassLoader的实现，它集成子URLClassLoader。它不会加载任何System&nbsp;Class（使用System&nbsp;ClassLoader加载），对Java2中父ClassLoader优先于子ClassLoader的规则，可以使用WebAppContext的setParentLoadPriority为true来配置。如果没有配置父ClassLoader，则使用当前的Thread&nbsp;Context&nbsp;ClassLoader，如果该ClassLoader也为null，则使用加载该类的ClassLoader，如果它还为null，则使用SystemClassLoader作为其父ClassLoader。</span></p>
<p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; ">&nbsp;</p>
<p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">在加载类时，WebAppClassLoader有SystemClass和ServerClass的类别，SystemClass是指在Web&nbsp;Application中可见的，但是他们不能被Web&nbsp;Application中的类(WEB-INF/classes，WEB-INF/lib中的类)覆盖的类，而ServerClass是指这些类是Server部分的实现，它对Web&nbsp;Application是不可见的，如果需要使用它们，可以将相应的jar包添加到WEB-INF/lib中。</span></p>
<p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; ">&nbsp;</p>
<p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">WebAppClassLoader默认支持.zip,.jar为扩展名的文件中查找class定义，可以使用org.eclipse.jetty.webapp.WebAppClassLoader.extensions系统属性添加更多的扩展名文件支持（以&#8220;,&#8221;或&#8220;;&#8221;分隔）。WebAppClassLoader也会添加WebAppContext中的ExtraClassPath到其ClassPath中（以&#8220;,&#8221;或&#8220;;&#8221;分隔），即添加URL。</span></p>
<p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; ">&nbsp;</p>
<p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">在WebInfConfiguration的configure方法中，他会默认的将所有WEB-INF/lib下的jar包以及WEB-INF/classes目录添加到WebAppClassLoader的ClassPath中，即添加URL。</span></p>
<p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; ">&nbsp;</p>
<p style="margin-bottom:0pt; margin-top:0pt; text-align:justify; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">在其loadClass的实现中，如果某class既是SystemClass又是ServerClass，则返回null；如果不是ServerClass，且是父ClassLoader优先或者是SystemClass，则使用父ClassLoader加载，然后再使用当前ClassLoader加载；在getResources和getResource的实现中，对于ServerClass它只能从当前ClassLoader中查找，对SystemClass它只能从父ClassLoader中查找。</span></p><img src ="http://www.blogjava.net/DLevin/aggbug/413817.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-05-24 22:17 <a href="http://www.blogjava.net/DLevin/archive/2014/05/24/413817.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之XmlConfiguration实现</title><link>http://www.blogjava.net/DLevin/archive/2014/05/24/414061.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sat, 24 May 2014 14:12:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/05/24/414061.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/414061.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/05/24/414061.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/414061.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/414061.html</trackback:ping><description><![CDATA[<p class="p0" style="margin-bottom:8.7000pt; margin-top:8.7000pt; text-autospace:ideograph-other; line-height:15.6000pt; "><span style="color: #ff6600; font-weight: bold; font-size: 15.5pt; font-family: Georgia;">概述</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">XmlConfiguration是Jetty中用来实现使用XML文件配置WebAppContext实例的框架，事实上，他可以用来配置任何的Object</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: Georgia; background: #ffffff;">。</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">类似Digester可以将一个XML配置文件直接实例化成一个类实例，XmlConfiguration可以使用一个XML文件初始化一个Object实例，它支持属性设置、方法调用、新建新实例等。</span></p><p style="margin-bottom:8.7000pt; margin-top:8.7000pt; text-autospace:ideograph-other; line-height:15.6000pt; "><span style="color: #ff6600; font-weight: bold; font-size: 15.5pt; font-family: 宋体;">XmlConfiguration实现</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">在XmlConfiguration的实现中，可以使用URL、configuration字符串、InputStream作为构造函数传入XmlConfiguration中，在XmlConfiguration的构造函数初始化时，它初始化XmlParser实例，使用XmlParser对XML文件进行解析成一个Node树，并将该树的根节点赋值给config字段；在初始化config字段的同时初始化processor字段，processor字段是ConfigurationProcessor类型，他用于解析XmlParser解析出的Node树的处理类，如果根节点的Tag是Configure，则processor使用Jetty中的默认实现：JettyXmlConfiguration类，否则使用ServiceLoader查找到的ConfigurationProcessorFactory实例，并调用其getConfigurationProcessor方法，传入dtd和tag作为参数，直到找到一个非null的ConfigurationProcessor；在创建出ConfigurationProcessor实例后，首先调用其init方法，在JettyXmlConfiguration类的实现中，init方法只是将传入的参数保存：Node树的config实例、以及两个idMap、propertyMap实例用于传递参数，其中idMap用于保存解析过程中所有id属性对应的实例，也可以用它来传递XML文件用于配置的实例以及其他ref引用的实例，而propertyMap用于定义一些XML文件中用到的属性对应的值。</span></p><p style="margin-bottom:0pt; margin-top:0pt; ">&nbsp;</p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">可以使用带Object实例的参数或不带参数两种方式调用configure方法，带参数表示将XML中内容配置实例中的属性，不带参数则先在内部创建class属性指定的类实例，然后使用XML的内容配置该新创建的实例的属性，如果根元素定义了id属性，则可以设置要配置的实例是idMap中该id值对应的实例。</span></p><p style="margin-bottom:0pt; margin-top:0pt; ">&nbsp;</p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">在configure方法的真正实现中，它遍历根节点的所有子节点，判断tag值（在这个过程中对任何有id定义的元素，将该id属性的值做为key，该元素对应的实例保存在idMap中）：</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;"><br /></span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">Set&nbsp;=&gt;&nbsp;用于设置name属性指定的set方法或字段的值，在查找方法中会有各种尝试：1.&nbsp;尝试当前值实例作为参数的set方法；2.&nbsp;使用Class实例的TYPE字段作为参数类型的set方法；3.&nbsp;尝试查找public类型的字段；4.&nbsp;尝试所有只有一个参数的对应的set方法；5.&nbsp;尝试是否可以将其转换成Set或数组类型的参数；6.&nbsp;尝试装箱后的参数。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">Put&nbsp;=&gt;&nbsp;要使用该方法，配置类型必须继承自Map接口，使用name属性作为key，获取其值实例，调用put方法。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">Call&nbsp;=&gt;&nbsp;指定class属性表示调用其静态方法；读取所有的Arg子节点（Arg节点必须紧跟Call节点），并且解析这些Arg的值类型，name属性指定方法名，在返回后如果其他节点定义，则使用它们配置返回的实例。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">Get&nbsp;=&gt;&nbsp;使用name属性指定的get无参数方法，如果没有找到get方法，则从name属性指定的字段中获取值，如果还有子元素，则对返回的实例进行配置。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">New&nbsp;=&gt;&nbsp;创建一个class指定的类实例，可以使用Arg指定构造函数参数。如果有除了Arg以外的节点定义，则使用它们对新创建的实例配置。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">Array&nbsp;=&gt;&nbsp;创建数组实例，type指定数组类型，支持以下列举的所有的值类型，使用Item指定它的元素。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">Ref&nbsp;=&gt;&nbsp;为传入的ref引用实例配置。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">Property&nbsp;=&gt;&nbsp;从传入的propertyMap中获取值，可以指定default属性。可以通过定义子元素继续配置返回的值。</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;"><br /></span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">Map&nbsp;=&gt;&nbsp;创建新的Map实例，使用Entry/Item,&nbsp;Item指定key、value的值。</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;"><br /></span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">对于所有值类型，可以指定其type属性，Jetty默认支持的类型有String、java.lang.String、URL、java.net.URL、InetAddress、java.net.InetAddress、boolean、byte、char、double、float、int、long、short、void、java.lang.Boolean.TYPE、java.lang.Byte.TYPE、java.lang.Character.TYPE、java.lang.Double.TYPE、java.lang.Float.TYPE、java.lang.Integer.TYPE、java.lang.Long.TYPE、java.lang.Short.TYPE、java.lang.Void.TPPE、Boolean、Byte、Character、Double、Float、Integer、Long、Short、null、string等。另外值类型还可以使用Call、Get、New、Ref、Array、Map、Property、SystemProperty定义。其中SystemProperty可以定义name和default属性用于表示prop名和default的值。ref引用必须先定义，因而这里无法处理循环引用的问题。<br /><br /></span></p><h2 style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #ff6600; font-weight: bold; font-size: 15.5pt; font-family: 宋体;">使用<br /></span></h2><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">可以使用XmlConfiguration，给定一个或多个Properties、XML文件参数，在Command&nbsp;Line中直接启动Jetty服务器。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">XmlConfiguration也可以用于EnvConfiguration中，用于进一步配置WebAppContext，可以手动设置jettyEnvXmlUrl属性，或默认使用WEB-INF/jetty-env.xml文件。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">XmlConfiguration还用于JettyWebXmlConfiguration，他默认查找WEB-INF目录下的jetty8-web.xml、jetty-web.xml、web-jetty.xml作为配置文件对WebAppContext做进一步的配置。<br /></span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;"><br /></span><span style="color: #ff6600; font-weight: bold; font-size: 15.5pt; font-family: 宋体;">附录</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;"><br /></span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">XmlConfiguration支持的XML文件格式使用configure_6_0.dtd文件定义：<br /></span></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">&lt;?</span><span style="color: #FF00FF; ">xml&nbsp;version="1.0"&nbsp;encoding="ISO-8859-1"</span><span style="color: #0000FF; ">?&gt;</span><br /><br /><span style="color: #008000; ">&lt;!--</span><span style="color: #008000; "><br />This&nbsp;is&nbsp;the&nbsp;document&nbsp;type&nbsp;descriptor&nbsp;for&nbsp;the<br />org.eclipse.XmlConfiguration&nbsp;class.&nbsp;&nbsp;It&nbsp;allows&nbsp;a&nbsp;java&nbsp;object&nbsp;to&nbsp;be<br />configured&nbsp;by&nbsp;with&nbsp;a&nbsp;sequence&nbsp;of&nbsp;Set,&nbsp;Put&nbsp;and&nbsp;Call&nbsp;elements.&nbsp;&nbsp;These&nbsp;tags&nbsp;are<br />mapped&nbsp;to&nbsp;methods&nbsp;on&nbsp;the&nbsp;object&nbsp;to&nbsp;be&nbsp;configured&nbsp;as&nbsp;follows:<br /><br />&nbsp;&nbsp;&lt;Set&nbsp;&nbsp;name="Test"&gt;value&lt;/Set&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;==&nbsp;&nbsp;obj.setTest("value");<br />&nbsp;&nbsp;&lt;Put&nbsp;&nbsp;name="Test"&gt;value&lt;/Put&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;==&nbsp;&nbsp;obj.put("Test","value");<br />&nbsp;&nbsp;&lt;Call&nbsp;name="test"&gt;&lt;Arg&gt;value&lt;/Arg&gt;&lt;/Call&gt;&nbsp;&nbsp;==&nbsp;&nbsp;obj.test("value");<br /><br />Values&nbsp;themselves&nbsp;may&nbsp;be&nbsp;configured&nbsp;objects&nbsp;that&nbsp;are&nbsp;created&nbsp;with&nbsp;the<br />&lt;New&gt;&nbsp;tag&nbsp;or&nbsp;returned&nbsp;from&nbsp;a&nbsp;&lt;Call&gt;&nbsp;tag.<br /><br />Values&nbsp;are&nbsp;matched&nbsp;to&nbsp;arguments&nbsp;on&nbsp;a&nbsp;best&nbsp;effort&nbsp;approach,&nbsp;but&nbsp;types<br />my&nbsp;be&nbsp;specified&nbsp;if&nbsp;a&nbsp;match&nbsp;is&nbsp;not&nbsp;achieved.<br /><br /></span><span style="color: #008000; ">--&gt;</span><br /><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ENTITY&nbsp;%&nbsp;CONFIG&nbsp;"Set|Get|Put|Call|New|Ref|Array|Map|Property"</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ENTITY&nbsp;%&nbsp;VALUE&nbsp;"#PCDATA|Get|Call|New|Ref|Array|Map|SystemProperty|Property"</span><span style="color: #0000FF; ">&gt;</span><br /><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ENTITY&nbsp;%&nbsp;TYPEATTR&nbsp;"type&nbsp;CDATA&nbsp;#IMPLIED&nbsp;"&nbsp;</span><span style="color: #0000FF; ">&gt;</span>&nbsp;<span style="color: #008000; ">&lt;!--</span><span style="color: #008000; ">&nbsp;String|Character|Short|Byte|Integer|Long|Boolean|Float|Double|char|short|byte|int|long|boolean|float|double|URL|InetAddress|InetAddrPort|&nbsp;#classname&nbsp;</span><span style="color: #008000; ">--&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ENTITY&nbsp;%&nbsp;IMPLIEDCLASSATTR&nbsp;"class&nbsp;NMTOKEN&nbsp;#IMPLIED"&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ENTITY&nbsp;%&nbsp;CLASSATTR&nbsp;"class&nbsp;NMTOKEN&nbsp;#REQUIRED"&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ENTITY&nbsp;%&nbsp;NAMEATTR&nbsp;"name&nbsp;NMTOKEN&nbsp;#REQUIRED"&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ENTITY&nbsp;%&nbsp;IMPLIEDNAMEATTR&nbsp;"name&nbsp;NMTOKEN&nbsp;#IMPLIED"&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ENTITY&nbsp;%&nbsp;DEFAULTATTR&nbsp;"default&nbsp;CDATA&nbsp;#IMPLIED"&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ENTITY&nbsp;%&nbsp;IDATTR&nbsp;"id&nbsp;NMTOKEN&nbsp;#IMPLIED"&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ENTITY&nbsp;%&nbsp;REQUIREDIDATTR&nbsp;"id&nbsp;NMTOKEN&nbsp;#REQUIRED"&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><br /><br /><span style="color: #008000; ">&lt;!--</span><span style="color: #008000; "><br />Configure&nbsp;Element.<br />This&nbsp;is&nbsp;the&nbsp;root&nbsp;element&nbsp;that&nbsp;specifies&nbsp;the&nbsp;class&nbsp;of&nbsp;object&nbsp;that<br />can&nbsp;be&nbsp;configured:<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;Configure&nbsp;class="com.acme.MyClass"&gt;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" />&nbsp;&lt;/Configure&gt;<br /></span><span style="color: #008000; ">--&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ELEMENT&nbsp;Configure&nbsp;(%CONFIG;)*&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ATTLIST&nbsp;Configure&nbsp;%IMPLIEDCLASSATTR;&nbsp;%IDATTR;&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><br /><br /><span style="color: #008000; ">&lt;!--</span><span style="color: #008000; "><br />Set&nbsp;Element.<br />This&nbsp;element&nbsp;maps&nbsp;to&nbsp;a&nbsp;call&nbsp;to&nbsp;a&nbsp;setter&nbsp;method&nbsp;or&nbsp;field&nbsp;on&nbsp;the&nbsp;current&nbsp;object.<br />The&nbsp;name&nbsp;and&nbsp;optional&nbsp;type&nbsp;attributes&nbsp;are&nbsp;used&nbsp;to&nbsp;select&nbsp;the&nbsp;setter<br />method.&nbsp;If&nbsp;the&nbsp;name&nbsp;given&nbsp;is&nbsp;xxx,&nbsp;then&nbsp;a&nbsp;setXxx&nbsp;method&nbsp;is&nbsp;used,&nbsp;or<br />the&nbsp;xxx&nbsp;field&nbsp;is&nbsp;used&nbsp;of&nbsp;setXxx&nbsp;cannot&nbsp;be&nbsp;found.<br />A&nbsp;Set&nbsp;element&nbsp;can&nbsp;contain&nbsp;value&nbsp;text&nbsp;and/or&nbsp;the&nbsp;value&nbsp;objects&nbsp;returned<br />by&nbsp;other&nbsp;elements&nbsp;such&nbsp;as&nbsp;Call,&nbsp;New,&nbsp;SystemProperty,&nbsp;etc.<br />If&nbsp;no&nbsp;value&nbsp;type&nbsp;is&nbsp;specified,&nbsp;then&nbsp;white<br />space&nbsp;is&nbsp;trimmed&nbsp;out&nbsp;of&nbsp;the&nbsp;value.&nbsp;If&nbsp;it&nbsp;contains&nbsp;multiple&nbsp;value<br />elements&nbsp;they&nbsp;are&nbsp;added&nbsp;as&nbsp;strings&nbsp;before&nbsp;being&nbsp;converted&nbsp;to&nbsp;any<br />specified&nbsp;type.<br /><br />A&nbsp;Set&nbsp;with&nbsp;a&nbsp;class&nbsp;attribute&nbsp;is&nbsp;treated&nbsp;as&nbsp;a&nbsp;static&nbsp;set&nbsp;method&nbsp;invocation.<br /></span><span style="color: #008000; ">--&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ELEMENT&nbsp;Set&nbsp;(&nbsp;%VALUE;&nbsp;)*&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ATTLIST&nbsp;Set&nbsp;%NAMEATTR;&nbsp;%TYPEATTR;&nbsp;%IMPLIEDCLASSATTR;&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><br /><br /><span style="color: #008000; ">&lt;!--</span><span style="color: #008000; "><br />Get&nbsp;Element.<br />This&nbsp;element&nbsp;maps&nbsp;to&nbsp;a&nbsp;call&nbsp;to&nbsp;a&nbsp;getter&nbsp;method&nbsp;or&nbsp;field&nbsp;on&nbsp;the&nbsp;current&nbsp;object.<br />The&nbsp;name&nbsp;attribute&nbsp;is&nbsp;used&nbsp;to&nbsp;select&nbsp;the&nbsp;get&nbsp;method.<br />If&nbsp;the&nbsp;name&nbsp;given&nbsp;is&nbsp;xxx,&nbsp;then&nbsp;a&nbsp;getXxx&nbsp;method&nbsp;is&nbsp;used,&nbsp;or<br />the&nbsp;xxx&nbsp;field&nbsp;is&nbsp;used&nbsp;if&nbsp;getXxx&nbsp;cannot&nbsp;be&nbsp;found.<br />A&nbsp;Get&nbsp;element&nbsp;can&nbsp;contain&nbsp;other&nbsp;elements&nbsp;such&nbsp;as&nbsp;Set,&nbsp;Put,&nbsp;Call,&nbsp;etc.<br />which&nbsp;act&nbsp;on&nbsp;the&nbsp;object&nbsp;returned&nbsp;by&nbsp;the&nbsp;get&nbsp;call.<br /><br />A&nbsp;Get&nbsp;with&nbsp;a&nbsp;class&nbsp;attribute&nbsp;is&nbsp;treated&nbsp;as&nbsp;a&nbsp;static&nbsp;get&nbsp;method&nbsp;or&nbsp;field.<br /></span><span style="color: #008000; ">--&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ELEMENT&nbsp;Get&nbsp;(%CONFIG;)*</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ATTLIST&nbsp;Get&nbsp;%NAMEATTR;&nbsp;%IMPLIEDCLASSATTR;&nbsp;%IDATTR;&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><br /><br /><span style="color: #008000; ">&lt;!--</span><span style="color: #008000; "><br />Put&nbsp;Element.<br />This&nbsp;element&nbsp;maps&nbsp;to&nbsp;a&nbsp;call&nbsp;to&nbsp;a&nbsp;put&nbsp;method&nbsp;on&nbsp;the&nbsp;current&nbsp;object,<br />which&nbsp;must&nbsp;implement&nbsp;the&nbsp;Map&nbsp;interface.&nbsp;The&nbsp;name&nbsp;attribute&nbsp;is&nbsp;used<br />as&nbsp;the&nbsp;put&nbsp;key&nbsp;and&nbsp;the&nbsp;optional&nbsp;type&nbsp;attribute&nbsp;can&nbsp;force&nbsp;the&nbsp;type<br />of&nbsp;the&nbsp;value.<br /><br />A&nbsp;Put&nbsp;element&nbsp;can&nbsp;contain&nbsp;value&nbsp;text&nbsp;and/or&nbsp;value&nbsp;elements&nbsp;such&nbsp;as&nbsp;Call,<br />New,&nbsp;SystemProperty,&nbsp;etc.&nbsp;If&nbsp;no&nbsp;value&nbsp;type&nbsp;is&nbsp;specified,&nbsp;then&nbsp;white<br />space&nbsp;is&nbsp;trimmed&nbsp;out&nbsp;of&nbsp;the&nbsp;value.&nbsp;If&nbsp;it&nbsp;contains&nbsp;multiple&nbsp;value<br />elements&nbsp;they&nbsp;are&nbsp;added&nbsp;as&nbsp;strings&nbsp;before&nbsp;being&nbsp;converted&nbsp;to&nbsp;any<br />specified&nbsp;type.<br /></span><span style="color: #008000; ">--&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ELEMENT&nbsp;Put&nbsp;(&nbsp;%VALUE;&nbsp;)*&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ATTLIST&nbsp;Put&nbsp;%NAMEATTR;&nbsp;%TYPEATTR;&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><br /><br /><span style="color: #008000; ">&lt;!--</span><span style="color: #008000; "><br />Call&nbsp;Element.<br />This&nbsp;element&nbsp;maps&nbsp;to&nbsp;an&nbsp;arbitrary&nbsp;call&nbsp;to&nbsp;a&nbsp;method&nbsp;on&nbsp;the&nbsp;current&nbsp;object,<br />The&nbsp;name&nbsp;attribute&nbsp;and&nbsp;Arg&nbsp;elements&nbsp;are&nbsp;used&nbsp;to&nbsp;select&nbsp;the&nbsp;method.<br /><br />A&nbsp;Call&nbsp;element&nbsp;can&nbsp;contain&nbsp;a&nbsp;sequence&nbsp;of&nbsp;Arg&nbsp;elements&nbsp;followed&nbsp;by<br />a&nbsp;sequence&nbsp;of&nbsp;other&nbsp;elements&nbsp;such&nbsp;as&nbsp;Set,&nbsp;Put,&nbsp;Call,&nbsp;etc.&nbsp;which&nbsp;act&nbsp;on&nbsp;any&nbsp;object<br />returned&nbsp;by&nbsp;the&nbsp;original&nbsp;call:<br /><br />&nbsp;&lt;Call&nbsp;id="o2"&nbsp;name="test"&gt;<br />&nbsp;&nbsp;&nbsp;&lt;Arg&gt;value1&lt;/Arg&gt;<br />&nbsp;&nbsp;&nbsp;&lt;Set&nbsp;name="Test"&gt;Value2&lt;/Set&gt;<br />&nbsp;&lt;/Call&gt;<br /><br />This&nbsp;is&nbsp;equivalent&nbsp;to:<br /><br />&nbsp;Object&nbsp;o2&nbsp;=&nbsp;o1.test("value1");<br />&nbsp;o2.setTest("value2");<br /><br />A&nbsp;Call&nbsp;with&nbsp;a&nbsp;class&nbsp;attribute&nbsp;is&nbsp;treated&nbsp;as&nbsp;a&nbsp;static&nbsp;call.<br /></span><span style="color: #008000; ">--&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ELEMENT&nbsp;Call&nbsp;(Arg*,(%CONFIG;)*)</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ATTLIST&nbsp;Call&nbsp;%NAMEATTR;&nbsp;%IMPLIEDCLASSATTR;&nbsp;%IDATTR;</span><span style="color: #0000FF; ">&gt;</span><br /><br /><br /><span style="color: #008000; ">&lt;!--</span><span style="color: #008000; "><br />Arg&nbsp;Element.<br />This&nbsp;element&nbsp;defines&nbsp;a&nbsp;positional&nbsp;argument&nbsp;for&nbsp;the&nbsp;Call&nbsp;element.<br />The&nbsp;optional&nbsp;type&nbsp;attribute&nbsp;can&nbsp;force&nbsp;the&nbsp;type&nbsp;of&nbsp;the&nbsp;value.<br /><br />An&nbsp;Arg&nbsp;element&nbsp;can&nbsp;contain&nbsp;value&nbsp;text&nbsp;and/or&nbsp;value&nbsp;elements&nbsp;such&nbsp;as&nbsp;Call,<br />New,&nbsp;SystemProperty,&nbsp;etc.&nbsp;If&nbsp;no&nbsp;value&nbsp;type&nbsp;is&nbsp;specified,&nbsp;then&nbsp;white<br />space&nbsp;is&nbsp;trimmed&nbsp;out&nbsp;of&nbsp;the&nbsp;value.&nbsp;If&nbsp;it&nbsp;contains&nbsp;multiple&nbsp;value<br />elements&nbsp;they&nbsp;are&nbsp;added&nbsp;as&nbsp;strings&nbsp;before&nbsp;being&nbsp;converted&nbsp;to&nbsp;any<br />specified&nbsp;type.<br /></span><span style="color: #008000; ">--&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ELEMENT&nbsp;Arg&nbsp;(&nbsp;%VALUE;&nbsp;)*&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ATTLIST&nbsp;Arg&nbsp;%TYPEATTR;&nbsp;%IMPLIEDNAMEATTR;&nbsp;&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><br /><br /><span style="color: #008000; ">&lt;!--</span><span style="color: #008000; "><br />New&nbsp;Element.<br />This&nbsp;element&nbsp;allows&nbsp;the&nbsp;creation&nbsp;of&nbsp;a&nbsp;new&nbsp;object&nbsp;as&nbsp;part&nbsp;of&nbsp;a<br />value&nbsp;for&nbsp;elements&nbsp;such&nbsp;as&nbsp;Set,&nbsp;Put,&nbsp;Arg,&nbsp;etc.&nbsp;The&nbsp;class&nbsp;attribute&nbsp;determines<br />the&nbsp;type&nbsp;of&nbsp;the&nbsp;new&nbsp;object&nbsp;and&nbsp;the&nbsp;contained&nbsp;Arg&nbsp;elements<br />are&nbsp;used&nbsp;to&nbsp;select&nbsp;the&nbsp;constructor&nbsp;for&nbsp;the&nbsp;new&nbsp;object.<br /><br />A&nbsp;New&nbsp;element&nbsp;can&nbsp;contain&nbsp;a&nbsp;sequence&nbsp;of&nbsp;Arg&nbsp;elements&nbsp;followed&nbsp;by<br />a&nbsp;sequence&nbsp;of&nbsp;elements&nbsp;such&nbsp;as&nbsp;Set,&nbsp;Put,&nbsp;Call,&nbsp;etc.&nbsp;elements<br />which&nbsp;act&nbsp;on&nbsp;the&nbsp;new&nbsp;object:<br /><br />&nbsp;&lt;New&nbsp;id="o"&nbsp;class="com.acme.MyClass"&gt;<br />&nbsp;&nbsp;&nbsp;&lt;Arg&gt;value1&lt;/Arg&gt;<br />&nbsp;&nbsp;&nbsp;&lt;Set&nbsp;name="test"&gt;Value2&lt;/Set&gt;<br />&nbsp;&lt;/New&gt;<br /><br />This&nbsp;is&nbsp;equivalent&nbsp;to:<br /><br />&nbsp;Object&nbsp;o&nbsp;=&nbsp;new&nbsp;com.acme.MyClass("value1");<br />&nbsp;o.setTest("Value2");<br /></span><span style="color: #008000; ">--&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ELEMENT&nbsp;New&nbsp;(Arg*,(%CONFIG;)*)</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ATTLIST&nbsp;New&nbsp;%CLASSATTR;&nbsp;%IDATTR;</span><span style="color: #0000FF; ">&gt;</span><br /><br /><br /><span style="color: #008000; ">&lt;!--</span><span style="color: #008000; "><br />Ref&nbsp;Element.<br />This&nbsp;element&nbsp;allows&nbsp;a&nbsp;previously&nbsp;created&nbsp;object&nbsp;to&nbsp;be&nbsp;referenced&nbsp;by&nbsp;id.<br />A&nbsp;Ref&nbsp;element&nbsp;can&nbsp;contain&nbsp;a&nbsp;sequence&nbsp;of&nbsp;elements&nbsp;such&nbsp;as&nbsp;Set,&nbsp;Put,&nbsp;Call,&nbsp;etc.<br />which&nbsp;act&nbsp;on&nbsp;the&nbsp;referenced&nbsp;object:<br /><br />&nbsp;&lt;Ref&nbsp;id="myobject"&gt;<br />&nbsp;&nbsp;&nbsp;&lt;Set&nbsp;name="Test"&gt;Value2&lt;/Set&gt;<br />&nbsp;&lt;/New&gt;<br /></span><span style="color: #008000; ">--&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ELEMENT&nbsp;Ref&nbsp;((%CONFIG;)*)</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ATTLIST&nbsp;Ref&nbsp;%REQUIREDIDATTR;</span><span style="color: #0000FF; ">&gt;</span><br /><br /><br /><span style="color: #008000; ">&lt;!--</span><span style="color: #008000; "><br />Array&nbsp;Element.<br />This&nbsp;element&nbsp;allows&nbsp;the&nbsp;creation&nbsp;of&nbsp;a&nbsp;new&nbsp;array&nbsp;as&nbsp;part&nbsp;of&nbsp;a<br />value&nbsp;of&nbsp;elements&nbsp;such&nbsp;as&nbsp;Set,&nbsp;Put,&nbsp;Arg,&nbsp;etc.&nbsp;The&nbsp;type&nbsp;attribute&nbsp;determines<br />the&nbsp;type&nbsp;of&nbsp;the&nbsp;new&nbsp;array&nbsp;and&nbsp;the&nbsp;contained&nbsp;Item&nbsp;elements<br />are&nbsp;used&nbsp;for&nbsp;each&nbsp;element&nbsp;of&nbsp;the&nbsp;array:<br /><br />&nbsp;&lt;Array&nbsp;type="java.lang.String"&gt;<br />&nbsp;&nbsp;&nbsp;&lt;Item&gt;value0&lt;/Item&gt;<br />&nbsp;&nbsp;&nbsp;&lt;Item&gt;&lt;New&nbsp;class="java.lang.String"&gt;&lt;Arg&gt;value1&lt;/Arg&gt;&lt;/New&gt;&lt;/Item&gt;<br />&nbsp;&lt;/Array&gt;<br /><br />This&nbsp;is&nbsp;equivalent&nbsp;to:<br />&nbsp;String[]&nbsp;a&nbsp;=&nbsp;new&nbsp;String[]&nbsp;{&nbsp;"value0",&nbsp;new&nbsp;String("value1")&nbsp;};<br /></span><span style="color: #008000; ">--&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ELEMENT&nbsp;Array&nbsp;(Item*)</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ATTLIST&nbsp;Array&nbsp;%TYPEATTR;&nbsp;%IDATTR;&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><br /><br /><span style="color: #008000; ">&lt;!--</span><span style="color: #008000; "><br />Map&nbsp;Element.<br />This&nbsp;element&nbsp;allows&nbsp;the&nbsp;creation&nbsp;of&nbsp;a&nbsp;new&nbsp;map&nbsp;as&nbsp;part&nbsp;of&nbsp;a<br />value&nbsp;of&nbsp;elements&nbsp;such&nbsp;as&nbsp;Set,&nbsp;Put,&nbsp;Arg,&nbsp;etc.&nbsp;The&nbsp;type&nbsp;attribute&nbsp;determines<br />the&nbsp;type&nbsp;of&nbsp;the&nbsp;new&nbsp;array&nbsp;and&nbsp;the&nbsp;contained&nbsp;Item&nbsp;elements<br />are&nbsp;used&nbsp;for&nbsp;each&nbsp;element&nbsp;of&nbsp;the&nbsp;array:<br /><br />&nbsp;&lt;Map&gt;<br />&nbsp;&nbsp;&nbsp;&lt;Entry&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Item&gt;keyName&lt;/Item&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Item&gt;&lt;New&nbsp;class="java.lang.String"&gt;&lt;Arg&gt;value1&lt;/Arg&gt;&lt;/New&gt;&lt;/Item&gt;<br />&nbsp;&nbsp;&nbsp;&lt;/Entry&gt;<br />&nbsp;&lt;/Map&gt;<br /><br />This&nbsp;is&nbsp;equivalent&nbsp;to:<br />&nbsp;Map&nbsp;m&nbsp;=&nbsp;new&nbsp;HashMap();<br />&nbsp;m.put("keyName",&nbsp;new&nbsp;String("value1"));<br /></span><span style="color: #008000; ">--&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ELEMENT&nbsp;Map&nbsp;(Entry*)</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ATTLIST&nbsp;Map&nbsp;%IDATTR;&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ELEMENT&nbsp;Entry&nbsp;(Item,Item)</span><span style="color: #0000FF; ">&gt;</span><br /><br /><br /><span style="color: #008000; ">&lt;!--</span><span style="color: #008000; "><br />Item&nbsp;Element.<br />This&nbsp;element&nbsp;defines&nbsp;an&nbsp;entry&nbsp;for&nbsp;the&nbsp;Array&nbsp;or&nbsp;Map&nbsp;Entry&nbsp;elements.<br />The&nbsp;optional&nbsp;type&nbsp;attribute&nbsp;can&nbsp;force&nbsp;the&nbsp;type&nbsp;of&nbsp;the&nbsp;value.<br /><br />An&nbsp;Item&nbsp;element&nbsp;can&nbsp;contain&nbsp;value&nbsp;text&nbsp;and/or&nbsp;the&nbsp;value&nbsp;object&nbsp;of<br />elements&nbsp;such&nbsp;as&nbsp;Call,&nbsp;New,&nbsp;SystemProperty,&nbsp;etc.&nbsp;If&nbsp;no&nbsp;value&nbsp;type<br />is&nbsp;specified,&nbsp;then&nbsp;white&nbsp;space&nbsp;is&nbsp;trimmed&nbsp;out&nbsp;of&nbsp;the&nbsp;value.<br />If&nbsp;it&nbsp;contains&nbsp;multiple&nbsp;value&nbsp;elements&nbsp;they&nbsp;are&nbsp;added&nbsp;as&nbsp;strings<br />before&nbsp;being&nbsp;converted&nbsp;to&nbsp;any&nbsp;specified&nbsp;type.<br /></span><span style="color: #008000; ">--&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ELEMENT&nbsp;Item&nbsp;(&nbsp;%VALUE;&nbsp;)*&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ATTLIST&nbsp;Item&nbsp;%TYPEATTR;&nbsp;%IDATTR;&nbsp;</span><span style="color: #0000FF; ">&gt;</span><br /><br /><br /><span style="color: #008000; ">&lt;!--</span><span style="color: #008000; "><br />System&nbsp;Property&nbsp;Element.<br />This&nbsp;element&nbsp;allows&nbsp;JVM&nbsp;System&nbsp;properties&nbsp;to&nbsp;be&nbsp;retrieved&nbsp;as<br />part&nbsp;of&nbsp;the&nbsp;value&nbsp;of&nbsp;elements&nbsp;such&nbsp;as&nbsp;Set,&nbsp;Put,&nbsp;Arg,&nbsp;etc.<br />The&nbsp;name&nbsp;attribute&nbsp;specifies&nbsp;the&nbsp;property&nbsp;name&nbsp;and&nbsp;the&nbsp;optional<br />default&nbsp;argument&nbsp;provides&nbsp;a&nbsp;default&nbsp;value.<br /><br />&nbsp;&lt;SystemProperty&nbsp;name="Test"&nbsp;default="value"&nbsp;/&gt;<br /><br />This&nbsp;is&nbsp;equivalent&nbsp;to:<br /><br />&nbsp;System.getProperty("Test","value");<br /></span><span style="color: #008000; ">--&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ELEMENT&nbsp;SystemProperty&nbsp;EMPTY</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ATTLIST&nbsp;SystemProperty&nbsp;%NAMEATTR;&nbsp;%DEFAULTATTR;&nbsp;%IDATTR;</span><span style="color: #0000FF; ">&gt;</span><br /><br /><br /><span style="color: #008000; ">&lt;!--</span><span style="color: #008000; "><br />Property&nbsp;Element.<br />This&nbsp;element&nbsp;allows&nbsp;arbitrary&nbsp;properties&nbsp;to&nbsp;be&nbsp;retrieved&nbsp;by&nbsp;name.<br />The&nbsp;name&nbsp;attribute&nbsp;specifies&nbsp;the&nbsp;property&nbsp;name&nbsp;and&nbsp;the&nbsp;optional<br />default&nbsp;argument&nbsp;provides&nbsp;a&nbsp;default&nbsp;value.<br /><br />A&nbsp;Property&nbsp;element&nbsp;can&nbsp;contain&nbsp;a&nbsp;sequence&nbsp;of&nbsp;elements&nbsp;such&nbsp;as&nbsp;Set,&nbsp;Put,&nbsp;Call,&nbsp;etc.<br />which&nbsp;act&nbsp;on&nbsp;the&nbsp;retrieved&nbsp;object:<br /><br />&nbsp;&lt;Property&nbsp;name="Server"&gt;<br />&nbsp;&nbsp;&nbsp;&lt;Call&nbsp;id="jdbcIdMgr"&nbsp;name="getAttribute"&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Arg&gt;jdbcIdMgr&lt;/Arg&gt;<br />&nbsp;&nbsp;&nbsp;&lt;/Call&gt;<br />&nbsp;&lt;/Property&gt;<br /></span><span style="color: #008000; ">--&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ELEMENT&nbsp;Property&nbsp;((%CONFIG;)*)</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;!</span><span style="color: #FF00FF; ">ATTLIST&nbsp;Property&nbsp;%NAMEATTR;&nbsp;%DEFAULTATTR;&nbsp;%IDATTR;</span><span style="color: #0000FF; ">&gt;</span></div><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">一个简单的例子（摘自：</span>http://www.blogjava.net/xylz/archive/2012/04/12/372999.html<span style="color: #4b4b4b; font-family: 宋体; font-size: 9.5pt; background-color: #ffffff;">）：</span></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Configure&nbsp;</span><span style="color: #FF0000; ">id</span><span style="color: #0000FF; ">="Server"</span><span style="color: #FF0000; ">&nbsp;class</span><span style="color: #0000FF; ">="org.eclipse.jetty.server.Server"</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="ThreadPool"</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">New&nbsp;</span><span style="color: #FF0000; ">class</span><span style="color: #0000FF; ">="org.eclipse.jetty.util.thread.QueuedThreadPool"</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="minThreads"</span><span style="color: #0000FF; ">&gt;</span>10<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="maxThreads"</span><span style="color: #0000FF; ">&gt;</span>200<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="detailedDump"</span><span style="color: #0000FF; ">&gt;</span>false<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">New</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Call&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="addConnector"</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Arg</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">New&nbsp;</span><span style="color: #FF0000; ">class</span><span style="color: #0000FF; ">="org.eclipse.jetty.server.nio.SelectChannelConnector"</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="host"</span><span style="color: #0000FF; ">&gt;&lt;</span><span style="color: #800000; ">Property&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="Inside&nbsp;in&nbsp;Jetty.host"</span><span style="color: #FF0000; ">&nbsp;</span><span style="color: #0000FF; ">/&gt;&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="port"</span><span style="color: #0000FF; ">&gt;&lt;</span><span style="color: #800000; ">Property&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="Inside&nbsp;in&nbsp;Jetty.port"</span><span style="color: #FF0000; ">&nbsp;default</span><span style="color: #0000FF; ">="8080"</span><span style="color: #0000FF; ">/&gt;&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="maxIdleTime"</span><span style="color: #0000FF; ">&gt;</span>300000<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="Acceptors"</span><span style="color: #0000FF; ">&gt;</span>2<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="statsOn"</span><span style="color: #0000FF; ">&gt;</span>false<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="confidentialPort"</span><span style="color: #0000FF; ">&gt;</span>8443<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="lowResourcesConnections"</span><span style="color: #0000FF; ">&gt;</span>20000<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="lowResourcesMaxIdleTime"</span><span style="color: #0000FF; ">&gt;</span>5000<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">New</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Arg</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Call</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="handler"</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">New&nbsp;</span><span style="color: #FF0000; ">id</span><span style="color: #0000FF; ">="Handlers"</span><span style="color: #FF0000; ">&nbsp;class</span><span style="color: #0000FF; ">="org.eclipse.jetty.server.handler.HandlerCollection"</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="handlers"</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Array&nbsp;</span><span style="color: #FF0000; ">type</span><span style="color: #0000FF; ">="org.eclipse.jetty.server.Handler"</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Item</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">New&nbsp;</span><span style="color: #FF0000; ">id</span><span style="color: #0000FF; ">="Contexts"</span><span style="color: #FF0000; ">&nbsp;class</span><span style="color: #0000FF; ">="org.eclipse.jetty.server.handler.ContextHandlerCollection"</span><span style="color: #0000FF; ">/&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Item</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Item</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">New&nbsp;</span><span style="color: #FF0000; ">id</span><span style="color: #0000FF; ">="DefaultHandler"</span><span style="color: #FF0000; ">&nbsp;class</span><span style="color: #0000FF; ">="org.eclipse.jetty.server.handler.DefaultHandler"</span><span style="color: #0000FF; ">/&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Item</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Array</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">New</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="stopAtShutdown"</span><span style="color: #0000FF; ">&gt;</span>true<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="sendServerVersion"</span><span style="color: #0000FF; ">&gt;</span>true<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="sendDateHeader"</span><span style="color: #0000FF; ">&gt;</span>true<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="gracefulShutdown"</span><span style="color: #0000FF; ">&gt;</span>1000<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="dumpAfterStart"</span><span style="color: #0000FF; ">&gt;</span>false<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">Set&nbsp;</span><span style="color: #FF0000; ">name</span><span style="color: #0000FF; ">="dumpBeforeStop"</span><span style="color: #0000FF; ">&gt;</span>false<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Set</span><span style="color: #0000FF; ">&gt;</span><br /><span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">Configure</span><span style="color: #0000FF; ">&gt;</span></div><img src ="http://www.blogjava.net/DLevin/aggbug/414061.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-05-24 22:12 <a href="http://www.blogjava.net/DLevin/archive/2014/05/24/414061.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之DescriptorProcessor实现</title><link>http://www.blogjava.net/DLevin/archive/2014/05/24/414034.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sat, 24 May 2014 14:06:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/05/24/414034.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/414034.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/05/24/414034.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/414034.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/414034.html</trackback:ping><description><![CDATA[<h2><span style="color: #ff6600;">概述</span></h2><p style="margin-bottom: 0pt; margin-top: 0pt;"><span style="color: #4b4b4b; font-size: 9.5pt; font-family: Georgia; background: #ffffff;">在Jetty中，所有XML文件的配置使用Descriptor来表达，而对这些Descriptor的处理使用DescriptorProcessor来实现。</span></p><br /><h2><span style="color: #ff6600;">Descriptor和DescriptorProcessor类图</span></h2><img src="http://www.blogjava.net/images/blogjava_net/dlevin/DescriptorProcessor.jpg" alt="" height="381" width="1022" /><br /><br /><p style="margin-bottom:8.7000pt; margin-top:8.7000pt; text-autospace:ideograph-other; line-height:15.6000pt; "><span style="color: #ff6600; font-weight: bold; font-size: 15.5pt; font-family: 宋体;">Descriptor实现</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: Georgia; background: #ffffff;">Descriptor可以表达一个*.tld文件（TldDescriptor）、</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">一个/META-INF/web.xml文件(WebDescriptor)，一个/org/eclipse/jetty/webapp/webdefault.xml（DefaultsDescriptor），一个/META-INF/web-fragment.xml文件（FragmentDescriptor），一个override-web.xml文件（OverrideDescriptor）。其中TldDescriptor在TagLibConfiguration的TagListener中查找并使用TldProcessor解析；WebDescriptor在WebXmlConfiguration的preConfigure中查找，并设置到MetaData的webXmlRoot字段中，并更新MetaData的ordering字段，其资源文件可以手动设置WebAppContext的descriptor字段，或者未设置而使用META-INF/web.xml文件；DefaultsDescriptor也在WebXmlConfiguration的preConfigure中查找，并设置到MetaData的webDefaultsRoot字段中，并更新MetaData的ordering字段，其资源文件可以手动设置WebAppContext中的defaultsDescriptor字段，或未设置而默认使用/org/eclipse/jetty/webapp/webdefault.xml文件；OverrideDescriptor也在WebXmlConfiguration的preConfigure中查找，并设置到MetaData的webOverrideRoots集合中，并更新MetaData中的ordering字段，其资源文件可以手动设置，如果未设置，则忽略；而FragmentDescriptor则是在FragmentConfiguration中的preConfigure中添加到MetaData的webFragmentResourceMap、webFragmentNameMap以及webFragmentRoots中，如果MetaData的ordering为null，且不为absolute，则更新ordering字段。</span></p><p style="margin-bottom:0pt; margin-top:0pt; ">&nbsp;</p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">每个Descriptor使用一个xml的Resource实例作为构造函数构建，并使用XmlParser将其解析成类DOM树，保存树的root节点引用。</span></p><p style="margin-bottom:0pt; margin-top:0pt; ">&nbsp;</p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">除了TldDescriptor在TagLibConfiguration中已经处理完成，其他的Descriptor使用StandardDescriptorProcessor以及PlusDescriptorProcessor来处理，其中StandardDescriptorProcessor在WebXmlConfiguration的configure方法中注册到MetaData的descriptorProcessors集合中，而PlusDescriptorProcessor在PlusConfiguration的configure方法中注册到MetaData中。并在MataData的resolve方法中使用注册的DescriptorProcessor依次解析webDefaultsRoot、webXmlRoot、webOverrideRoots以及webFragmentRoots对应的Descriptor实例。</span></p><p style="margin-bottom:8.7000pt; margin-top:8.7000pt; text-autospace:ideograph-other; line-height:15.6000pt; "><span style="color: #ff6600; font-weight: bold; font-size: 15.5pt; font-family: 宋体;">DescriptorProcessor实现</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">DescriptorProcessor只有一个process方法，他遍历传入的Descriptor的所有Node，并对不同Node做相应的处理。在IterativeDescriptorProcessor的采用了非常巧妙的实现方法，即使用一个visitors的Map，包含节点的tag到相应处理方法的映射，因而在IterativeDescriptorProcessor的实现中，它遍历Descriptor的节点树，对每个节点查找对应的处理方法，并调用查找到的方法，其子类的实现只需要注册这个visitors的Map，然后实现注册的方法即可；为了增加可扩展性，在解析前和解析后分别添加了start、end的插入点。</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;"><br /></span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;"><br /></span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">如在StandardDescriptorProcessor中，注册了如下几个visitor方法：</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;"><br /></span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">context-param&nbsp;=&gt;&nbsp;visitContextParam&nbsp;向WebAppContext添加InitParam信息。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">display-name&nbsp;=&gt;&nbsp;visitDisplayName&nbsp;向WebAppContext设置displayName属性。</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;"><br /></span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">servlet&nbsp;=&gt;&nbsp;visitServlet&nbsp;向ServletHandler中添加一个新的ServletHolder，并配置其servlet-name、init-param、servlet-class、jsp-file、load-on-startup、security-role-ref、run-as、async-supported、enabled、multipart-config等信息；如果id设置为jsp，则会在InitParam中配置scratchdir、classpath参数，以及为Jasper配置com.sun.appserv.jsp.classpath参数，而在WebAppContext中为Jasper配置org.apache.catalina.jsp_classpath属性；用于注册org.apache.jasper.servlet.JspServlet；对jsp-file，设置其forcePath为该值。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">servlet-mapping=&gt;&nbsp;visitServletMapping&nbsp;配置ServletHandler中servlet-name对应的ServletMapping信息。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">session-config&nbsp;=&gt;&nbsp;visitSessionConfig&nbsp;设置SessionHandler中SessionManager的一些配置信息。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">mime-mapping&nbsp;=&gt;&nbsp;visitMimeMapping&nbsp;设置WebAppContext中extension到mimeType的映射。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">welcome-file-list&nbsp;=&gt;&nbsp;visitWelcomeFileList&nbsp;设置WebAppContext中的welcomeFiles。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">locale-encoding-mapping-list&nbsp;=&gt;&nbsp;visitLocaleEncodingList&nbsp;设置WebAppContext中locale到encoding的映射关系。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">error-page&nbsp;=&gt;&nbsp;visitErrorPage&nbsp;设置ErrorPageErrorHandler中errorCode或exceptionType到location的映射关系。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">taglib&nbsp;=&gt;&nbsp;visitTagLib&nbsp;设置taglib-uri到taglib-location的映射关系，即WebAppContext中taglib-uri是taglib-location的alias。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">jsp-config&nbsp;=&gt;&nbsp;visitJspConfig&nbsp;将jsp-property-group下url-pattern映射到JspServlet中。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">security-constraint&nbsp;=&gt;&nbsp;visitSecurityConstraint&nbsp;向SecurityHandler中添加ConstraintMapping。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">login-config&nbsp;=&gt;&nbsp;visitLoginConfig&nbsp;向SecurityHandler中设置AuthMethod、RealmName属性，以及对FORM方法的验证，设置login、error页面的InitParam。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">security-role&nbsp;=&gt;&nbsp;visitSecurityRole&nbsp;向SecurityHandler中注册定义的role集合。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">filter&nbsp;=&gt;&nbsp;visitFilter&nbsp;向ServletHandler注册FilterHolder，并配置filter-name、filter-class、init-param、async-supported等信息。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">filter-mapping&nbsp;=&gt;&nbsp;visitFilterMapping&nbsp;向ServletHandler注册FilterMapping信息。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">listenr&nbsp;=&gt;&nbsp;visitListener&nbsp;向WebAppContext注册EventListener。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">distributable&nbsp;=&gt;&nbsp;visitDestributable&nbsp;设置WebDescriptor的distributable属性为true。</span></p><p style="margin-bottom:0pt; margin-top:0pt; ">&nbsp;</p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">在PlusDescriptorProcessor中，首先在其start方法中会向WebAppContext注册InjectionCollection、LifeCycleCallbackCollection、RunAsCollection（该属性在RunAsAnnotationHandler中使用）属性，并且注册了以下几个visitor方法：</span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;"><br /></span><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">env-entry&nbsp;=&gt;&nbsp;visitEnvEntry&nbsp;向InjectionCollection添加Injection实例，其中jndiName为env-entry-name定义的值，valueClass为env-entry-type定义的类型，而targetClass、targetName为injection-target下的injection-target-class、injection-target-name中定义的值，每个injection-target生成一个Injection实例。同时将env-entry-value中定义的值绑定到java:com/env/&lt;name&gt;对应的资源中。（Injection实例也可以使用@Resource注解注册，并在ResourceAnnotationHandler中解析）</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">resource-ref&nbsp;=&gt;&nbsp;visitResourceRef&nbsp;向InjectionCollection添加Injection实例，其中jndiName为res-ref-name，typeClass为res-type，并绑定该引用资源。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">resource-env-ref&nbsp;=&gt;&nbsp;visitResourceEnvRef&nbsp;向InjectionCollection添加Injection实例，其中jndiName为resource-env-ref-name，typeClass为resource-env-ref-type，并绑定该env引用资源。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">message-destination-ref&nbsp;=&gt;&nbsp;visitMessageDestinationRef&nbsp;向InjectionCollection添加Injection实例，其中jndiName为message-destination-ref-name，typeClass为message-destination-type，并绑定该message-destination引用资源。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">post-construct&nbsp;=&gt;&nbsp;visitPostConstruct&nbsp;向LifeCycleCallbackCollection注册一个PostConstructCallback，其targetClass由lifecycle-callback-class定义，而method由lifecycle-callback-method定义（该PostConstructCallback也可以使用@PostConstruct的Annotation方式注册，并在PostConstructAnnotationHandler中解析）。</span></p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">pre-destroy&nbsp;=&gt;&nbsp;visitPreDestroy&nbsp;向LifeCycleCallbackCollection注册PreDestroyCallback，其targetClass由lifecycle-callback-class定义，methodName由lifecycle-callback-method定义（该PreDestroyCallback也可以使用@PreDestroy注解注册，并在PreDestroyAnnotationHandler中解析）。</span></p><p style="margin-bottom:0pt; margin-top:0pt; ">&nbsp;</p><p style="margin-bottom:0pt; margin-top:0pt; "><span style="color: #4b4b4b; font-size: 9.5pt; font-family: 宋体; background: #ffffff;">所有以上注册的RunAsCollection、InjectionCollection、LifeCycleCallbackCollection都在PlusDecorator中使用，PlusDecorator类实现Decorator方法，在所有的decorate实现方法中，使用RunAsCollection向ServletHolder中注册配置的roleName（感觉这里有bug，应该是decorate一个Servlet而不是ServletHolder）；使用InjectionCollection向Servlet、Filter、EventListener注入JNDI对应的值；使用LifeCycleCallbackCollection调用所有注册的PostConstruct方法。而在destroyServlet、Filter实例时，使用LifeCycleCallbackCollection调用素有注册的PreDestroy方法。</span></p><img src ="http://www.blogjava.net/DLevin/aggbug/414034.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-05-24 22:06 <a href="http://www.blogjava.net/DLevin/archive/2014/05/24/414034.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之SecurityHandler</title><link>http://www.blogjava.net/DLevin/archive/2014/05/18/413797.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sun, 18 May 2014 14:02:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/05/18/413797.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/413797.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/05/18/413797.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/413797.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/413797.html</trackback:ping><description><![CDATA[<h2><span style="color: #ff6600;">概述</span></h2>Jetty的强大之处在于可以自由的配置某些组建的存在与否，以提升性能，减少复杂度，而其本身也因为这种特性而具有很强的可扩展性。SecurityHandler就是Jetty对Servlet中Security框架部分的实现，并可以根据实际需要装卸和替换。Servlet的安全框架主要有两个部分：数据传输的安全以及数据授权，对数据传输的安全，可以使用SSL对应的Connector实现，而对于数据授权安全，Servlet定义了一套自己的框架。<br /><br />Servlet的安全框架支持两种方式的验证：首先，是用于登陆的验证，对于定义了role-name的资源都需要进行登陆验证，Servlet支持NONE、BASIC、CLIENT-CERT、DIGEST、FORM等5种验证方式(&lt;login-config&gt;/&lt;auth-method&gt;)；除了用户登陆验证，Servlet框架还定义了role的概念，一个role可以包含一个或多个用户，一个用户可以隶属于多个role，一个资源可以有一个或多个role，只有这些定义的role才能访问该资源，用户只能访问它所隶属的role能访问的资源。另外，对一个Servlet来说，还可以定义role-name到role-link的映射关系，从文档上，这里的role-name是Servlet中使用的名字，而role-link是Container中使用的名字，感觉很模糊，从Jetty的角度，role-name是web.xml中在&lt;security-constraint&gt;/&lt;auth-constraint&gt;/&lt;role-name&gt;中对一个URL Pattern的role定义，而role-link则是UserIdentity中roles数组的值，而UserIdentity是LoginService中创建的，它从文件、数据库等加载已定义的user的信息：用户名、密码、它隶属的role等，如果Servlet中没有定义role-name到role-link的映射，则直接使用role-name去UserIdentity中比较role信息。<br /><br />关于Servlet对Security框架的具体解释，可以参考Oracle的文档：<a href="http://docs.oracle.com/cd/E19798-01/821-1841/6nmq2cpk7/index.html">http://docs.oracle.com/cd/E19798-01/821-1841/6nmq2cpk7/index.html</a><br /><br />在web.xml中，对用于登陆验证方式的定义如下：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">login-config</span><span style="color: #0000FF; ">&gt;</span>&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">auth-method</span><span style="color: #0000FF; ">&gt;</span>FORM<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">auth-method</span><span style="color: #0000FF; ">&gt;</span>&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">realm-name</span><span style="color: #0000FF; ">&gt;</span>Example-Based&nbsp;Authentiation&nbsp;Area<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">realm-name</span><span style="color: #0000FF; ">&gt;</span>&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">form-login-config</span><span style="color: #0000FF; ">&gt;</span>&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">form-login-page</span><span style="color: #0000FF; ">&gt;</span>/jsp/security/protected/login.jsp<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">form-login-page</span><span style="color: #0000FF; ">&gt;</span>&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">form-error-page</span><span style="color: #0000FF; ">&gt;</span>/jsp/security/protected/error.jsp<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">form-error-page</span><span style="color: #0000FF; ">&gt;</span>&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">form-login-config</span><span style="color: #0000FF; ">&gt;</span>&nbsp;<br />&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">login-config</span><span style="color: #0000FF; ">&gt;</span>&nbsp;<br />OR<br />&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">login-config</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">auth-method</span><span style="color: #0000FF; ">&gt;</span>BASIC<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">auth-method</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">realm-name</span><span style="color: #0000FF; ">&gt;</span>Tomcat&nbsp;Manager&nbsp;Application<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">realm-name</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">login-config</span><span style="color: #0000ff;">&gt;</span></div>而对资源所属role的定义如下：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">security-constraint</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">security-constraint</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">web-resource-collection</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">web-resource-name</span><span style="color: #0000FF; ">&gt;</span>Status&nbsp;interface<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">web-resource-name</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">url-pattern</span><span style="color: #0000FF; ">&gt;</span>/status/*<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">url-pattern</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">web-resource-collection</span><span style="color: #0000FF; ">&gt;<br /></span>&nbsp; &nbsp;&nbsp;<font color="#0000ff">...</font><br />&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">auth-constraint</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">role-name</span><span style="color: #0000FF; ">&gt;</span>manager-gui<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">role-name</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">role-name</span><span style="color: #0000FF; ">&gt;</span>manager-script<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">role-name</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">role-name</span><span style="color: #0000FF; ">&gt;</span>manager-jmx<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">role-name</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;</span><span style="color: #800000; ">role-name</span><span style="color: #0000FF; ">&gt;</span>manager-status<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">role-name</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">auth-constraint</span><span style="color: #0000FF; ">&gt;</span><br />&nbsp;&nbsp;<span style="color: #0000FF; ">&lt;/</span><span style="color: #800000; ">security-constraint</span><span style="color: #0000FF; ">&gt;</span></div><h2><span style="color: #ff6600;">Jetty对Servlet Security实现概述和类图</span></h2>在Jetty中，使用Authenticator接口抽象不同用户登陆验证的逻辑；使用LoginService接口抽象对用户名、密码的验证；使用UserIdentity保存内部定义的一个用户的用户名、密码、role集合；使用ConstraintMapping保存URL Pattern到role集合的映射；使用UserIdentity.Scope保存一个Servlet中role-name到role-link的映射。他们的类图如下：<br /><img src="http://www.blogjava.net/images/blogjava_net/dlevin/Jetty_Security2.jpg" alt="" height="1223" width="1470" /><br /><h2><span style="color: #ff6600;">UserIdentity实现</span></h2>UserIdentity表示一个用户的认证信息，它包含Subject和UserPrincipal，其中Subject是Java Security框架定义的类型，而UserPrincipal则用于存储用户名以及认证信息，在Jetty中一般使用KnownUser来存储，它包含了UserName以及Credential实例，其中Credential可以是Crypt、MD5、Password等。在Credential中定义了check方法用于验证传入的credential是否是正确的。<br /><h2><span style="color: #ff6600;">IdentityService实现</span></h2>IdentityService我猜原本用于将UserIdentity、RunAsToken和当前Thread关联在一起，以及创建UserIdentity、RunAsToken，然而我看的版本中，DefaultIdentityService貌似还没有实现完成，目前只是根据提供的Subject、Principal、roles创建DefaultUserIdentity实例，以及使用runAsName创建RoleRunAsToken，对Servlet中的runAsToken，我看的Jetty版本也还没有实现完成。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;UserIdentity&nbsp;newUserIdentity(<span style="color: #0000FF; ">final</span>&nbsp;Subject&nbsp;subject,&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;Principal&nbsp;userPrincipal,&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String[]&nbsp;roles)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;DefaultUserIdentity(subject,userPrincipal,roles);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;RunAsToken&nbsp;newRunAsToken(String&nbsp;runAsName)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;RoleRunAsToken(runAsName);<br />&nbsp;&nbsp;&nbsp;&nbsp;}</div><h2><span style="color: #ff6600;">LoginService实现</span></h2>在Jetty中，LoginService用来验证给定的用户名和证书信息(如密码)，即对应的login方法；以及验证给定的UserIdentity，即对应的validate方法；其Name属性用于标识实例本身(即作为当前使用的realm name)；另外IdentityService用于根据加载的用户名和证书信息创建UserIdentity实例。<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;LoginService&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;getName();<br />&nbsp;&nbsp;&nbsp;&nbsp;UserIdentity&nbsp;login(String&nbsp;username,Object&nbsp;credentials);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;validate(UserIdentity&nbsp;user);<br />&nbsp;&nbsp;&nbsp;&nbsp;IdentityService&nbsp;getIdentityService();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setIdentityService(IdentityService&nbsp;service);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;logout(UserIdentity&nbsp;user);<br />}</div><br />为了验证用户提供的用户名和证书的正确性和合法性，需要有一个地方用来存储定义好的正确的用户名以及对应的证书信息(如密码等)，Jetty提供了DB、Properties文件、JAAS、SPNEGO作为用户信息源的比较。对于DB或Properties文件方式存储用户信息，如果每次的验证都去查询数据库或读取文件内容，效率会很低，因而还有一种实现方式是将数据库或文件中定义的用户信息预先的加载到内存中，这样每次验证只需要读取内存即可，这种方式的实现性能会提高很多，但是这样就无法动态的修改用户信息，并且如果用户信息很多，会占用很多的内存，目前Jetty采用后者实现，其中数据库存储用户信息有两个：JDBCLoginService以及DataSourceLoginService，Properties文件存储对应的实现是HashLoginService，它们都继承自MappedLoginService。在MappedLoginService中保存了一个ConcurrentMap&lt;String, UserIdentity&gt;实例，它是一个UserName到UserIdentity的映射，在该实例start时，它会从底层的数据源中加载用户信息，对HashLoginService，它会从config指定的Properties文件中加载用户信息，并填充ConcurrentMap&lt;String, UserIdentity&gt;，其中Properties文件的格式为：&lt;username&gt;=credential, role1, role2, ....如果credential以"MD5:"开头，表示它是MD5数据，如果以"CRYPT:"开头，表示它是crypt数据，否则表示它是密码字符；如果以存在的用户不在新读取的用户列表中，则将其移除，因为HashLoginService还可以启动一个线程以隔一定的时间重新加载文件中的内容，以处理文件更新的问题。在MappedLoginService中还定义了几个Principal的实现类：KnownUser、RolePrincipal、Anonymous等，在添加加载的用户时，使用KnownUser保存username和credential信息，并将该Principal添加到Subject的Principals集合中，同时对每个role创建RolePrincipal，并添加到Subject的Principals集合中，而将credential添加到Subject的PrivateCredentials集合中，使用IdentityService创建UserIdentity，并添加到ConcurrentMap&lt;String, UserIdentity&gt;中。在login验证中，首先使用传入的username查找存在的UserIdentity，并使用找到的UserIdentity中的Principal的check方法验证传入的credential，如果验证失败，返回null(即调用Credential的check方法：Password/MD5/Crypt)。对DataSourceLoginService和JDBCLoginService只是从数据库中加载用户信息，不详述。而JAASLoginService和SpnegoLoginService也只是使用各自的协议进行验证，不细述。<br /><h2><span style="color: #ff6600;">Authenticator实现</span></h2>Authenticator用于验证传入的ServletRequest、ServletResponse是否包含正确的认证信息。其接口定义如下：<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Authenticator&nbsp;{<br />&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// Jetty支持BASIC、FORM、DIGEST、CLIENT_CERT、SPNEGO的认证，该方法返回其中的一种，或用于自定义的方法。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;getAuthMethod();<br /><br />&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 设置配置信息(SecurityHandler继承自AuthConfiguration接口)：AuthMethod、RealmName、InitParameters、LoginService、IdentityService、IsSessionRenewedOnAuthentication</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setConfiguration(AuthConfiguration&nbsp;configuration);<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">// 验证逻辑的实现方法，其中mandatory若为false表示当前资源有没有配置role信息，或者@ServletSecurity中的@HttpConstraint的EmptyRoleSemantic被配置为PERMIT，此时返回Deferred类型的Authentication，如果不手动的调用其authenticate或login方法，就不会对该请求进行验证。<br />&nbsp; &nbsp; // 对BasicAuthenticator的实现，它从Authorization请求头中获取认证信息(用户名和用户密码，使用":"分割，并且使用Base64编码)，调用LoginService进行认证，当认证通过时，如果配置了renewSession为true，则将HttpSession中的所有属性更新一遍，并且添加(org.eclipse.jetty.security.secured, True) entry，并使用UserIdentity以及AuthMethod创建UserAuthentication返回。如果认证失败，则返回401 Unauthorized错误，并且在相应消息中包含头：WWW-Authenticate: basic realm=&lt;LoginService.name&gt;<br />&nbsp; &nbsp; // 对FormAuthenticator的实现，它首先要配置formLoginPage、formLoginPath(默认j_security_check)、formErrorPage、formErrorPath；只有当前请求URL是formLoginPath时，从j_username和j_password请求参数中获取username和password信息，使用LoginService验证，如果验证通过且这个请求是因为之前请求其他资源重定向过来的，这重定向到之前的URL，创建一个SessionAuthentication放入HttpSession中，并返回一个新创建的FormAuthentication；如果验证失败，如果没定义formErrorPage，返回403 Forbidden相应，否则重定向或forward到formErrorPage；对于其他URL请求，查看在当前Session中是否存在已认证的Authentication，如果有，但是重新验证缓存的Authentication失败，则将这个Authentication从HttpSession中移除；否则返回这个Session中的Authentication；对于其他情况，表示当前请求需要认证后才能访问，此时保存当前请求URI以及POST数据到Session中，以在认证之后可以直接跳转，然后重定向或forward到formLoginPage中。<br />&nbsp; &nbsp; // 对DigestAuthenticator的实现类似BasicAuthenticator，只是它使用Digest的方式对认证数据进行加密和解密。<br />&nbsp; &nbsp; // 对ClientCertAuthenticator则采用客户端证书的方式认证，SpnegoAuthenticator使用SPNEGO方式认证，JaspiAuthenticator使用JASPI方式认证。&nbsp;</span><br />&nbsp;&nbsp;&nbsp;&nbsp;Authentication&nbsp;validateRequest(ServletRequest&nbsp;request,&nbsp;ServletResponse&nbsp;response,&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;mandatory)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServerAuthException;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">// 只用于JaspiAuthenticator，用于所有后继handler处理完成后对ServletRequest、ServletResponse、User的进一步处理，目前不了解JASPI的协议逻辑，因而不了解具体的用途。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;secureResponse(ServletRequest&nbsp;request,&nbsp;ServletResponse&nbsp;response,&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;mandatory,&nbsp;User&nbsp;validatedUser)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServerAuthException;<br />}</div><h2><span style="color: #ff6600;">SecurityHandler与ConstraintSecurityHandler实现</span></h2>SecurityHandler继承自HandlerWrapper，并实现了Authenticator.AuthConfiguration接口，因而它包含了realm、authMethod、initParameters、loginService、identityService、renewSession等字段，在其start时，它会首先从ServletContext的InitParameters中导入org.eclipse.jetty.security.*属性的值到其InitParameters中，如果LoginService为null，则从Server中查找一个已经注册的LoginService，使用Authenticator.Factory根据AuthMethod创建对应的Authenticator实例。<br /><br />ConstraintSecurityHandler继承自SecurityHandler类，它定义了ConstraintMapping列表、所有定义的role、以及pathSpec到Map&lt;String, RoleInfo&gt;(key为httpMethod，RoleInfo包含UserDataConstraint枚举类型和roles集合)的映射，其中ConstraintMapping中保存了method、methodOmissions、pathSpec、Constraint(Constraint中包含了name、roles、dataConstraint等信息)，ConstraintMapping在解析web.xml文件时添加，它对应&lt;security-constraint&gt;下的配置，如auth-constraint下的role-name配置对应roles数组，user-data-contraint对应dataConstraint，web-resource-name对应name，http-method对应method，url-pattern对应pathSpec；在每次添加ConstraintMapping时都会更新roles列表以及pathSpec到Map&lt;String, RoleInfo&gt;的映射。<br /><br />在SecurityHandler的handle方法中，它只需要对REQUEST、ASYNC类型的DispatcherType需要验证：它首先根据pathInContext和Request实例查找RoleInfo信息；如果RoleInfo处于forbidden状态，发送403 Forbidden相应，如果DataConstraint配置了Intergal、Confidential，但是Connector中没有配置相应的port，则发送403 Forbidden相应，否则重定向请求到Integral、Confidential对应的URL；对没有验证过的请求调用Authenticator.validateRequest()对请求进行验证；如果验证的结果是Authentication.ResponseSent，设置Request的handled为true，如果为Authentication.User，表示认证成功，设置该Authentication到Request中，并检查role，即检查当前User是否处于RoleInfo中的role集合中，如果不是，发送403 Forbidden响应，否则调用下一个handler的handle方法，之后调用Authenticator.secureResponse()方法；如果验证结果是Authentication.Deferred，在调用下一个handler的handle方法后调用Authenticator.secureResponse()方法；否则直接调用Authenticator.secureResponse()方法。<img src ="http://www.blogjava.net/DLevin/aggbug/413797.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-05-18 22:02 <a href="http://www.blogjava.net/DLevin/archive/2014/05/18/413797.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之ServletHandler</title><link>http://www.blogjava.net/DLevin/archive/2014/05/17/413788.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sat, 17 May 2014 15:20:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/05/17/413788.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/413788.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/05/17/413788.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/413788.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/413788.html</trackback:ping><description><![CDATA[<h2><span style="color: #ff6600;">概述</span></h2>ServletHandler继承自ScopedHandler，是Jetty中用于存储所有Filter、FilterMapping、Servlet、ServletMapping的地方，以及用于实现一次请求所对应的Filter链和Servlet执行流程的类。对Servlet的框架实现中，它也被认为是Handler链的末端，因而在它的doHandle()方法中没有调用nextHandle()方法。<br /><h2><span style="color: #ff6600;">ServletHandler的成员</span></h2>正如前面提到的，ServletHandler是一个用于管理Filter、FilterMapping、Servlet、ServletMapping的容器，因而它需要一下成员用于存储这些它管理的实例：<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;FilterHolder[]&nbsp;_filters=<span style="color: #0000FF; ">new</span>&nbsp;FilterHolder[0];<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;FilterMapping[]&nbsp;_filterMappings;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;ServletHolder[]&nbsp;_servlets=<span style="color: #0000FF; ">new</span>&nbsp;ServletHolder[0];<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;ServletMapping[]&nbsp;_servletMappings;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;Map&lt;String,FilterHolder&gt;&nbsp;_filterNameMap=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;HashMap&lt;String,FilterHolder&gt;();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;List&lt;FilterMapping&gt;&nbsp;_filterPathMappings;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;MultiMap&lt;String&gt;&nbsp;_filterNameMappings;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;Map&lt;String,ServletHolder&gt;&nbsp;_servletNameMap=<span style="color: #0000FF; ">new</span>&nbsp;HashMap&lt;String,ServletHolder&gt;();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;PathMap&nbsp;_servletPathMap;</div>其中FilterHolder和ServletHolder分别用于存储Filter、Servlet实例以及其配置信息，即web.xml配置文件中的&lt;filter&gt;、&lt;servlet&gt;的配置信息(参考：<a href="http://www.blogjava.net/DLevin/archive/2014/05/11/413500.html">Servlet、Filter、Registration的实现</a>)；而FilterMapping和ServletMapping则是FilterName到URL Pattern的mapping信息，以及ServletName到URL Pattern的mapping信息，即web.xml中的&lt;filter-mapping&gt;、&lt;servlet-mapping&gt;信息。<br /><br />其中FilterMapping包含了一个Filter适用的所有URL Pattern、Servlets、DispatcherType以及对应FilterHolder信息：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;_dispatches=DEFAULT;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;String&nbsp;_filterName;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">transient</span>&nbsp;FilterHolder&nbsp;_holder;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;String[]&nbsp;_pathSpecs;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;String[]&nbsp;_servletNames;</div>它有appliesTo()方法用于判断传入的path和dispatcherType是否符包含当前Filter。对于URL Pattern：*.do=&gt;anything.do, /foo/poo/abc.do, /path/to/*=&gt;/path/to, /path/to/abdc。<br /><br />而ServletMapping包含了ServletName和其适用的所有URL Pattern，它的URL Pattern的mapping规则和FilterMapping中的规则一样：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;String&nbsp;_servletName;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;String[]&nbsp;_pathSpecs;</div><br />在管理Filter和FilterMapping中，可以使用FilterHolder、pathSpec、DispatcherType向_filters数组中添加一个FilterHolder，并向_filterMappings数组中添加一个FilterMapping实例；而由该方法引申出来的，可以直接传入Filter实例、Filter类实例、Filter类名，而由方法内部创建对应的FilterHolder实例；DispatcherType可以是一个EnumSet类型的DispatcherType；可以直接添加Filter或FilterMapping或两个同时添加；也可以使用prependFilterMapping将新的FilterMapping添加到数组前。对Servlet和ServletMapping管理也是类似，使用ServletHolder和pathSpec添加_servlets数组和_servletMappings数组，并引申出Servlet可以是实例、Servlet类名、Servlet类实例，而由内部创建ServletHolder实例；也可以单独的添加ServletHolder或ServletMapping实例。<br /><br />在ServletHandler中还有_filterNameMap和_servletNameMap实例用于存储FilterName到FilterHolder以及ServletName到ServletHolder的映射，它在每次_filters、_servlets数组更新时都会随着更新，并且在doStart方法中也会再更新一次；另外对Filter还有_filterPathMapping用于存储所有FilterMapping的一个List，_filterNameMapping用于存储ServletName到多个FilterMapping的MultiMap，对Servlet中也有_servletPathMap，包含pathSpec到ServletHolder的PathMap，他们在每次_filterMappings、_servletMappings更新时以及doStart方法中都会被更新。而在start时也会start所有的FilterHolder和ServletHolder，对所有FilterHolder的start按其定义顺序进行，而对ServletHolder的start，则按其InitOrder排序。<br /><h2><span style="color: #ff6600;">doScope方法实现</span></h2>doScope方法用于准备执行环境，其实现逻辑为：如果传入的target不是ServletName(即以"/"开头，表示它为Path)，则使用该target从_servletPathMap中找到对应的ServletHolder，并计算出当前的ServletPath和PathInfo，如果时INCLUDE类型的Dispatch，设置Request的javax.servlet.include.serlvet_path，javax.servlet.include.path_info属性为计算出来的值，否则设置Request的ServletPath和PathInfo的值为计算出的值；对target为ServletName，ServletHolder的实例从_servletNameMap字段中查找。然后将当前找到的ServletHolder作为UserIdentityScope设置到Request中，以及设置org.eclipse.multipartConfig属性为ServletHolder中的MultipartConfig实例。在刚方法退出时，将UserIdentityScope、ServletPath、PathInfo还原回原来的值。<br /><h2><span style="color: #ff6600;">doHandle方法实现</span></h2>doHandle方法用于真正实现执行逻辑：它首先通过target找到FilterChain实例，对于target为path时，它遍历整个_filterPathMapping的列表，选出所有符合pathInContext的FilterHolder数组，以及从_filterNameMappings中找出所有ServletHolder中存储的ServletName对应的FilterMapping并且DispatcherType相符合的FilterMapping数组，以及注册的ServletName为"*"的FilterMapping且DispatcherType相符的FilterMapping数组，合并这些数组，并一同用Request、ServletHolder创建FilterChain实例；对target为ServletName时，只需要查找_filterNameMapping字段中的FilterMapping。如果没有FilterHolder实例，则向客户端发送404 Not Found响应；否则如果FilterChain实例不为null，调用其doFilter方法，传入ServletRequest和ServletResponse参数，在FilterChain的doFilter方法中它回一次遍历Filter的doFilter方法，直到最后调用ServletHolder的handle方法；否则，直接调用ServletHolder的handle方法。<img src ="http://www.blogjava.net/DLevin/aggbug/413788.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-05-17 23:20 <a href="http://www.blogjava.net/DLevin/archive/2014/05/17/413788.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之Servlet框架及实现(AsyncContext、RequestDispatcher、HttpSession)</title><link>http://www.blogjava.net/DLevin/archive/2014/05/17/413766.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sat, 17 May 2014 09:58:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/05/17/413766.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/413766.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/05/17/413766.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/413766.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/413766.html</trackback:ping><description><![CDATA[<h2><span style="color: #ff6600;">概述</span></h2>
<span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">Servlet是Server Applet的缩写，即在服务器端运行的小程序，而Servlet框架则是对HTTP服务器(Servlet Container)和用户小程序中间层的标准化和抽象。这一层抽象隔离了HTTP服务器的实现细节，而Servlet规范定义了各个类的行为，从而保证了这些&#8220;服务器端运行的小程序&#8221;对服务器实现的无关性(即提升了其可移植性)。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">在Servlet规范有以下几个核心类(接口)：</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">ServletContext</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：定义了一些可以和Servlet Container交互的方法。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">Registration</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：实现Filter和Servlet的动态注册。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">ServletRequest(HttpServletRequest)</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：对HTTP请求消息的封装。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">ServletResponse(HttpServletResponse)</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：对HTTP响应消息的封装。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">RequestDispatcher</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：将当前请求分发给另一个URL，甚至ServletContext以实现进一步的处理。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">Servlet(HttpServlet)</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：所有&#8220;服务器小程序&#8221;要实现了接口，这些&#8220;服务器小程序&#8221;重写doGet、doPost、doPut、doHead、doDelete、doOption、doTrace等方法(HttpServlet)以实现响应请求的相关逻辑。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">Filter(FilterChain)</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：在进入Servlet前以及出Servlet以后添加一些用户自定义的逻辑，以实现一些横切面相关的功能，如用户验证、日志打印等功能。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">AsyncContext</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：实现异步请求处理。<br />
</span>
<h2><span style="color: #ff6600;">AsyncContext</span></h2>
在Servlet 3.0中引入了AsyncContext，用于实现一个请求可以暂停处理，然后在将来的某个时候重新处理该请求，以释放当前请求处理过程中占用的线程。在使用时，当发现请求需要等待一段时间后才能做进一步处理时，可以调用ServletRequest.startAsync()方法，返回AsyncContext实例，使用自己的线程池启动一个线程来做接下来的处理或者将其放入一个任务队列中，以由一个线程不断的检查它的可用状态，以实现最后的返回处理，或调用dispatch方法将其分发给其他URL做进一步响应处理。这项功能对SocketConnector没有多大意义，因为即使Servlet的servic俄方发退出了，其所占用的线程会继续等待，并不会被回收，只有对SelectChannelConnector来说才有效果，因为它的等待不在HttpConnection的handleRequest方法中(AsyncContinuation的scheduleTimeout方法中)，而是将timeout的信息提交给SelectSet，它内部会有Acceptors个线程对timeout进行检查。AsyncContext接口定义如下：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;AsyncContext&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 原始请求相关信息的属性名，即dispatch方法所基于的计算信息。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;ASYNC_REQUEST_URI&nbsp;=&nbsp;"javax.servlet.async.request_uri";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;ASYNC_CONTEXT_PATH&nbsp;=&nbsp;"javax.servlet.async.context_path";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;ASYNC_PATH_INFO&nbsp;=&nbsp;"javax.servlet.async.path_info";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;ASYNC_SERVLET_PATH&nbsp;=&nbsp;"javax.servlet.async.servlet_path";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;ASYNC_QUERY_STRING&nbsp;=&nbsp;"javax.servlet.async.query_string";<br />
<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// AsyncContinuation是Jetty对AsyncContext的实现，它是一个有限状态机。<br />
&nbsp; &nbsp; // 1. 在初始化时，它处于IDLE状态，initial状态为true。<br />
&nbsp; &nbsp; // 2. 调用其handling()方法使其进入处理模式：若它处于IDLE状态，则设置initial状态为false，将状态转移到DISPATCHED，清除AsyncListener，返回true；若它处于REDISPATCH状态，将状态转移到REDISPATCHED，返回true；若它处于COMPLETING状态，状态转移到UNCOMPLETED，返回false；若它处于ASYNCWAIT状态，返回false。对其他状态，抛出IllegalStateException。<br />
&nbsp; &nbsp; // 3. 如果当前请求因为某些原因无法进一步处理时，可以调用ServletRequest.startAsync方法让当前请求进入ASYNCSTARTED状态，即调用AsyncContinuation.suspend方法，只有它处于DISPATCHED、REDISPATCHED状态下才能调用suspend方法，即在调用handling()方法之后。此方法还会更新AsyncEventState字段的信息，以及调用已注册的AsyncListener的onStartAsync方法，并清除已注册的AsyncListener。<br />
&nbsp; &nbsp; // 4. 调用unhandle()方法判断这个当前请求是否不需要做进一步处理而可以退出handleRequest中的循环：对ASYNCSTARTED状态，将其状态设置为ASYNCWAIT，并向SelectChannelHttpConnection中schedle一个timeout时间，如果此时它还是处于ASYNCWAIT状态(因为对非SelectChannelConnector，它会一直等待下一个dispatch/complete/timeout事件的到来，更新当前状态，并取消等待)，则返回true，否则如果它变为COMPLETING状态，则设置状态为UNCOMPLETED，返回true，否则设置其状态为REDISPATCHED，并返回false；对DISPATCHED、REDISPATCHED状态，设置状态为UNCOMPLETED，返回true；对REDISPATCHING状态，设置为REDISPATCHED状态，返回false；对COMPLETING状态，设置为UNCOMPLETED，返回true；对其他状态，抛出异常。<br />
&nbsp; &nbsp; // 5. 当进入异步状态的请求完成后，需要将当前处理交由Container做进一步处理，如由另一个path完成进一步处理等，调用AsyncContext的dispatch方法，将当前请求分发回Container：如果当前AsyncContinuation处于ASYNCWAIT状态并且没有超时，设置状态为REDISPATCH，并cancelTimeout()、scheduleDispatch()；对已经处于REDISPATCH状态，直接返回；对处于ASYNCSTARTED状态，设置为REDISPATCHING，并返回。<br />
&nbsp; &nbsp; // 6. 如果当前AsyncContinuation超时，调用其expired方法：对于处于ASYNCSARTED、ASYNCWAIT状态，触发AsyncListener的onTimeout事件，调用complete方法，并scheduleDispatch<br />
&nbsp; &nbsp; // 7. 当完成异步请求处理时，调用其complete方法：如果处于ASYNCWAIT状态，设置状态为COMPLETING，如果没有超时，scheduleTimeout、scheduleDispatch；当前状态为ASYNCSTARTED，设置状态为COMPLETING；对其他状态，抛出异常。<br />
&nbsp; &nbsp; // 8. 当退出handleRequest方法时，如果当前AsyncContinuation处于UNCOMPLETE状态，调用其doComplete方法，将其状态设置为COMPLETE，如果出现异常，注册javax.servlet.error.exception, javax.servlet.error.message属性，并触发AsyncListener的onError事件，否则触发onComplete事件。<br />
&nbsp; &nbsp; // 9. 对状态为ASYNCSTARTED、REDISPATCHING、COMPLETING、ASYNCWAIT，表示处于suspend状态。<br />
&nbsp; &nbsp; // 10. 对状态为ASYNCSTARTED、REDISPATCHING、REDISPATCH、ASYNCWAIT，表示其处于异步请求开启的状态。<br />
&nbsp; &nbsp; // 11. 对状态不是IDLE、DISPATCHED、UNCOMPLETED、COMPLETED，表示当前正处于异步请求状态。&nbsp;</span><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 在调用ServletRequest.startAsync方法中使用的ServletRequest、ServletResponse实例。在调用ServletRequest.startAsync方法时，内部调用AsyncContinuation的suspend方法，<br />
&nbsp; &nbsp; // 传入ServletContext、ServletRequest、ServletResponse实例，在有在AsyncContinuation实例处于DISPATCHED、REDISPATCHED状态下才能调用suspend方法。此时将_expired、_resumed状态设置为false，更新AsyncEventState中的AsyncContext、ServletContext(_suspendedContext, _dispatchedContext)、ServletRequest、ServletResponse、Path等信息(即如果传入的Request、Response、_suspendContext和当前已保存的实例不同或_event实例为null，则重新创建AsyncEventState实例，否则清除_event中的_dispatchedContext和_path字段)。将当前AsyncContinuation的状态设置为ASYNCSTARTED，保存已注册的AsyncListener列表(_asyncListeners)到_lastAsyncListeners，清除_asyncListeners列表，并触发_lastAsyncListeners中的onStartAsync事件(该事件中可以决定是否需要将自己注册回去)。<br />
&nbsp; &nbsp; // 对于AsyncContext实例，如果已经调用suspend方法，则返回_event中的ServletRequest、ServletResponse，否则返回HttpConnection中的ServletRequest、ServletResponse。&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ServletRequest&nbsp;getRequest();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ServletResponse&nbsp;getResponse();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 当前AsyncContext是否使用原始的request、response实例进行初始化。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;hasOriginalRequestAndResponse();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;dispatch();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;dispatch(String&nbsp;path);<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;dispatch(ServletContext&nbsp;context,&nbsp;String&nbsp;path);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;complete();<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;start(Runnable&nbsp;run);<br />
<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;addListener(AsyncListener&nbsp;listener);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;addListener(AsyncListener&nbsp;listener,&nbsp;ServletRequest&nbsp;servletRequest,&nbsp;ServletResponse&nbsp;servletResponse);<br />
<br />
&nbsp; &nbsp;<span style="color: #0000FF; ">public</span>&nbsp;&lt;T&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;AsyncListener&gt;&nbsp;T&nbsp;createListener(Class&lt;T&gt;&nbsp;clazz)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException;&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setTimeout(<span style="color: #0000FF; ">long</span>&nbsp;timeout);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;getTimeout();<br />
}</div>
在Server的handleAsync()方法中，他使用HttpConnection的Request字段的AsyncEventState中的ServletRequest、ServletResponse作为Handler调用handle方法的参数，如果AsyncEventState中有path值，则会用该值来更新baseRequest中的URI相关信息。<br />
<h2><span style="color: #ff6600;">RequestDispatcher</span></h2>
在Servlet中RequestDispatcher用于将请求分发到另一个URL中，或向响应中包含更多的信息。一般用于对当前请求做一些前期处理，然后需要后期其他Servlet、JSP来做进一步处理。在Jetty中使用Dispatcher类实现该接口，其接口定义如下：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;RequestDispatcher&nbsp;{<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 在Dispatcher类中包含了ContextHandler、uri、path、dQuery、named字段，其中ContextHandler是当前Web Application配置的Handler链用于将请求分发给当前Container(调用handle()方法)做进一步处理、dipatch后请求的全URI、path表示uriInContext、dQuery表示新传入的parameter、named表示可以使用Servlet名称创建Dispatcher，即将当前请求分发到一个命名的Servlet中。<br />
&nbsp; &nbsp; // 在Dispatcher类中有三个方法：forward、error、include。对forward、error来说，如果Response已经commit，会抛出IllegalStateException。<br />
&nbsp; &nbsp; // 其中forward和error只是DispatcherType不一样(FORWARD、ERROR)，其他逻辑一样：清除ServletResponse中所有响应相关的字段，如Content Buffer、locale、ContentType、CharacterEncoding、MimeType等信息，设置ServletRequest的DispatchType；对named方式的Dispatcher，直接调用ContextHandler的handle方法，其target参数即为传入的named；如果dQuery字段不为null，将该dQuery中的包含的参数合并到当前请求中；更新Request的URI、ContextPath，并在其Request属性中添加原始请求的pathInfo、queryString、RequestURI、contextPath、servletPath信息，分别对应该接口中定义的字段，如果这是第二次forward，则保留最原始的请求相关的信息；最后调用ContextHandler的handle方法，target为path属性；在调用结束后，将RequestURI、ContextPath、ServletPath、PathInfo、Attributes、Parameters、QueryString、DispatcherType属性设置为原来的值。<br />
&nbsp; &nbsp; // 对include方法，它不会清除Response中的Buffer等信息：首先设置DispatcherType为INCLUDE，HttpConnection中的include字段加1，表示正处于INCLUDE的dispatch状态，从而阻止对ServletResponse响应头的设置、发送重定向响应、发送Error响应等操作，该include字段会在该方法结束是调用HttpConnection的included方法将其减1；同样对于named设置的Dispatcher实例，直接调用ContextHandler的handle方法，target为named值；对以path方式的include，首先合并传入的dQuery参数到Request中，更新Request中属性的requestURI、contextPath、pathInfo、query等，后调用ContextHandler的handle方法，target为path，在handle方法完成后，将请求Attributes、Parameters、DispatcherType设置会原有值。<br />
</span><br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000; ">// 在forward中，原始请求对应信息使用的属性名。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;FORWARD_REQUEST_URI&nbsp;=&nbsp;"javax.servlet.forward.request_uri";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;FORWARD_CONTEXT_PATH&nbsp;=&nbsp;"javax.servlet.forward.context_path";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;FORWARD_PATH_INFO&nbsp;=&nbsp;"javax.servlet.forward.path_info";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;FORWARD_SERVLET_PATH&nbsp;=&nbsp;"javax.servlet.forward.servlet_path";<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;FORWARD_QUERY_STRING&nbsp;=&nbsp;"javax.servlet.forward.query_string";<br />
<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 在include中，原始请求对应信息使用的属性名。</span><br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;INCLUDE_REQUEST_URI&nbsp;=&nbsp;"javax.servlet.include.request_uri";<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;INCLUDE_CONTEXT_PATH&nbsp;=&nbsp;"javax.servlet.include.context_path";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;INCLUDE_PATH_INFO&nbsp;=&nbsp;"javax.servlet.include.path_info";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;INCLUDE_SERVLET_PATH&nbsp;=&nbsp;"javax.servlet.include.servlet_path";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;INCLUDE_QUERY_STRING&nbsp;=&nbsp;"javax.servlet.include.query_string";<br />
<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 在error中，原始请求对应信息使用的属性名。</span><br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;ERROR_EXCEPTION&nbsp;=&nbsp;"javax.servlet.error.exception";<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;ERROR_EXCEPTION_TYPE&nbsp;=&nbsp;"javax.servlet.error.exception_type";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;ERROR_MESSAGE&nbsp;=&nbsp;"javax.servlet.error.message";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;ERROR_REQUEST_URI&nbsp;=&nbsp;"javax.servlet.error.request_uri";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;ERROR_SERVLET_NAME&nbsp;=&nbsp;"javax.servlet.error.servlet_name";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;ERROR_STATUS_CODE&nbsp;=&nbsp;"javax.servlet.error.status_code";<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;forward(ServletRequest&nbsp;request,&nbsp;ServletResponse&nbsp;response)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException,&nbsp;IOException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;include(ServletRequest&nbsp;request,&nbsp;ServletResponse&nbsp;response)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException,&nbsp;IOException;<br />
}</div>
<h2><span style="color: #ff6600;">HttpSession</span></h2>
Http的请求是无状态的，这种方式的好处是逻辑简单，因为服务器不需要根据当前服务器的状态做一些特殊处理，然而在实际应用中，有些时候希望一系列的请求共享一些数据和信息，在HTTP中可以有两种方式实现这种需求：一种是cookie，所有这些共享的数据和信息都使用cookie在发送请求时发送给服务器，在响应请求时将最新的信息和状态通过set-cookie的方式重新发送回客户端，这种方式可以使服务器依然保持简单无状态的处理逻辑，然而它每次都要来回传送这种状态信息，会占用带宽，而且cookie本身有大小限制，有些客户端处于安全的因素会禁止cookie使用，另外cookie采用明文方式，对有些数据来说是不适合的；另一种方式则是采用服务器端Session的方法，即使用SessionId将一系列的请求关联在一起，可以向Session存储这些请求共享的信息和数据，Session方式的好处是这些数据保存在服务器端，因而它是安全的，而且不需要每次在客户端和服务器端传输，可以减少带宽，而它不好的地方是会增加服务器负担，因为如果Session过多会占用服务器内存，另外它也会增加服务器端的逻辑，服务器要有一种机制保证相同的SessionId确实属于同一个系列的请求。<br />
在Servlet种使用HttpSession抽象这种服务器端Session的信息。它包含了SessionId、CreationTime、LastAccessedTime、MaxInactiveInterval、Attributes等信息，在Servlet中的可见范伟是ServletContext，即跨Web Application的Session是不可见的。在Jetty中使用SessionManager来管理Session，Session可以存储在数据库中(JDBCSessionManager)，也可以存在内存中(HashSessionManager)。在Jetty中使用AbstractSessionManager的内部类Session来实现HttpSession接口，并且该实现是线程安全的。HttpSession的接口定义如下：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;HttpSession&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// HttpSession创建的时间戳，从1970-01-01 00:00:00.000开始算到现在的毫秒数。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;getCreationTime();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 当前Session的ID号，它用来唯一标识Web Application中的一个Session实例。在Jetty的实现中，有两种ID：NodeId和ClusterId，在SessionIdManager创建一个ClusterId时，可以使用一个SecureRandom的两次nextLong的36进制的字符串相加或者两次当前SessionIdManager的hashCode、当前可用内存数、random.nextInt()、Request的hashCode左移32位的异或操作的36进制字符串相加，并添加workName前缀，如果该ID已经存在，则继续使用以上逻辑，直到找到一个没有被使用的唯一的ID号。如果请求中的RequestedSessionId存在并在使用，则使用该值作为SessionID；如果当前请求已经存在一个正在使用的SessionId(在org.eclipse.jetty.server.newSessionId请求熟悉中)，则使用该ID。而对与NodeId，它会在ClusterId之后加一个".workerName"，可以通过SessionIdManager设置workName或在HashSessionIdManager中使用org.eclipse.jetty.ajp.JVMRoute请求属性设置。在AbstractSessionManager中设置NodeIdInSessionId为true来配置使用NodeId作为SessionId，默认使用ClusterId作为SessionId。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getId();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回最后一次访问时间戳。在每一次属于同一个Session的新的Request到来时都会更新该值。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;getLastAccessedTime();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回该Session对应的ServletContext。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ServletContext&nbsp;getServletContext();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 设置Session的Idle时间，以秒为单位。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setMaxInactiveInterval(<span style="color: #0000FF; ">int</span>&nbsp;interval);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getMaxInactiveInterval();&nbsp; &nbsp;&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// Attribute相关操作。在设置属性时，如果传入value为null，则移除该属性；如果该属性已存在，则替换该属性；如果属性值实现了HttpSessionBindingListener，则它在替换时会触发其valueUnbound事件，属性设置时会触发valueBound事件；如果HttpSession中注册了HttpSessionAttributeListener，则会触发响应的attributeAdded、attributeReplaced事件。而removeAttribute时，也会触发相应的valueUnbound事件以及attributeRemoved事件。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Object&nbsp;getAttribute(String&nbsp;name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Enumeration&lt;String&gt;&nbsp;getAttributeNames();&nbsp; &nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setAttribute(String&nbsp;name,&nbsp;Object&nbsp;value);<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;removeAttribute(String&nbsp;name);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// Session失效，它移除所有和其绑定的属性，并且将Session实例从SessionManager中移除。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;invalidate();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// true表识客户端没有使用session。此时客户端请求可能不需要使用session信息或者它使用cookie作为session的信息交互。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isNew();<br />
}</div>
在Jetty中使用SessionIdManager来创建管理SessionId信息，默认实现有HashSessionIdManager和JDBCSessionIdManager：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;SessionIdManager&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;LifeCycle&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;idInUse(String&nbsp;id);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;addSession(HttpSession&nbsp;session);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;removeSession(HttpSession&nbsp;session);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;invalidateAll(String&nbsp;id);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;newSessionId(HttpServletRequest&nbsp;request,<span style="color: #0000FF; ">long</span>&nbsp;created);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getWorkerName();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getClusterId(String&nbsp;nodeId);<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getNodeId(String&nbsp;clusterId,HttpServletRequest&nbsp;request);&nbsp;&nbsp;<br />
}</div>
而HttpSession的创建和管理则使用SessionManager，默认有HashSessionManager和JDBCSessionManager两个实现：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;SessionManager&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;LifeCycle&nbsp;{<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 在AbstractSessionManager定义了Session内部类实现了HttpSession接口，使用SessionIdManager来生成并管理SessionId，可以注册HttpSessionAttributeListener和HttpSessionListener(在HttpSession创建和销毁时分别触发sessionCreated、sessionDestroyed事件)。另外它还实现了SessionCookieConfig内部类，用于使用Cookie配置Session的信息，如Name(默认JSESSIONID)、domain、path、comment、httpOnly、secure、maxAge等。在HashSessionManager和JDBCSessionManager中还各自有一个线程会检查Session的expire状态，并Invalidate已经expired的Session。最后，AbstractSessionManager还包含了Session相关的统计信息。<br />
<br />
&nbsp; &nbsp; // 在SessionManager中定义的一些属性，可以使用该方法定义的一些属性在ServletContext的initParam中设置，即web.xml文件中的init-param中设置。<br />
</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">// 创建并添加HttpSession实例。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;HttpSession&nbsp;newHttpSession(HttpServletRequest&nbsp;request);<br />
<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000; ">// 根据SessionId获取HttpSession实例。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;HttpSession&nbsp;getHttpSession(String&nbsp;id);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 获取Cookie作为SessionTrackingMode时，该Cookie是否属于httpOnly(用来阻止某些cross-script攻击)</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;getHttpOnly();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// Session的最大Idle时间，秒为单位</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getMaxInactiveInterval();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setMaxInactiveInterval(<span style="color: #0000FF; ">int</span>&nbsp;seconds);<br />
<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setSessionHandler(SessionHandler&nbsp;handler);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 事件相关操作</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;addEventListener(EventListener&nbsp;listener);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;removeEventListener(EventListener&nbsp;listener);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;clearEventListeners();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">// 在使用Cookie作为SessionTrackingMode时，获取作为Session Tracking的Cookie</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;HttpCookie&nbsp;getSessionCookie(HttpSession&nbsp;session,&nbsp;String&nbsp;contextPath,&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;requestIsSecure);<br />
<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;SessionIdManager&nbsp;getIdManager();<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setIdManager(SessionIdManager&nbsp;idManager);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isValid(HttpSession&nbsp;session);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getNodeId(HttpSession&nbsp;session);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getClusterId(HttpSession&nbsp;session);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 更新Session的AccessTime。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;HttpCookie&nbsp;access(HttpSession&nbsp;session,&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;secure);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;complete(HttpSession&nbsp;session);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 使用URL作为SessionTrackingMode时，在URL中作为SessionId的parameter name。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setSessionIdPathParameterName(String&nbsp;parameterName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getSessionIdPathParameterName();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 使用URL作为SessionTrackingMode时，在URL中SessionId信息的前缀，默认为：;&lt;sessionIdParameterName&gt;=</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getSessionIdPathParameterNamePrefix();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isUsingCookies();<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isUsingURLs();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Set&lt;SessionTrackingMode&gt;&nbsp;getDefaultSessionTrackingModes();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Set&lt;SessionTrackingMode&gt;&nbsp;getEffectiveSessionTrackingModes();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setSessionTrackingModes(Set&lt;SessionTrackingMode&gt;&nbsp;sessionTrackingModes);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;SessionCookieConfig&nbsp;getSessionCookieConfig();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isCheckingRemoteSessionIdEncoding();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setCheckingRemoteSessionIdEncoding(<span style="color: #0000FF; ">boolean</span>&nbsp;remote);<br />
}</div><h2>
<span style="color: #ff6600;">SessionHandler
</span></h2>SessionHandler继承子ScopedHandler，它主要使用SessionManager在doScope方法中为当前Scope设置Session信息。<br />1. 如果使用Cookie作为SessionId的通信，则首先从Cookie中向Request设置RequestedSessionId。<br />2. 否则，从URL中计算出RequestedSessionId，并设置到Request中。<br />3. 如果SessionManager发生变化，则更新Request中SessionManager实例以及Session实例。<br />4. 如果Session发生变化，则更新Session的AccessTime，并将返回的cookie写入Response中。<br />5. 在退出时设置回Request原有的SessionManager和Session实例，如果需要的话。<img src ="http://www.blogjava.net/DLevin/aggbug/413766.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-05-17 17:58 <a href="http://www.blogjava.net/DLevin/archive/2014/05/17/413766.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之Servlet框架及实现(ServletRequest、ServletResponse)</title><link>http://www.blogjava.net/DLevin/archive/2014/05/16/413534.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Thu, 15 May 2014 17:29:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/05/16/413534.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/413534.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/05/16/413534.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/413534.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/413534.html</trackback:ping><description><![CDATA[<h2><span style="color: #ff6600;">概述</span></h2>
<span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">Servlet是Server Applet的缩写，即在服务器端运行的小程序，而Servlet框架则是对HTTP服务器(Servlet Container)和用户小程序中间层的标准化和抽象。这一层抽象隔离了HTTP服务器的实现细节，而Servlet规范定义了各个类的行为，从而保证了这些&#8220;服务器端运行的小程序&#8221;对服务器实现的无关性(即提升了其可移植性)。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">在Servlet规范有以下几个核心类(接口)：</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">ServletContext</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：定义了一些可以和Servlet Container交互的方法。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">Registration</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：实现Filter和Servlet的动态注册。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">ServletRequest(HttpServletRequest)</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：对HTTP请求消息的封装。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">ServletResponse(HttpServletResponse)</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：对HTTP响应消息的封装。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">RequestDispatcher</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：将当前请求分发给另一个URL，甚至ServletContext以实现进一步的处理。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">Servlet(HttpServlet)</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：所有&#8220;服务器小程序&#8221;要实现了接口，这些&#8220;服务器小程序&#8221;重写doGet、doPost、doPut、doHead、doDelete、doOption、doTrace等方法(HttpServlet)以实现响应请求的相关逻辑。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">Filter(FilterChain)</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：在进入Servlet前以及出Servlet以后添加一些用户自定义的逻辑，以实现一些横切面相关的功能，如用户验证、日志打印等功能。</span><br style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;" />
<strong style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px;">AsyncContext</strong><span style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; font-size: 13px; line-height: 20.799999237060547px; background-color: #ffffff;">：实现异步请求处理。</span><br />
<h2><span style="color: #ff6600;">ServletRequest</span></h2>
ServletRequest是对Servlet请求消息的封装，其子接口HttpServletRequest则是对HTTP请求消息的封装，在Servlet框架中默认实现了ServletRequestWrapper和HttpServletRequestWrapper以便利用户对请求的Wrap。<br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/Jetty_ServletRequest.jpg" alt="" height="1351" width="1168" /><br />
在Jetty中，使用Request类实现HttpServletRequest接口，Request包含了HttpConnection引用，因为HttpConnection包含了HttpParser解析HTTP请求后的所有信息，如请求行、请求头以及请求内容。其中ServletRequest接口定义与实现如下：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;ServletRequest&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000;">/ Request级别的Attribute操作，它属于Request的私有数据，因而Request使用Attributes私有字段实现。然而Jetty添加了一些自定义的Attribute：<br />
&nbsp; &nbsp; // 在getAttribute时，org.eclipse.jetty.io.EndPoint.maxIdleTime用于获取EndPoint中MaxIdleTime属性，org.eclipse.jetty.continuation用于获取Continuation实例(如果该属性没被占用)<br />
</span>&nbsp;<span style="color: #008000;">&nbsp; &nbsp;// 在setAttribute时，org.eclipse.jetty.server.Request.queryEncoding属性同时用于设置Request的QueryEncoding属性；<br />
&nbsp; &nbsp; // org.eclipse.jetty.server.sendContent同时用于直接向客户端发送Object指定的相应内容，该值必须是HttpContent、Resource、Buffer、InputStream类型；<br />
</span>&nbsp;<span style="color: #008000;">&nbsp; &nbsp;// org.eclipse.jetty.server.ResponseBuffer同时用于设置对客户端相应的ByteBuffer数据；org.eclipse.jetty.io.EndPoint.maxIdleTime同时用于设置EndPoint中的MaxIdleTime属性。<br />
</span>&nbsp;&nbsp;<span style="color: #008000;">&nbsp; // 如果注册了ServletRequestAttributeListener，则相应的attributeAdded、attributeReplaced、attributeRemoved方法会被调用。</span><br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Object&nbsp;getAttribute(String&nbsp;name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Enumeration&lt;String&gt;&nbsp;getAttributeNames();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span>&nbsp;setAttribute(String&nbsp;name,&nbsp;Object&nbsp;o);<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span>&nbsp;removeAttribute(String&nbsp;name);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ CharacterEncoding属性，用于指定读取请求内容时使用的编码方式，如果设置的编码方式不支持，抛出UnsupportedEncodingException。CharacterEncoding的设置必须在读取Parameter或getReader方法的调用之前，不然该方法不会有效果。该属性默认为null，此时Jetty默认使用UTF-8编码Parameter，使用ISO-8859-1编码方式创建Reader实例。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getCharacterEncoding();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setCharacterEncoding(String&nbsp;env)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;UnsupportedEncodingException;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">// 请求内容的长度，以字节为单位，-1表示长度未知。该值从HttpConnection的RequestFields字段中获取，该字段在HttpParser解析HTTP请求消息时填充。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getContentLength();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 返回请求内容的MIME type，即在请求头中设置的ContentType头，该值同样从HttpConnection的RequestFields字段中获取，该字段在HttpParser解析HTTP请求消息时填充。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getContentType();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 使用ServletInputStream包装请求内容的读取，该方法和getReader方法，只能使用一种方式来读取请求内容，否则抛出IllegalStateException。<br />
&nbsp; &nbsp; // ServletInputStream继承自InputStream，它只是实现了一个readLine函数。在该方法实现中返回HttpConnection的getInputStream，在该方法返回前，如果当前请求有Expect: 100-continue头，则先向客户端发送100 Continue相应，然后返回HttpInput实例，它继承自ServletInputStream，从HttpParser中读取数据。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ServletInputStream&nbsp;getInputStream()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 请求参数相关的操作，请求参数可以在URL使用?paramName=paramValue&amp;...的方式指定，也可是使用POST/PUT方法在请求消息体中指定(ContentType: application/x-www-form-urlencoded)，或同时存在。URL的parameter信息读取使用queryEncoding字段指定的编码方式(默认编码为UTF-8)，请求消息体中信息读取使用characterEncoding字段指定的编码方式(默认编码为ISO-8859-1)。在读取请求消息体中的parameter数据时，可以使用ContextHandler中的MaxFormContentSize属性或Server中的org.eclipse.jetty.server.Request.maxFormContentSize的Attribute配置最大支持的parameter数据大小，如果ContentLength超过该值，则抛出IllegalStateException。<br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ 相同的paramName可以多次出现，因而一个paramName可能对应多个值，此时使用getParameterValues()方法获取所有的值。<br />
&nbsp; &nbsp; // 如果使用POST指定请求参数，使用getInputStream或getReader读取数据会对parameter的读取有影响。</span><br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getParameter(String&nbsp;name);<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Enumeration&lt;String&gt;&nbsp;getParameterNames();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String[]&nbsp;getParameterValues(String&nbsp;name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Map&lt;String,&nbsp;String[]&gt;&nbsp;getParameterMap();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回当前请求使用的协议以及其版本号，如HTTP/1.1，对HTTP请求，它在解析请求行时设置。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getProtocol();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 返回请求的Schema，默认为http，在SSL相关的Connector的customize方法中会被设置为https。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getScheme();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回当前请求发送的服务器名称，如果请求URI中包含Host Name信息，则ServerName使用该信息；否则使用Host头(值为host:port)中的Server Name信息；<br />
&nbsp; &nbsp; // 如果不存在Host头，尝试使用EndPont中的LocalHost(LocalAddr，如果_dns值设置为false，即Connector的ResolveNames属性为false，其默认值为false)；<br />
&nbsp; &nbsp; // 否则使用当前Server的LocalHost的Address：InetAddress.getLocalHost().getHostAddress();<br />
&nbsp; &nbsp; // 对代理服务器，如果Jetty在Connector中开启了forwarded检查，如果Connector中设置了_hostHeader值，则使用强制设置Host头为该值，清除已有的ServerName和Port，并重新计算；<br />
&nbsp; &nbsp; // 否则如果存在X-Forwarded-Host头，强制设置Host头为该头值的最左边的值，即第一个代理服务器的主机名，清除已有的ServerName和Port，并重新计算；<br />
&nbsp; &nbsp; // 如果存在X-Forwarded-Server头，则强制设置Request的ServerName值为该头值的最左边的值，即第一个代理服务器的主机名。<br />
&nbsp; &nbsp; // 如果存在X-Forwarded-For头，则更新Request的RemoteAddr和RemoteHost值，即最原始客户端的地址和主机名。<br />
&nbsp; &nbsp; // 如果存在X-Forwarded-Proto头，则更新Request的Schema为该头值的最左边的值。(这是原始请求的Schema还是神马呢？)<br />
&nbsp; &nbsp; // 具体参考：</span><span style="color: #008000;">http://benni82.iteye.com/blog/849139</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getServerName();<br />
<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// ServerPort有一下查找路径：请求URI中的Port；Host头中值的Port；EndPoint的LocalPort；如果都没有找到，则对HTTPS默认值为443，对HTTP默认值为80</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getServerPort();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 将ServletInputStream包裹成BufferedReader类型，使用CharacterEncoding编码方式，如果没有设置该编码，默认使用ISO-8559-1，因而CharacterEncoding的设置必须在该方法调用之前。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;BufferedReader&nbsp;getReader()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回客户端的IP地址，如果开启了forwarded检查，则该值会被更新为原始的客户端请求IP，即使该请求穿越了好几个代理服务器。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getRemoteAddr();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 如果Connector设置了ResolveNames属性为true，即Request中_dns字段为true，则返回客户端主机名，否则返回客户端主机IP；如果开启了forwarded检查，则该值被更新为最原始的客户端的主机名或IP，即使该请求穿越了好几个代理服务器。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getRemoteHost();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回Accept-Language请求头中的指定的Locale值，支持多个值，以",", " ", "\t"等字符分隔，每个值都可以是如下格式：language-&lt;country&gt;;q&lt;value&gt;或language-&lt;country&gt;; q=&lt;value&gt;的格式，在选择一个Locale时使用qValue最大的那个。如果没有设置，默认使用当前服务器的Locale值。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Locale&nbsp;getLocale();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回Accept-Language头中指定的所有Locale枚举值，以qValue的值降序排列，如果没有指定该头，使用服务器默认的Locale值。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Enumeration&lt;Locale&gt;&nbsp;getLocales();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 检查当前请求是否在安全传输通道，如https。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isSecure();<br />
&nbsp; &nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回一个RequestDispatcher，内部使用ServletContext获取RequestDispatcher实例，根据传入的path计算uriInContext值：如果它以"/"开头，uriInContext的值即为该值，<br />
</span><span style="color: #008000; ">&nbsp; &nbsp; // 如果它不以"/"开头，即表示它是相对与当前请求的path，则uriInContext的值为相对于当前Request URI，如pathInfo为/foo/goo，path为poo，则uriInContext为：&lt;servletPath&gt;/foo/poo<br />
</span>&nbsp; &nbsp;<span style="color: #0000FF; ">public</span>&nbsp;RequestDispatcher&nbsp;getRequestDispatcher(String&nbsp;path);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回客户端的端口，调用EndPoint的getRemotePort方法。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getRemotePort();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回当前服务器的主机名，如果Connector的ResolveNames为true，否则为当前服务器的IP地址。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getLocalName();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回当前服务器的IP地址，调用EndPoint的getLocalAddr方法。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getLocalAddr();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回当前服务器的端口号，即当前连接使用的端口号，不是服务器本身监听的端口号。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getLocalPort();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">// 返回当前Request正在执行所在ServletContext。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ServletContext&nbsp;getServletContext();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 启动当前请求的异步模式，该方法调用后，即可以推出当前请求的处理方法，在退出之前需要将返回的AsyncContext放到一个等待Queue或类似的数据结构中，从而在之后的处理中还能得到这个<br />
&nbsp; &nbsp; // AsyncContext实例进一步处理这个Request，AsyncContext包含了对当前Request和Response实例的引用，一般唤起这个异步的请求，使用AsyncContext的dispatch方法，<br />
&nbsp; &nbsp; // 从而保证在下一次的处理过程中依然存在Filter链通道。这个方法和带ServletRequest、ServletResponse参数的方法区别在于:<br />
&nbsp; &nbsp; // 如果Servlet A对应为/url/A，在Servlet A中调用request.getRequestDispatcher("/url/B")，此时在Servlet B中，如果调用request.startAsync().dispatch()，此时会dispatch到/url/A，<br />
&nbsp; &nbsp; // 但是如果在Servlet B中调用request.startAsync(request, response).dispatch()，此时会dispatch到/url/B中。<br />
&nbsp; &nbsp; // 另外该方法会在调用之前注册的AsyncListener的onStartAsync()方法之后，清除这些已注册的AsyncListener。在onStartAsync方法中可以将自己重新注册到AsyncContext中，只是这个设计好奇怪。。。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;AsyncContext&nbsp;startAsync()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IllegalStateException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;AsyncContext&nbsp;startAsync(ServletRequest&nbsp;servletRequest,&nbsp;ServletResponse&nbsp;servletResponse)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IllegalStateException;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 查看是否当前request异步模式已经启动，即已经调用startAsync方法，但是还没有调用AsyncContext中的dispatch或onComplete方法。</span><span style="color: #008000; "><br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isAsyncStarted();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 查看当前Request是否支持异步模式，默认为true，如果Filter或Servlet不支持异步模式，这在调用对应的doFilter、service之前会把该值设置为false。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isAsyncSupported();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 获取最近一次调用startAsync方法时创建的AsyncContext实例，如果当前request没有异步模式还没有启动，则抛出IllegalStateException。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;AsyncContext&nbsp;getAsyncContext();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 获取当前请求的Dispatcher类型。Dispatcher Type是Container用于选区对应的Filter链。请求最初为Dispatcher.REQUEST；如果调用RequestDispatcher.forward()方法，<br />
&nbsp; &nbsp; // 则变为DispatcherType.FORWARD；如果调用RequestDispatcher.include()方法，则变为DispatcherType.INCLUDE；如果调用AsyncContext.dispatch()方法，则变为<br />
&nbsp; &nbsp; // DispatcherType.ASYNC；最后如果请求被dispatch到error page中，则为DispatcherType.ERROR。<br />
</span>&nbsp;<span style="color: #008000;">&nbsp; &nbsp;// DispatcherType在HttpConnection中的handleRequest方法中，在调用server.handle()/server.handleAsync()方法之前，设置为REQUEST、ASYNC，根据当前Request的AsyncContext是否处于初始化状态，如果是，则为REQUEST，否则为ASYNC状态，其他值则在Dispatcher中forward或include方法中设置。</span><br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;DispatcherType&nbsp;getDispatcherType();<br />
}</div>
HttpServletRequest接口定义如下：<br />
<div style="background-color: #eeeeee; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all; font-size: 13px;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;HttpServletRequest&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;ServletRequest&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ Servlet的四种验证方式(他们在web.xml文件中的long-config/auth-method中定义)：<br />
&nbsp; &nbsp; // BASIC使用Authentication头传输认证信息，该信息以Base64的编码方式编码，当需要用户验证时会弹出登陆窗口。<br />
&nbsp; &nbsp; // FORM使用表单的方式认证，用户名和密码在j_username和j_password字段中，登陆URL为/j_security_check，第一次使用表单方式明文传输，之后将认证信息存放在Session中，在Session实效之前可以不用在手动登陆。<br />
&nbsp; &nbsp; // DIGEST也时使用Authentication头传输认证信息，但是它使用加密算法将密码加密。<br />
&nbsp; &nbsp; // CLIENT_CERT则使用客户端证书的方式传输认证信息。<br />
&nbsp; &nbsp; // 更详细的内容参考：</span><span style="color: #008000;">http://docs.oracle.com/cd/E19798-01/821-1841/bncas/index.html</span><span style="color: #008000;">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;BASIC_AUTH&nbsp;=&nbsp;"BASIC";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;FORM_AUTH&nbsp;=&nbsp;"FORM";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;CLIENT_CERT_AUTH&nbsp;=&nbsp;"CLIENT_CERT";<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;DIGEST_AUTH&nbsp;=&nbsp;"DIGEST";<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回当前请求的认证方法，可以是BASIC、FORM、CLIENT_CERT、DIGEST。如果没有定义对应Servlet的认证配置，则返回null。在Jetty实现中，在ServletHandler根据配置信息以及认证的结果设置请求的Authentication实例，如果Authentication实例是Authentication.Deffered实例，则先验证，并设置验证后的Authentication实例，如果Authentication实例是Authentication.User实例，则设置返回其AuthMethod属性，否则返回null。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getAuthType();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">// 返回当前请求包含的Cookie数组，从请求的Cookie头中获取，如果没有Cookie信息，则返回null。在Jetty实现中，使用CookieCutter类解析Cookie头。<br />
&nbsp; &nbsp; // Cookie值的格式为：&lt;name&gt;=value; [$path=..; $domain=...; $port=...; $version=..]; &lt;name&gt;=&lt;value&gt;.....<br />
&nbsp; &nbsp; // 在Servlet中，Cookie类包含comment(描述信息)、domain(cookie只是对指定的domain域可见，默认情况下cookie只会传输给设置这个cookie的server)、maxAge(cookie的最长可存活秒数，负数表示永远不会失效，0表示cookie会被删除)、path(指定那个path下的请求这个cookie才会被发送，包括它的子目录)、secure(这个cookie是否只在https请求中发送)、version(0表示netscape最初定义的cookie标准，1表示兼容RFC 2109，一般都是0以保持最大兼容性)、isHttpOnly(HttpOnly标识的cookie一般不应该暴露给客户端的脚本，以部分解决跨站点脚本攻击问题)字段。对浏览器，一般他们应该能为每个Web Server存储至少20个cookie，总共至少能处理300个cookie，以及至少4k大小。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Cookie[]&nbsp;getCookies();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 解析指定请求头的Date值，并转换成long值，如If-Modified-Since头。Jetty支持的Date格式有：EEE, dd MMM yyyy HH:mm:ss zzz; EEE, dd-MMM-yy HH:mm:ss等。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;getDateHeader(String&nbsp;name);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回指定的请求头的值，没有该头，返回null，如果有多个相同名字的头，则返回第一个该头的值。name是大小写无关。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getHeader(String&nbsp;name);&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回指定请求头的所有值</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Enumeration&lt;String&gt;&nbsp;getHeaders(String&nbsp;name);&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回请求头的所有名称，有些Container会禁用该方法，此时返回null。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Enumeration&lt;String&gt;&nbsp;getHeaderNames();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回指定请求头的int值。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getIntHeader(String&nbsp;name);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回请求方法，如GET、POST、PUT等。该值在解析请求行结束后设置。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getMethod();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 设置请求的额外path信息，该值在不同的地方会被设置为不同的值，如果请求的URI为: http://host:80/context/servlet/path/info如在HttpConnection中，其值为/context/servlet/path/info<br />
&nbsp; &nbsp; // 在ContextHandler的doScope中，其值被设置为/servlet/path/info；在ServletHandler的doScope方法中，其值被设置为/path/info，并设置ServletPath为/servlet。<br />
</span><span style="color: #008000; ">&nbsp; &nbsp; // 这里基于的假设为contextPath为/context，servletPath为/servlet，即在web.xml配置中Servlet相关的url-pattern为/servlet/*。<br />
</span><span style="color: #008000;">&nbsp; &nbsp; // 如果没有/path/info后缀，则PathInfo为null，如果Servlet的path-pattern设置为/*，则ServletPath为""，对url-pattern为*.do类似的配置，pathInfo永远为null，servletPath则为/path/info.do的值；有些时候ServletPath的值也可能是Servlet Name的值。</span><br />
&nbsp; &nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getPathInfo();<br />
&nbsp; &nbsp;<span style="color: #0000ff;">public</span>&nbsp;String&nbsp;getServletPath();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回pathInfo对应的在ServletContext下的真是Server路径，如果pathInfo为null，则返回null。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getPathTranslated();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回当前请求所属的ContextPath。它在ContextHandler的doScope方法中或Dispatcher的forward方法中设置。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getContextPath();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000; ">// 返回当前请求的query字符串，如果设置了queryEncoding，使用该编码方式编码。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getQueryString();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回请求的登陆用户，如果用户没有认证，则返回null。该信息从设置的Authentication实例中获取，如果其实例为Authentication.Defered，则需要先验证，然后返回获取UserIdentity，并从中获取Principal，而从Principal中可以获取用户名。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getRemoteUser();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 验证当前请求的User是否在传入的Role中，即使用设置的Authentication实例验证，当前登陆的User所在的Roles中是否存在传入的RoleName。其中UserIdentity.Scope实例用于查找RoleName对应在Container使用的RoleName，如果没有这个映射则使用传入的RoleName本身，这个scope在ServletHandler的doScope方法中设置。这个映射在web.xml中的security-rol-ref(role-name-&gt;role-link)中设置。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isUserInRole(String&nbsp;role);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回当前登陆User的Principal实例，从设置的Authentication实例中获取，或者为null。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;java.security.Principal&nbsp;getUserPrincipal();<br />
<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000; ">// 返回客户端指定的Session ID，它可以和Server的当前Session ID不同。客户端可以使用两种方式设置该值：Cookie和URL。默认先从Cookie中找，找不到再从URL中找。<br />
&nbsp; &nbsp; // 对Cookie方式，在web.xml的cookie-config/name中配置Session的Cookie Name(默认值为JSSESSIONID)，找到该Cookie Name对应的Cookie值作为Requested Session ID<br />
&nbsp; &nbsp; // 对URL方式，在;&lt;sessionIdPathParamName&gt;=....[;#?/]之间的值作为Requested Session ID的值。其中sessionIdPathParamName可以通过web.xml的context-param，使用org.eclipse.jetty.servlet.SessionIdPathParameterName属性值配置，默认为jsessionid。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getRequestedSessionId();<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">boolean</span>&nbsp;isRequestedSessionIdFromCookie();<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">boolean</span>&nbsp;isRequestedSessionIdFromURL();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">// 检查当前Requested Session Id是否valid，在Jetty中valid是指RequstedSessionId存在，并且和当前请求的Server Session的ClusterId相同，即他们的SessionId相同。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">boolean</span>&nbsp;isRequestedSessionIdValid();&nbsp; &nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回/contextPath/servletPath/pathInfo的值。对forward后请求，该值为forward后的URI。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getRequestURI();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回getSchema()://getServerName():getPort()/getRequestURI()，对forward后请求，该值为forward后的URL。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;StringBuffer&nbsp;getRequestURL();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回和当前请求相关联的HttpSession，如果没有关联的HttpSession，且create为true，则创建新的HttpSession实例。没有参数即create为true。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;HttpSession&nbsp;getSession(<span style="color: #0000FF; ">boolean</span>&nbsp;create);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;HttpSession&nbsp;getSession();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//使用Container定义的认证机制验证请求用户的合法性。在Jetty实现中，只是对Authentication.Deferred的Authentication类型进行验证，否则返回401 Unauthorized错误相应，并返回false。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;authenticate(HttpServletResponse&nbsp;response)&nbsp;&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException,ServletException;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 提供用户名和密码并交由Container对其进行认证。在Jetty实现中，只对Authentication.Deferred类型提供用户名和密码认证，否则抛出ServletException。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;login(String&nbsp;username,&nbsp;String&nbsp;password)&nbsp;&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 注销当前用户，并清理_authentication字段。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;logout()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException;<br />
<br />
&nbsp; &nbsp; <span style="color: #008000; ">// 对multipart/form-data请求类型，表示请求内容由多个部分组成，此时使用RFC1867来解析该内容到多个Part中。在Servlet中一个Part有自己的请求头和请求消息提，包含ContentType、Name、Headers、Size(已写入的大小)、InputStream等信息，它还提供了一个方法将请求内容写入指定的文件中，以及删除该内部文件。并提供MultipartConfigElement类来做相关的配置，如写文件时的位置Location；最大可上传的文件大小MaxFileSize；最大可包含的所有Part的请求大小MaxRequestSize；如果写入的数据超过配置的大小，则开始将数据写入文件中MaxFileSizeThreshold。该配置信息可以使用org.eclipse.multipartConfig属性设置，而Location信息可以使用javax.servlet.context.tempdir属性在ServletContext中设置，或这使用java.io.tmpdir中指定的值。在Jetty中使用MultiPartInputStream来表达并解析请求内容到多个Part(MultiPart)中。具体格式可以参考：</span><span style="color: #008000;">http://blog.zhaojie.me/2011/03/html-form-file-uploading-programming.html</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Collection&lt;Part&gt;&nbsp;getParts()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException,&nbsp;ServletException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Part&nbsp;getPart(String&nbsp;name)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException,&nbsp;ServletException;<br />
}</div>
除了以上实现，Request还包含了一些额外的字段，如_timestamp在请求开始时设置，即解析请求头完成时；_dispatchTime在RequestLogHandler的handle方法，当Request是非Initial的状态下设置，即当前Request已经为Dispatched的状态；_handled表示该请求是否已经处理完成；<h2><span style="color: #ff6600;">ServletResponse</span></h2>
ServletResponse是对Servlet响应消息的封装，其子接口HttpServletResponse则是对HTTP响应消息的封装， <br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/Jetty_ServletResponse.jpg" alt="" height="736" width="1028" /><br />ServletResponse接口定义如下：<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;ServletResponse&nbsp;{<br />&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 设置与返回</span><span style="color: #008000;">消息体中的字符编码方式。如果没有设置，默认使用ISO-8559-1。其中setContentType和setLocale会隐式的设置该值，但是显示的设置会覆盖之前隐式的设置，而该值的设置也会影响ContentType的值。该值的设置必须在调用getWriter()方法之前，否则不会起作用。在set方法中，如果传入的charset为null，则清楚原来设置的值，并将原来的_mimeType值设置为Content-Type头；否则，更新characterEncoding的值，并更新contentType的属性以及Content-Type头(更新contentType中"charset="之后部分的值，或使用"mimeType; charset=&lt;characterType&gt;")。</span><span style="color: #008000;"><br /></span>&nbsp; &nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span>&nbsp;setCharacterEncoding(String&nbsp;charset);<br />&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getCharacterEncoding();<br /><br />&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 设置响应消息的Content-Type头以及contentType字段的值，它会同时影响mimeType字段以及characterEncoding字段。如果type为null，则清除contentType、mimeType的值，移除Content-Type响应头，并且如果locale也为null，同时清除characterType值；否则，设置mimeType为";"之前的值，而characterEncoding的值为"charset="之后的值，如果没有"charset="之后的值，则是使用"charset=&lt;characterType&gt;"值拼接以设置Content-Type响应头。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span>&nbsp;setContentType(String&nbsp;type);<br />&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getContentType();<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 设置Locale的值，同时设置响应的Content-Language头。同时该set也会影响characterEncoding、mimeType和Content-Type响应头。其中characterEncoding从ContextHandler中的Locale到charset的映射中获取，即web.xml中的locale-encoding-mapping-list中定义，而对Content-Type的值只影响"charset="之后的值。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span>&nbsp;setLocale(Locale&nbsp;loc);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;Locale&nbsp;getLocale();<br /><br />&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 设置contentLength字段以及Content-Length响应消息头。如果当前写入的数据已经大于或等于设置的值，则该方法还会关闭当前的ServletOutputStream或Writer。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span>&nbsp;setContentLength(<span style="color: #0000ff;">int</span>&nbsp;len);&nbsp;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回向客户端写数据的ServletOutputStream或PrintWriter。它们只能使用其中一个方法。在Jetty中使用HttpOutput内部封装HttpGenerator实例用于向Socket中写数据。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ServletOutputStream&nbsp;getOutputStream()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;PrintWriter&nbsp;getWriter()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;&nbsp; &nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 响应消息处理相关的Buffer操作，在Jetty中即为HttpGenerator中响应消息体的Buffer大小与刷新操作。设置BufferSize必须在写响应消息体之前。另外reset只是清除消息体的缓存，并不会清除响应状态码、响应头等信息，如果该方法调用时消息已经被commit，则抛出IllegalStateException。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setBufferSize(<span style="color: #0000FF; ">int</span>&nbsp;size);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getBufferSize();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;flushBuffer()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;resetBuffer();<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; &nbsp;<span style="color: #008000; ">// 是否响应消息已经commit，即响应消息状态行和消息头已经发送给客户端。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isCommitted();<br /><br />&nbsp; &nbsp;&nbsp;<span style="color: #008000; ">// 清除所有的响应消息状态，所有已经设置的响应头(但是不包括Connection头)，如果响应已经commit，则抛出IllegalStateException。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;reset();<br />}</div>HttpServletResponse接口定义如下，它添加了一些Cookie、Header相关的操作：<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;HttpServletResponse&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;ServletResponse&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 向响应消息中添加一个Cookie实例。即添加"Set-Cookie"头。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;addCookie(Cookie&nbsp;cookie);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 消息头操作：增删改查。一个相同名字的头可能会有多个条纪录，因而可以对应多个值。在Jetty实现中代理给HttpConnection中的responseFields字段。其中Date头的格式为：EEE, dd MMM yyyy HH:mm:ss 'GMT'，对于String为值的设置来说，处于INCLUDE Dispatch状态下，只能设置org.eclipse.jetty.server.include.&lt;HeaderName&gt;的头。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;containsHeader(String&nbsp;name);<br />&nbsp; &nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span>&nbsp;setDateHeader(String&nbsp;name,&nbsp;<span style="color: #0000ff;">long</span>&nbsp;date);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span>&nbsp;addDateHeader(String&nbsp;name,&nbsp;<span style="color: #0000ff;">long</span>&nbsp;date);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span>&nbsp;setHeader(String&nbsp;name,&nbsp;String&nbsp;value);<br />&nbsp; &nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span>&nbsp;addHeader(String&nbsp;name,&nbsp;String&nbsp;value);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span>&nbsp;setIntHeader(String&nbsp;name,&nbsp;<span style="color: #0000ff;">int</span>&nbsp;value);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span>&nbsp;addIntHeader(String&nbsp;name,&nbsp;<span style="color: #0000ff;">int</span>&nbsp;value);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;String&nbsp;getHeader(String&nbsp;name);&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;Collection&lt;String&gt;&nbsp;getHeaders(String&nbsp;name);&nbsp;<br />&nbsp; &nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;Collection&lt;String&gt;&nbsp;getHeaderNames();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 编码传入的url，决定并添加是否需要在URL中加入Session ID信息以作为Session追踪。处于健壮性的考虑，所有Servlet产生的URL必须使用改方法编码，不然对不支持Cookie的浏览器将会失去Session信息。在实现中，如果Request使用Cookie作为Session追踪，则去除url中的sessionId信息；否则如果session存在并可用，则向url中添加session追踪信息，在"#"或"?"之前。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;encodeURL(String&nbsp;url);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 编码传入的url，用于sendRedirect方法中。在Jetty中改方法直接调用encodeURL()方法。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;encodeRedirectURL(String&nbsp;url);<br /><br />&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 向客户端发送响应状态码和原因(如果有的话)。服务器会保留已经设置的cookie，但是会在必要情况下修改响应头。如果在web.xml中定义了响应的状态码到error page的映射，则该响应会被转发到那个错误页面中。在Jetty实现中，它清除Buffer信息，characterEncoding值，Expires、Last-Modified、Cache-Control、Content-Type、Content-Length头，设置响应状态和消息，对非204(No Content)、304(Not Modified)、206(Partial Content)、200(OK)的状态码(即允许有消息体)，首先查找有没有注册的ErrorHandler
，如果有，向Request的属性中设置javax.servlet.error.status_code, javax.servlet.error.message, javax.servlet.error.request_uri, javax.servlet.error.servlet_name为相应的值(RequestDispatcher中定义的属性key)，并调用ErrorHandler的handle方法；否则设置Cache-Control头为must-revalidate,no-cache,no-store，设置Content-Type头为text/html;charset=ISO-8859-1，并返回一个简单的错误页面包含状态码和消息原因。最后调用HttpConnection的completeResponse()方法以完成响应。</span><br />&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;sendError(<span style="color: #0000FF; ">int</span>&nbsp;sc,&nbsp;String&nbsp;msg)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;sendError(<span style="color: #0000FF; ">int</span>&nbsp;sc)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 发送客户端一个重定向的消息和URL，即设者响应状态码为302 Moved Temporary，在Location中包含要重定向目的地的URL，此时客户端会使用新的URL重新发送请求。如果location以"/"开头，表示它是绝对地址，否则为相应请求的相对地址，即最终解析成Request.getRequestURI()/location，location可以包含query信息。最后调用HttpConnection的completeResponse()方法以完成当前响应。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;sendRedirect(String&nbsp;location)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 设置响应状态码和状态描述信息。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setStatus(<span style="color: #0000FF; ">int</span>&nbsp;sc);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setStatus(<span style="color: #0000FF; ">int</span>&nbsp;sc,&nbsp;String&nbsp;sm);<br />&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getStatus();<br />}</div><img src ="http://www.blogjava.net/DLevin/aggbug/413534.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-05-16 01:29 <a href="http://www.blogjava.net/DLevin/archive/2014/05/16/413534.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之Servlet框架及实现(Servlet、Filter、Registration)</title><link>http://www.blogjava.net/DLevin/archive/2014/05/11/413500.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sun, 11 May 2014 15:52:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/05/11/413500.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/413500.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/05/11/413500.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/413500.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/413500.html</trackback:ping><description><![CDATA[<h2><span style="color: #ff6600;">概述</span></h2>
Servlet是Server Applet的缩写，即在服务器端运行的小程序，而Servlet框架则是对HTTP服务器(Servlet Container)和用户小程序中间层的标准化和抽象。这一层抽象隔离了HTTP服务器的实现细节，而Servlet规范定义了各个类的行为，从而保证了这些&#8220;服务器端运行的小程序&#8221;对服务器实现的无关性(即提升了其可移植性)。<br />
在Servlet规范有以下几个核心类(接口)：<br />
<strong>ServletContext</strong>：定义了一些可以和Servlet Container交互的方法。<br />
<strong>Registration</strong>：实现Filter和Servlet的动态注册。<br />
<strong>ServletRequest(HttpServletRequest)</strong>：对HTTP请求消息的封装。<br />
<strong>ServletResponse(HttpServletResponse)</strong>：对HTTP响应消息的封装。<br />
<strong>RequestDispatcher</strong>：将当前请求分发给另一个URL，甚至ServletContext以实现进一步的处理。<br />
<strong>Servlet(HttpServlet)</strong>：所有&#8220;服务器小程序&#8221;要实现了接口，这些&#8220;服务器小程序&#8221;重写doGet、doPost、doPut、doHead、doDelete、doOption、doTrace等方法(HttpServlet)以实现响应请求的相关逻辑。<br />
<strong>Filter(FilterChain)</strong>：在进入Servlet前以及出Servlet以后添加一些用户自定义的逻辑，以实现一些横切面相关的功能，如用户验证、日志打印等功能。<br />
<strong>AsyncContext</strong>：实现异步请求处理。<br />
<h2><span style="color: #ff6600;">Jetty中的Holder</span></h2>
在Jetty中，每个Servlet和其相关信息都由ServletHolder封装，并且将Servlet相关操作代理给ServletHolder；同理，对Filter也有FilterHolder与其相对应；另外ServletHolder和FilterHolder都继承自Holder实例。在Servlet 3.0中引入动态向ServletContext注册Servlet和Filter，并返回相应的Registration实例，用于进一步配置与其关联的Servlet和Filter，因而Registration也是和ServletHolde和FilterHolder相关联的接口。在Jetty中，他们的类关系图如下：<br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/Jetty_Holder.jpg" alt="" height="848" width="1370" /><br />
<h2><span style="color: #ff6600;">Servlet</span></h2>
Servlet和Filter是Servlet规范中用于定义用户逻辑实现的接口，Servlet是最初的版本，所有的&#8220;服务器端小程序&#8221;都要实现该接口，并交由Servlet Container管理其实例，负责其生命周期，以及当相应请求到来时调用相应方法。Servlet接口非常简单：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Servlet&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// Servlet Container在创建一个Servlet后调用该方法，并传入ServletConfig实例，从而用户可以在这个方法中做一些自定义的初始化工作，如初始化数据库连接等。<br />
&nbsp; &nbsp; // 并且Servlet Container可以保证一个Servlet实例init方法之后被调用一次，但是对一个Servlet类init方法可能会被多次调用，因而有些Servlet Container可能会在某些情况下将某些<br />
&nbsp; &nbsp; // Servlet移出Servlet Container，而后又重新加载这些Servlet，如为了在处理Servlet Container资源压力比较大的情况下。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;init(ServletConfig&nbsp;config)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回在init方法中传入的ServletConfig实例。ServletConfig包含了在web.xml配置文件中配置当前Servlet的初始化参数，并且可以使用该ServletConfig实例获取当前ServletContext实例。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ServletConfig&nbsp;getServletConfig();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 当该Servlet对应的请求到来时，Servlet Container会调用这个Servlet的service方法，用于处理请求，并将相应写入ServletResponse参数中，该方法只能在init方法完成后被调用。<br />
&nbsp; &nbsp; // 在service方法中，可以选择使用ServletResponse定义的方法返回响应给客户端，或只是向ServletResponse中写入响应，最终由Servlet Container根据ServletResponse的信息将响应返回给客户端。<br />
&nbsp; &nbsp; // 很多情况下，Servlet Container都不会使用多线程来处理客户请求，应该该方法会在多线程环境下被使用，Servlet实现者可以实现SingleThreadMode接口以强制该方法只在单线程的环境下被使用。<br />
&nbsp; &nbsp; // 但是SingleThreadMode接口已经在Servlet 2.3中被废弃，实现该接口也会影响Servlet的执行性能，而且有些Servlet Container会选择实例或多个Servlet实例，以保证对请求的响应性能，因而此时依然不能保证该方法的单线程特性，因而不建议使用这个SingleThreadMode接口。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;service(ServletRequest&nbsp;req,&nbsp;ServletResponse&nbsp;res)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException,&nbsp;IOException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回当前Servlet的描述信息，如作者、版本号、版权等。在GenericServlet默认实现中，返回空字符串。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getServletInfo();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">// 当Servlet Container销毁当前Servlet时会调用该方法，从而给Servlet提供拥有清理当前Servlet占用的资源的地方，如关闭和数据库的连接等。<br />
&nbsp; &nbsp; // Servlet Container会保证该方法在所有执行service方法的线程完成或超时后被调用。Servlet Container一般会在三种情况下会调用该方法：<br />
&nbsp; &nbsp; // 1. Servlet Container当前资源比较紧张，需要将一些不再用或不常用的Servlet移出；2. 实现某些Servlet的热部署；3. 当前Web Application被停止。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;destroy();<br />
}</div>
对每个Servlet都有一个ServletConfig实例和其对应，ServletConfig中包含了在web.xml文件中定义的Servlet的init-param参数，并且可以使用ServletConfig实例获取ServletContext实例：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;ServletConfig&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回该ServletConfig对应的Servlet的名称，在web.xml文件中定义的Servlet名称或对于没有注册的Servlet为其类名。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getServletName();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回和其关联的ServletContext。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ServletContext&nbsp;getServletContext();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回在web.xml配置文件中定义或者使用ServletRegistration动态定义添加的init-param参数。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getInitParameter(String&nbsp;name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Enumeration&lt;String&gt;&nbsp;getInitParameterNames();<br />
}</div>
在Jetty中，不管是在web.xml中配置的Servlet还是使用ServletContext动态注册并使用ServletRegistration.Dynamic动态配置的Servlet，在ServletHandler内部都使用ServletHolder来表示一个Servlet，并且由ServletHolder来处理所有和Servlet相关的逻辑。ServletHolder的实现逻辑在之后给出。<br />Servlet框架中默认实现了两个Servlet：GenericServlet和HttpServlet，GenericServlet只是对Servlet的简单实现，而HttpServlet会根据请求中的方法将请求分发给相应的：doGet/doPost/doPut/doHead/doOptions/doTrace等方法，它还提供了getLastModified()方法，重写该方法用于实现条件GET。以上这些方法(除doOptions和doTrace已经有具体的逻辑实现)默认实现直接方法405 Method Not Allowed响应(Http/1.1)或404 Bad Request响应(HTTP/1.0)。重写相应的方法以实现各个Method对应的逻辑。<br />
<h2><span style="color: #ff6600;">Filter</span></h2>
在Servlet 2.3开始引入了Filter机制，以在Servlet的service方法的执行前后添加一些公共的Filter逻辑，为面向切面的编程提供了很大的便利，这些公共的逻辑如纪录一个Request从进入Servlet Container到出所花费的总时间、为每个Request添加一些额外的信息以帮助之后处理、对所有或特定Request添加用户验证功能等。Filter可以在web.xml文件中定义，由Servlet Container负责其实例化、初始化以及doFilter方法的调用。在Servlet 3.0以后，还支持动态的给ServletContext注册Filter，并由返回的FilterRegistration.Dynamic实例做进一步的配置。Filter的接口定义也是比较简单：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Filter&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 由ServletContainer在初始化一个Filter时调用，Filter的实现者可以在该方法中添加一些用户自定义的初始化逻辑，同时可以保存FilterConfig实例，它可以获取定义的init-param初始化参数以及获取ServletContext实例。其他情况和Servlet类似，不赘述。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;init(FilterConfig&nbsp;filterConfig)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 每一次请求到来都会穿越配置的FilterChain，执行配置的Servlet，然后从这个FilterChain中返回。在doFilter方法的实现中，要调用下一个Filter，使用FilterChain的doFilter方法。<br />
&nbsp; &nbsp; // 在调用FilterChain的doFilter之前为执行请求之前的处理，而之后为请求已经执行完成，在响应返回的路上的逻辑处理。也可以步调用FilterChain的doFilter方法，以阻止请求的进一步处理。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;doFilter(ServletRequest&nbsp;request,&nbsp;ServletResponse&nbsp;response,&nbsp;FilterChain&nbsp;chain)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException,&nbsp;ServletException;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 在Filter被移出Servlet Container时调用，它和Servlet类似，不赘述。**</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;destroy();<br />
}</div>
Filter接口中包含对FilterConfig以及FilterChain的使用，FilterConfig和ServletConfig定义、实现以及逻辑都类似：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;FilterConfig&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getFilterName();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ServletContext&nbsp;getServletContext();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getInitParameter(String&nbsp;name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Enumeration&lt;String&gt;&nbsp;getInitParameterNames();<br />
}</div>
FilterChain是Servlet中将Filter链以Channel的方式连接在一起的类，它实现了可以向执行Servlet前和后都添加一些切面逻辑，甚至阻止Servlet的执行，Filter的实现者使用FilterChain的doFilter方法调用在这个链中的下一个Filter的doFilter方法，如果当前Filter是链中最后一个Filter，则调用响应的Servlet。其接口定义如下：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;FilterChain&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">// 调用该方法会调用Filter链中的下一个Filter的doFilter方法，如果当前Filter是这条链中的最后一个Filter，则该方法会调用响应的Servlet的service方法。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;doFilter&nbsp;(ServletRequest&nbsp;request,&nbsp;ServletResponse&nbsp;response)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException,&nbsp;ServletException;<br />
}</div>
在Jetty中，ServletHandler的内部类Chain实现了FilterChain接口，在构造Chain实例时，首先根据Request的URL以及对应Servlet Name查找所有相关的Filter列表，然后使用这个Filter列表、Request实例、当前请求对应的ServletHolder创建这个链，在其doFilter方法实现中，它会存储一个_filter索引，它指向下一个Filter实例，当每个Filter调用doFilter方法时，Chain会根据这个索引获取下一个Filter实例，并将该索引向后移动，从而调用下一个Filter的doFilter方法，如果这个索引值到达最后一个Filter链中的Filter，且有ServletHolder实例存在，则调用该ServletHolder的handle方法，否则调用notFound方法，即向客户端发送404 NOT FOUND响应。如果Filter不支持ASYNC模式，则在调用其doFilter之前，需要将Request的ASYNC支持设置为false。<br />
在ServletHandler中还有CachedChain实现了FilterChain接口，它以链表的形式纪录找到的Filter列表，并将这个列表缓存在ServletHandler中，不同的dispatch类型有一个列表，并且可以根据请求的URL或请求的Servlet名来查找是否已经有缓存的Filter链表。<br />
在ServletHandler中，可以使用setFilterChainsCached方法来配置是否使用CachedChain还是直接使用Chain，默认使用CachedChain。<br />
<h2><span style="color: #ff6600;">Registration</span></h2>
Registration是Servlet 3.0规范中引入的接口，用于表示向ServletContext中动态注册的Servlet、Filter的实例，从而实现对这些动态注册的Servlet、Filter实例进行进一步的配置。 对于Servlet和Filter的配置，他们的共同点是他们有响应的Name、ClassName、Init Parameters以及asyncSupported属性，而这些方法正是Registration接口的定义。Registration接口将setAsyncSupport方法定义在其内部的Dynamic接口中，Dynamic用于表示这是用于动态的配置这个Servlet或Filter的含义，但是为什么要将这个方法放在Dynamic接口中呢？如何决定不同的方法应该是在Registration本身的接口中，而那些应该放到Dynamic接口中呢？<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Registration&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回这个Registration实例关联的Servlet或Filter的Name，这个Name在向ServletContext注册Servlet或Filter时给定。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getName();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回这个Registration实例关联的Servlet或Filter的类名。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getClassName();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 和这个Registration实例关联的初始化参数的操作。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;setInitParameter(String&nbsp;name,&nbsp;String&nbsp;value);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getInitParameter(String&nbsp;name);<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Set&lt;String&gt;&nbsp;setInitParameters(Map&lt;String,&nbsp;String&gt;&nbsp;initParameters);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Map&lt;String,&nbsp;String&gt;&nbsp;getInitParameters();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Dynamic&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Registration&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 配置Registration关联的Servlet或Filter是否支持异步操作。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setAsyncSupported(<span style="color: #0000FF; ">boolean</span>&nbsp;isAsyncSupported);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</div>
Registration有两个子接口：ServletRegistration和FilterRegistration，分别用于表示Servlet相关的配置和Filter相关的配置。<br />
对ServletRegistration，它可以设置Servlet的URL Mapping、RunAsRole属性、LoadOnStartup属性等：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;ServletRegistration&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Registration&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">// 添加URL patterns到这个ServletRegistration关联的Servlet的映射。如果有任意的URL patterns已经映射到其他的Servlet中，则该方法不会执行任何行为。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Set&lt;String&gt;&nbsp;addMapping(String...&nbsp;urlPatterns);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">// 获取所有到当前ServletRegistration对应的Servlet的URL patterns。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Collection&lt;String&gt;&nbsp;getMappings();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 获取当前ServletRegistration对应的Servlet的RunAsRole。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getRunAsRole();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Dynamic&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;ServletRegistration,&nbsp;Registration.Dynamic&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 设置当前ServletRegistration对应的Servlet的loadOnStartup等级。如果loadOnStartup大于或等于0，表示Servlet Container要优先初始化该Servlet，<br />
&nbsp; &nbsp; &nbsp; &nbsp; // 此时Servlet Container要在Container初始化时实例化并初始化该Servlet，即在所有注册的ContextListener的contextInitialized方法调用完成后。<br />
&nbsp; &nbsp; &nbsp; &nbsp; // 如果loadOnStartup小于0，则表示这个Servlet可以在用到的时候实例化并初始化。默认值为-1。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setLoadOnStartup(<span style="color: #0000FF; ">int</span>&nbsp;loadOnStartup);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 设置ServletRegistration相关的ServletSecurityElement属性。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Set&lt;String&gt;&nbsp;setServletSecurity(ServletSecurityElement&nbsp;constraint);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 设置ServletRegistration对应的Servlet的MultipartConfigElement属性。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setMultipartConfig(MultipartConfigElement&nbsp;multipartConfig);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 设置ServletRegistration对应的Servlet的RunAsRole属性。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setRunAsRole(String&nbsp;roleName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</div>
对FilterRegistration，它可以配置Filter的URL Mapping和Servlet Mapping等：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;FilterRegistration&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Registration&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 添加FilterRegistration关联的Filter到Servlet的映射，使用Servlet Name、DispatcherType作为映射。isMatchAfter表示新添加的Mapping是在已有的Mapping之前还是之后。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;addMappingForServletNames(EnumSet&lt;DispatcherType&gt;&nbsp;dispatcherTypes,&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isMatchAfter,&nbsp;String...&nbsp;servletNames);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 获取当前FilterRegistration关联的Filter已存在的到Servlet Name的映射。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Collection&lt;String&gt;&nbsp;getServletNameMappings();<br />
<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 添加FilterRegistration关联的Filter到Servlet的映射，使用URL patterns、DispatcherType作为映射。isMatchAfter表示新添加的Mapping是在已有的Mapping之前还是之后。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;addMappingForUrlPatterns(EnumSet&lt;DispatcherType&gt;&nbsp;dispatcherTypes,&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isMatchAfter,&nbsp;String...&nbsp;urlPatterns);<br />
<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 获取当前FilterRegistration关联的Filter已存在的到URL patterns的映射。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Collection&lt;String&gt;&nbsp;getUrlPatternMappings();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Dynamic&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;FilterRegistration,&nbsp;Registration.Dynamic&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</div>
在Jetty中对Registration的实现在Holder中定义，而相应的ServletRegistration和FilterRegistration实现作为ServletHolder和FilterHolder中的内部类实现，具体参考这两个类的实现。<br />
<h2><span style="color: #ff6600;">Holder实现</span></h2>
在之前有提到，在Jetty中Servlet和Filter由相应的ServletHolder和FilterHolder封装，以将Servlet/Filter相关的信息和配置放在一起，并处理各自相关的逻辑，即面向对象设计中的将数据靠近操作。由于Servlet和Filter有一些相同的配置和逻辑，因而在ServletHolder和FilterHolder中提取出了Holder父类。在Holder的实现中，它主要定义了一些Servlet和Filter都要使用的字段，比高实现了所有和InitParameter相关的操作：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">enum</span>&nbsp;Source&nbsp;{&nbsp;EMBEDDED,&nbsp;JAVAX_API,&nbsp;DESCRIPTOR,&nbsp;ANNOTATION&nbsp;};<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;Source&nbsp;_source;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">protected</span>&nbsp;<span style="color: #0000FF; ">transient</span>&nbsp;Class&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;T&gt;&nbsp;_class;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">protected</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;Map&lt;String,String&gt;&nbsp;_initParams=<span style="color: #0000FF; ">new</span>&nbsp;HashMap&lt;String,String&gt;(3);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">protected</span>&nbsp;String&nbsp;_className;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">protected</span>&nbsp;String&nbsp;_displayName;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">protected</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;_extInstance;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">protected</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;_asyncSupported=<span style="color: #0000FF; ">true</span>;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">protected</span>&nbsp;String&nbsp;_name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">protected</span>&nbsp;ServletHandler&nbsp;_servletHandler;</div>
Holder继承自AbstractLifeCycle，它在start时，如果_class字段没有被设置，则会使用ClassLoader加载_className中指定的类实例并赋值给_class字段；Source只是目前只是一种元数据的形式存在，用于表示Servlet或Filter的来源；而extInstance用于表示Servlet和Filter实例是直接通过ServletContext注册而来，而不是在当前Holder内部创建。<br />
在Holder类中还定了两个内部类：HolderConfig和HolderRegistration，其中HoldConfig实现了ServletConfig/FilterConfig相关的所有InitParameter相关的操作(代理给Holder)；HolderRegistration实现了Registration.Dynamic接口，其实现也都代理给Holder类中的方法。<br />
<h2><span style="color: #ff6600;">FilterHolder实现</span></h2>
FilterHolder实现比较简单，它直接继承自Holder类，它额外的包含了一下几个字段：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">transient</span>&nbsp;Filter&nbsp;_filter;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">transient</span>&nbsp;Config&nbsp;_config;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">transient</span>&nbsp;FilterRegistration.Dynamic&nbsp;_registration;</div>
其中_filter字段在start时如果没有初始化，则使用ServletContext创建该Filter实例，而_config字段则在启动时直接创建Config实例(Config是FilterHolder的内部类，且它继承自HolderConfig，并实现了FilterConfig接口)，最后调用_filter.init()方法并传入_config实例。在stop时，调用_filter.destroy()方法从而该Filter有机会做一些清理工作，并且调用ServletHandler中的destroyFilter()方法，以通知ContextHandler中定义的Decorators。在注册外部实例化的Filter时，设置_extInstance为true，同时更新_class字段，以及_name字段(如果_name字段未被设置的话)。<br />
最后，FilterHolder中还定义了Registration内部类，它继承自HolderRegistration，并实现了FilterRegistration.Dynamic接口。该Registration内部类实现了Mapping相关方法，Jetty中使用FilterMapping来表达一个Filter的映射关系。在FilterMapping中定义了一下映射关系：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;_dispatches=DEFAULT;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;String&nbsp;_filterName;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">transient</span>&nbsp;FilterHolder&nbsp;_holder;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;String[]&nbsp;_pathSpecs;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;String[]&nbsp;_servletNames;</div>
它包含了两个appliesTo()方法，这两个方法在Chain用于根据dispatcherType或dispatcherType以及path计算当前FilterMapping是否匹配给定的dispatcherType或dispatcherType和path。在对dispatcherType做匹配计算时，使用FilterMapping实例的没有设置dispatcherType集合，它依然匹配REQUEST或ASYNC(如果Filter支持ASYNC模式的话)。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;appliesTo(<span style="color: #0000FF; ">int</span>&nbsp;type)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(_dispatches==0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;type==REQUEST&nbsp;||&nbsp;type==ASYNC&nbsp;&amp;&amp;&nbsp;_holder.isAsyncSupported();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;(_dispatches&amp;type)!=0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</div>
<h2><span style="color: #ff6600;">ServletHolder实现</span></h2>
ServletHolder实现相对复杂，它继承自Holder类，并实现了UserIdentity.Scope接口以及Comparable接口，其中Comparable接口用于当ServletHandler在start时，对注册的所有ServletHolder的数组排序以决定他们的start顺序。它包含了一下额外的字段：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;_initOrder;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;_initOnStartup=<span style="color: #0000FF; ">false</span>;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;Map&lt;String,&nbsp;String&gt;&nbsp;_roleMap;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;String&nbsp;_forcedPath;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;String&nbsp;_runAsRole;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;RunAsToken&nbsp;_runAsToken;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;IdentityService&nbsp;_identityService;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;ServletRegistration.Dynamic&nbsp;_registration;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">transient</span>&nbsp;Servlet&nbsp;_servlet;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">transient</span>&nbsp;Config&nbsp;_config;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">transient</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;_unavailable;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">transient</span>&nbsp;UnavailableException&nbsp;_unavailableEx;<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;Map&lt;String,String&gt;&nbsp;NO_MAPPED_ROLES&nbsp;=&nbsp;Collections.emptyMap();</div>
其中_initOrder为ServletRegistration.Dynamic接口的实现，它定义ServletHolder在ServletHandler中的start顺序，即compareTo()方法的实现的主要参考信息。在Jetty中，只要设置了loadOnStart字段，则它就会在start时被初始化(即使设置的值为负数)。在设置外部Servlet实例直接到ServletHolder中时，_extInstance字段为被设置为true。如果在web.xml中的Servlet定义中有jsp-file定义，则设置该ServletHolder的forcePath值为该jsp-file中定义的值，而其className的值为servlet-class的定义，此时在ServletHolder的handle方法中会将forcePath的值设置到Request的org.apache.catalina.jsp_file属性中(这个设置用来干嘛呢？还不了解。。。)；在ServletHolder启动时，如果Servlet实例实现了SingleThreadModel接口，则Servlet实例使用SingleThreadedWrapper类来表示(它包含一个Servlet栈 ，对每次请求，它会复用以前已经创建的Servlet实例或者创建一个新的实例以处理当前的请求，即该Servlet会有多个实例)，如果_extInstance为true或者配置了loadOnStart属性，则在ServletHolder启动是就会初始化这个Servlet(实例化Servlet，并调用Servlet的init方法)。当一个请求到来时，ServletHolder调用其handle方法以处理该请求，在该方法中调用Servlet的service方法，如果在处理过程中出错了，则在Request中设置javax.servlet.error.servlet_name属性为ServletHolder的Name属性。<br />
<br />
类似FilterMapping，Jetty也使用ServletMapping表达Servlet和URL pattern的映射关系：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;String[]&nbsp;_pathSpecs;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;String&nbsp;_servletName;</div><br />其他Security相关的字段和逻辑暂不做介绍。。。。。<img src ="http://www.blogjava.net/DLevin/aggbug/413500.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-05-11 23:52 <a href="http://www.blogjava.net/DLevin/archive/2014/05/11/413500.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之Servlet框架及实现(ServletContext)</title><link>http://www.blogjava.net/DLevin/archive/2014/05/11/413395.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sat, 10 May 2014 17:22:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/05/11/413395.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/413395.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/05/11/413395.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/413395.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/413395.html</trackback:ping><description><![CDATA[<div>
<h2><font color="#ff6600">概述</font></h2>
Servlet是Server Applet的缩写，即在服务器端运行的小程序，而Servlet框架则是对HTTP服务器(Servlet Container)和用户小程序中间层的标准化和抽象。这一层抽象隔离了HTTP服务器的实现细节，而Servlet规范定义了各个类的行为，从而保证了这些&#8220;服务器端运行的小程序&#8221;对服务器实现的无关性(即提升了其可移植性)。<br />
在Servlet规范有以下几个核心类(接口)：<br />
<strong>ServletContext</strong>：定义了一些可以和Servlet Container交互的方法。<br />
<strong>Registration</strong>：实现Filter和Servlet的动态注册。<br />
<strong>ServletRequest(HttpServletRequest)</strong>：对HTTP请求消息的封装。<br />
<strong>ServletResponse(HttpServletResponse)</strong>：对HTTP响应消息的封装。<br />
<strong>RequestDispatcher</strong>：将当前请求分发给另一个URL，甚至ServletContext以实现进一步的处理。<br />
<strong>Servlet(HttpServlet)</strong>：所有&#8220;服务器小程序&#8221;要实现了接口，这些&#8220;服务器小程序&#8221;重写doGet、doPost、doPut、doHead、doDelete、doOption、doTrace等方法(HttpServlet)以实现响应请求的相关逻辑。<br />
<strong>Filter(FilterChain)</strong>：在进入Servlet前以及出Servlet以后添加一些用户自定义的逻辑，以实现一些横切面相关的功能，如用户验证、日志打印等功能。<br />
<strong>AsyncContext</strong>：实现异步请求处理。<br />
<h2><span style="color: #ff6600;">ServletContext</span></h2>
Context在这里是指一个Web Application的上下文(Web Application是一个Server子URL下的Servlet和资源的集合)，即它包含了这个Web Application级别的信息，如当前Web Application对应的根路径、使用的Servlet版本、使用的ClassLoader等，在一个JVM中的一个Web Application只能有一个Context(一个JVM可以包含多个Web Application，它们包含不同的根路径，即不同的Context路径，Context路径可以是空("/")即这个JVM只能包含一个Web Application)。ServletContext则是对这个Context的抽象，它还定义了一些和Servlet Container交互的方法，如获取文件的MINE Type、Dispatch请求到另一个URL或Context、将日志写入文件、根据提供的路径获取Resource实例、向这个ServletContext注册并获取Servlet或Filter、向这个ServletContext注册并获取Attribute或初始参数、向这个ServletContext注册或获取相关Listener等。对Distributed的Web Application来说，每个JVM下的Web Application都有一个独立的ServletContext，因而ServletContext不可以作为全局信息存储的地方，因而它并没有分布式信息同步的功能，即它只是本地的ServletContext。在Servlet中，使用ServletConfig实例可以获取ServletContext实例。<br />
类图如下：<br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/ServletContext2.jpg" alt="" height="797" width="1112" /><br />
ServletContext的接口定义如下：<br />
<div style="background-color: #eeeeee; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><span style="font-size: 13px; color: #0000ff;">public</span><font size="2">&nbsp;</font><span style="font-size: 13px; color: #0000ff;">interface</span><font size="2">&nbsp;ServletContext&nbsp;{</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;</font><span style="font-size: 13px; color: #008000;">// Servlet Container为当前Web Application设置临时目录，并将该临时目录的值存储到当前ServletContext的属性中使用的属性名。</span><span style="font-size: 13px; color: #008000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-size: 13px; color: #008000;">// Jetty使用WebInfConfiguration(在preConfig
ure()方法中)来设置该值，设置temp目录的规则：<br />
</span><span style="font-size: 13px; color: #008000;">&nbsp; &nbsp;&nbsp;</span><span style="font-size: 13px; color: #008000;">// 1. 如果存在WEB-INF/work目录，则temp目录的值为：WEB-INF/work/Jetty_&lt;host&gt;_&lt;port&gt;__&lt;resourceBase&gt;_&lt;context&gt;_&lt;virtualhost+base36_hashcode_of_whole_string&gt;<br />
</span><span style="font-size: 13px; color: #008000;">&nbsp; &nbsp;&nbsp;</span><span style="font-size: 13px; color: #008000;">// 2. 如果"javax.servlet.context.tempdir"已经在外部被设置，并且该目录存在且可写，则temp目录直接设置为该目录实例。<br />
</span><span style="font-size: 13px; color: #008000;">&nbsp; &nbsp;&nbsp;</span><span style="font-size: 13px; color: #008000;">// 3. 如果系统变量中"jetty.home"目录下存在"work"目录且可写，则设置temp目录的值为：${jetty.home}/work/</span><span style="font-size: 13px; color: #008000;">Jetty_&lt;host&gt;_&lt;port&gt;__&lt;resourceBase&gt;_&lt;context&gt;_&lt;virtualhost+base36_hashcode_of_whole_string&gt;</span><br />
<span style="font-size: 13px; color: #008000;">&nbsp; &nbsp;&nbsp;</span><span style="font-size: 13px; color: #008000;">// 4. 如果存在"org.eclipse.jetty.webapp.basetempdir"的属性，且该目录存在并可写，设置temp目录为：${</span><span style="font-size: 13px; color: #008000;">org.eclipse.jetty.webapp.basetempdir</span><span style="font-size: 13px; color: #008000;">}/</span><span style="font-size: 13px; color: #008000;">Jetty_&lt;host&gt;_&lt;port&gt;__&lt;resourceBase&gt;_&lt;context&gt;_&lt;virtualhost+base36_hashcode_of_whole_string&gt;</span><br />
<span style="font-size: 13px; color: #008000;">&nbsp; &nbsp;&nbsp;</span><span style="font-size: 13px; color: #008000;">// 5. 如果以上条件都不成立，则设置temp目录为：</span><span style="font-size: 13px; color: #008000;">${</span><span style="font-size: 13px; color: #008000;">java.io.tmpdir</span><span style="font-size: 13px; color: #008000;">}/</span><span style="font-size: 13px; color: #008000;">Jetty_&lt;host&gt;_&lt;port&gt;__&lt;resourceBase&gt;_&lt;context&gt;_&lt;virtualhost+base36_hashcode_of_whole_string&gt;，且删除已存在临时目录。<br />
</span><span style="font-size: 13px; color: #008000;">&nbsp; &nbsp;&nbsp;</span><span style="font-size: 13px; color: #008000;">// 注：对temp目录的父目录不是work，会注册在JVM退出时删除该temp目录，并在temp目录下创建.active目录。<br />
</span><br />
<font size="2">&nbsp; &nbsp;&nbsp;</font><span style="font-size: 13px; color: #0000ff;">public</span><font size="2">&nbsp;</font><span style="font-size: 13px; color: #0000ff;">static</span><font size="2">&nbsp;</font><span style="font-size: 13px; color: #0000ff;">final</span><font size="2">&nbsp;String&nbsp;TEMPDIR&nbsp;=&nbsp;"javax.servlet.context.tempdir";</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;</font><span style="font-size: 13px; color: #008000;">/</span><span style="font-size: 13px; color: #008000;">/ Servlet 3.0中新引入的特性，即可以在WEB-INF/lib下的jar包中定义/META-INF/web-fragment配置响应的Servlet等。<br />
</span><font size="2">&nbsp; &nbsp;&nbsp;</font><span style="font-size: 13px; color: #008000;">/</span><span style="font-size: 13px; color: #008000;">/ 如果在web.xml文件中定义了absolute-ordering，或者在jar包中存在/META-INF/web-fragment.xml文件，且定义了ordering，<br />
</span><font size="2">&nbsp; &nbsp;&nbsp;</font><span style="font-size: 13px; color: #008000;">/</span><span style="font-size: 13px; color: #008000;">/ 则该属性的值即为根据规范中定义的规则计算出来的读取jar包中web-fragment.xml文件的jar包名顺序，它时一个List&lt;String&gt;类型，包含jar包名字。<br />
</span><font size="2">&nbsp; &nbsp;&nbsp;</font><span style="font-size: 13px; color: #008000;">/</span><span style="font-size: 13px; color: #008000;">/ 在Jetty中使用Ordering来表示这种顺序，它有两个实现类：AbsoluteOrdering和RelativeOrdering用来分别表示在web.xml和web-fragment.xml中定义的absolute-ordering和ordering定义，<br />
</span><font size="2">&nbsp; &nbsp;&nbsp;</font><span style="font-size: 13px; color: #008000;">/</span><span style="font-size: 13px; color: #008000;">/&nbsp;</span><span style="font-size: 13px; color: #008000;">并且将最终的解析结果汇总到Metadata类中，并根据规范中定义的规则以及metadata-complete的定义来计算实际的解析顺序</span><span style="font-size: 13px; color: #008000;">，<br />
</span><font size="2">&nbsp; &nbsp;&nbsp;</font><span style="font-size: 13px; color: #008000;">/</span><span style="font-size: 13px; color: #008000;">/&nbsp;</span><span style="font-size: 13px; color: #008000;">而对这两种配置文件的解析由WebDescriptor和FragmentDescriptor来实现，它们都包含了metadata-complete解析，而真正的解析入口在WebXmlConfiguration和FragmentConfiguration中。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ 该规范的定义参考：</span><span style="color: #008000;">https://blogs.oracle.com/swchan/entry/servlet_3_0_web_fragment</span><span style="color: #008000;"><br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;ORDERED_LIBS&nbsp;=&nbsp;"javax.servlet.context.orderedLibs";<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回当前Web Application的Context Path，即当前Web Application的根路径，Servlet Container根据该路径以及一个Request URL的匹配情况来选择一个Request应该交给那个Web Application处理该请求。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// Context Path以"/"开始，但是不能以"/"结尾，对默认的根Context，它返回""，而不是"/"。该值在配置Jetty的ContextHandler中设置。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 有些Servlet Container支持多个Context Path指向同一个Context，此时可以使用HttpServletRequest中的getContextPath()来获取该Request实际对应的Context Path，此时这两个Context Path的值可能会不同，但是ServletContext中返回的Context Path值是主要的值。另外Jetty也不支持这种特性。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getContextPath();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 通过给定一个Context Path以在当前</span><span style="color: #008000;">Servlet Container中找到其对应的ServletContext实例。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 可以通过该方法获取Servlet Container中定义的另一个Web Application的ServletContext实例，并获得其RequestDispatcher，并将当前请求Dispatch到那个Web Application中做进一步的处理。这里的uripath必须以"/"开头，且其路径相对于当前Server的根路径。出于安全考虑，该方法可能会返回null。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 在Jetty的实现中，这里uripath可以是一个具体的路径，并且支持查找最准确的匹配。如：对uripath为/foo/goo/abc.html，在Server中由以下Context Path定义："/", "/foo", "/foo/goo"，则最终查找到的ServletContext为"/foo/goo"作为Context Path对应的ServletContext实例。</span><br />
&nbsp; &nbsp; <span style="color: #0000FF; ">public</span>&nbsp;ServletContext&nbsp;getContext(String&nbsp;uripath);<br />
&nbsp; &nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/返回Servlet规范的主版本，如3</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getMajorVersion();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 返回Servlet规范的次版本，如0</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getMinorVersion();<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 返回当前Web Application基于的Servlet规范的主版本，如在web.xml文件中定义的version(&lt;web-app version="..." ...&gt;...&lt;/web-app&gt;，即Jetty中的实现)</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getEffectiveMajorVersion();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/&nbsp;</span><span style="color: #008000;">返回当前Web Application基于的Servlet规范的次版本，如在web.xml文件中定义的version(&lt;web-app version="..." ...&gt;...&lt;/web-app&gt;，即Jetty中的实现)</span><br />
&nbsp; &nbsp; <span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getEffectiveMinorVersion();<br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回给定file的MIME type，返回null如果无法计算出其MIME type。这个映射关系由Servlet Container定义或在web.xml文件中定义(mime-mapping, extension, mine-type)。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 常见的MIME type有：text/html, image/gif等。Jetty使用MimeTypes类来封装所有和MIME type相关的操作，MimeTypes类中定义了所有默认支持的MIME type以及编码类型，<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 并且默认从org/eclipse/jetty/http/mime.properties文件中加载默认的映射，如css=text/css, doc=application/msword等，使用addMimeMapping()方法向该类注册web.xml中定义的文件名扩展名到MIME type的映射。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">//&nbsp;</span><span style="color: #008000;">而从org/eclipse/jetty/http/encoding.properties文件中加载MIME type的默认编码类型，如text/xml=UTF-8等。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 在使用文件名查找MIME type时，根据文件名的扩展名查找已注册或默认注册的MIME type。用户自定义的映射优先。用户定义的MIME type映射支持extension为"*"，表示任意扩展名。</span><br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getMimeType(String&nbsp;file);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 对于给定目录，返回该目录下所有的资源以及目录。path必须以"/"开头，如果它不是指向一个目录，则返回空的Set。所有返回的路径都相对当前Web Application的根目录，<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/&nbsp;</span><span style="color: #008000; ">或对于/WEB-INF/lib中jar包中的/META-INF/resources/目录，如一个Web Application包含以下资源：/catalog/offers/music.html, /WEB-INF/web.xml, <br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ 以及</span><span style="color: #008000; ">/WEB-INF/lib/catalog.jar!/META-INF/resources/catalog/moreOffers/books.html，则getResourcePaths("/catalog")返回{"/catalog/offers/", /catalog/moreOffers/"}<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ Jetty的实现中，在MetaInfConfiguration中，它会扫描WEB-INF/lib目录下所有的jar包，如果发现在某个jar包中存在META-INF/resources/目录，<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/&nbsp;</span><span style="color: #008000;">就会将该目录资源作为baseResource在WebInfConfiguration中注册到ContextHandler(WebAppContext)中。从而实现jar包中的META-INF/resources/目录作为根目录的查找。</span>&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Set&lt;String&gt;&nbsp;getResourcePaths(String&nbsp;path);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000;">/ 返回给定path的URL，path必须以"/"开头，它相对当前Web Application的根目录或相对/WEB-INF/lib中jar包中的/META-INF/resources/目录。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/&nbsp;</span><span style="color: #008000;">其中查找顺序前者优先于后者，但是在/WEB-INF/lib/目录下的jar包的查找顺序未定义。该方法不同于Class.getResource()在于它不使用ClassLoader，如果没有找到给定资源，返回null。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ 在WebAppContext实现中，它还支持Alias查找，并且如果其extractWAR的变量为false，给定的资源在WAR包中，则该URL返回WAR包中的URL。</span>&nbsp; &nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;URL&nbsp;getResource(String&nbsp;path)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;MalformedURLException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 参考getResource(path)的查找实现，如果其返回的URL为null，则该方法返回null，否则返回URL对应的InputStream。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;InputStream&nbsp;getResourceAsStream(String&nbsp;path);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 创建一个RequestDispatcher用于将一个Request、Response分发到path对应的URL中，这里path必须以"/"开头，且它相对于当前Context Path。如果无法创建RequestDispatcher，返回null。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">//&nbsp;</span><span style="color: #008000; ">path可以包含query数据用于传递参数：uriInContext?param1=abc&amp;param2=123....该方法可以和getContext(uripath)一起使用，以将当前请求分发到另一个Web Application中。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 该方法的另一种用法是先有一个Servlet或Filter处理基本的逻辑，然后使用这个RequestDispatcher将当前请求forward到另一个URL中或include一个JSP文件</span><span style="color: #008000;">生成响应页面，如果在处理过程中出错，则将其当前请求分发到错误处理的流程中。</span><br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// RequestDispatcher支持两种类型的分发：forward和include，唯一的区别是include只可以改变Response的内容，不可以改变其Header信息，forward则没有这种限制。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;RequestDispatcher&nbsp;getRequestDispatcher(String&nbsp;path);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 创建一个RequestDispatcher用于将一个Request、Response分发到name对应的Servlet(JSP)中。如果没能找到响应的Servlet，返回null。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;RequestDispatcher&nbsp;getNamedDispatcher(String&nbsp;name);&nbsp; &nbsp;&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 将msg打印到Servlet对应的log文件中，在Jetty中，使用INFO级别打印，logger名称为web.xml定义的display-name，或者context path。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/&nbsp;</span><span style="color: #008000; ">Jetty默认使用SLF4J作为日志打印框架，可以使用"org.eclipse.jetty.util.log.class"系统属性改变其日志打印框架。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;log(String&nbsp;msg);&nbsp; &nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 打印message和throwable到Servlet对应的log文件中，在Jetty中使用WARN级别打印该信息。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;log(String&nbsp;message,&nbsp;Throwable&nbsp;throwable);<br />
<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 返回给定path在当前机器上操作系统对应的位置。对/META-INF/resources下的资源，除非他们已经解压到本地目录，否则对那些资源该方法返回null。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ 在Jetty实现中，使用getResource()方法相同的实现获取Resource实例，如果其getFile()返回不为null，则返回该File的canonical路径。</span><br />
&nbsp; &nbsp; <span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getRealPath(String&nbsp;path);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回Servlet Container的名称和版本号，其格式为：&lt;servername&gt;/&lt;versionnumber&gt;，如：Jetty/8.1.9.v20130131。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getServerInfo();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ ServletContext级别的初始参数操作，可以在web.xml中使用context-param定义，也可以手动设置。在get中如果找不到对应的项，返回null，在set时，如果已存在name，则返回false，并且不更新相应的值。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getInitParameter(String&nbsp;name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Enumeration&lt;String&gt;&nbsp;getInitParameterNames();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;setInitParameter(String&nbsp;name,&nbsp;String&nbsp;value);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// ServletContext级别的属性操作，其中属性名遵循包命名规则。在set中，如果object为null表示移除该属性，如果name以存在，则会替换原有的值，如果注册了ServletContextAttributeListener，则会出发相应的attributeRemoved、attributeReplaced、attributeAdded事件。在remove中，如果name存在且被移除了，则会触发attributeRemoved事件。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 在Jetty中使用ContextHandler中的Context内部类实现ServletContext，在ContextHandler中定义了两个相关字段：_attributes以及_contextAttributes，其中_attributes表示在Jetty内部通过ContextHandler设置的属性，而_contextAttributes表示用户设置的属性，但是在获取属性值时，两个字段的属性都会考虑进去，在移除属性时，如果是移除_attributes字段中的值，则不会触发attributeRemoved事件。</span><br />
&nbsp; &nbsp; <span style="color: #0000FF; ">public</span>&nbsp;Object&nbsp;getAttribute(String&nbsp;name);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Enumeration&lt;String&gt;&nbsp;getAttributeNames();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setAttribute(String&nbsp;name,&nbsp;Object&nbsp;object);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;removeAttribute(String&nbsp;name);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回当前Web Application在web.xml中的display-name定义的ServletContext名字，在Jetty实现中，如果该值为null，则返回context path。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getServletContextName();<br />
<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 该部分具体的使用可以参考：</span><span style="color: #008000;">http://www.blogjava.net/yongboy/archive/2010/12/30/346209.html</span><span style="color: #008000;">&nbsp;<br />
</span><br />
&nbsp; &nbsp; <span style="color: #008000; ">// 动态的向ServletContext中注册Servlet，注册的Servlet还可以通过返回的ServletRegistration继续配置，如addMapping、setInitParameter等。<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">//&nbsp;</span><span style="color: #008000; ">在使用className实例话Servlet时，使用当前ServletContext相关联的ClassLoader。在创建Servlet实例时，会根据该类中定义的以下Annotation做相应的配置：<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">//&nbsp;</span><span style="color: #008000;">javax.servlet.annotation.ServletSecurity、</span><span style="color: #008000;">javax.servlet.annotation.MultipartConfig、</span><span style="color: #008000;">javax.annotation.security.RunAs、</span><span style="color: #008000;">javax.annotation.security.DeclareRoles</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ServletRegistration.Dynamic&nbsp;addServlet(String&nbsp;servletName,&nbsp;String&nbsp;className);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ServletRegistration.Dynamic&nbsp;addServlet(String&nbsp;servletName,&nbsp;Servlet&nbsp;servlet);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ServletRegistration.Dynamic&nbsp;addServlet(String&nbsp;servletName,&nbsp;Class&nbsp;&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Servlet&gt;&nbsp;servletClass);<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 创建给定Servlet类的Servlet实例，并且</span><span style="color: #008000;">会根据该类中定义的以下Annotation做相应的配置：<br />
</span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">//&nbsp;</span><span style="color: #008000;">javax.servlet.annotation.ServletSecurity、</span><span style="color: #008000;">javax.servlet.annotation.MultipartConfig、</span><span style="color: #008000;">javax.annotation.security.RunAs、</span><span style="color: #008000;">javax.annotation.security.DeclareRoles</span><br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 在创建Servlet实例后，一般还要调用addServlet()方法将其注册到ServletContext中。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;&lt;T&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Servlet&gt;&nbsp;T&nbsp;createServlet(Class&lt;T&gt;&nbsp;clazz)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 根据servletName获取ServletRegistration实例</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ServletRegistration&nbsp;getServletRegistration(String&nbsp;servletName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">// 获取所有在ServletContext中注册的servletName到ServletRegistration映射的Map。所有动态注册和使用配置注册的映射。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Map&lt;String,&nbsp;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;ServletRegistration&gt;&nbsp;getServletRegistrations();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 动态的向ServletContext中注册Filter，注册的Filter可以通过返回的FilterRegistration进一步配置，如addMappingForUrlPatterns、setInitParameter等</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;FilterRegistration.Dynamic&nbsp;addFilter(String&nbsp;filterName,&nbsp;String&nbsp;className);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;FilterRegistration.Dynamic&nbsp;addFilter(String&nbsp;filterName,&nbsp;Filter&nbsp;filter);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;FilterRegistration.Dynamic&nbsp;addFilter(String&nbsp;filterName,&nbsp;Class&nbsp;&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Filter&gt;&nbsp;filterClass);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 创建给定Filter类实例的Filter实例，一般都会后继调用addFilter将该实例注册到ServletContext中。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;&lt;T&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Filter&gt;&nbsp;T&nbsp;createFilter(Class&lt;T&gt;&nbsp;clazz)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 根据filterName获取FilterRegistration实例。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;FilterRegistration&nbsp;getFilterRegistration(String&nbsp;filterName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000;">/ 返回所有filterName到FilterRegistration映射的Map，包括所有动态注册和使用配置注册的映射。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Map&lt;String,&nbsp;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;FilterRegistration&gt;&nbsp;getFilterRegistrations();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回SessionCookieConfig实例，用于session tracking的cookie属性，多次调用该方法返回相同的实例。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;SessionCookieConfig&nbsp;getSessionCookieConfig();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 设置session tracking模式，可以是URL、COOKIE、SSL。Jetty8只支持URL和COOKIE。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setSessionTrackingModes(Set&lt;SessionTrackingMode&gt;&nbsp;sessionTrackingModes);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回当前ServletContext默认支持的session tracking模式。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Set&lt;SessionTrackingMode&gt;&nbsp;getDefaultSessionTrackingModes();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回当前ServletContext目前使用的session tracking模式。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Set&lt;SessionTrackingMode&gt;&nbsp;getEffectiveSessionTrackingModes();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 向ServletContext动态的注册Listener，该Listener类或实例必须实现以下的一个或多个接口：</span><span style="color: #008000; "><br />
&nbsp; &nbsp; //&nbsp;</span><span style="color: #008000; ">ServletContextAttributeListener、</span><span style="color: #008000; ">ServletRequestListener、</span><span style="color: #008000; ">ServletRequestAttributeListener、</span><span style="color: #008000; ">HttpSessionListener、</span><span style="color: #008000;">HttpSessionAttributeListener}&lt;/tt&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;// 如果这个ServletContext传入ServletContainerInitializer的onStartup方法，那么这个Listener类或实例也可以实现ServletContextListener接口。<br />
&nbsp; &nbsp; // 注：动态注册的ServletContextListener中的contextInitialized方法中不可以调用Servlet 3.0中定义的这些动态注册Servlet、Filter、Listener等方法，不然会抛出UnsupportedOperationException，看起来像是出于一致性、安全性或是兼容性的考虑，但是具体是什么原因一直想不出来。而且在Jetty实现中，它在注册EventListener实例是确取消了这种限制，而对注册EventListener类实例和类名确有这种限制，不知道这是Jetty的bug还是其他什么原因。。。。。</span><span style="color: #008000; "><br />
&nbsp; &nbsp; // 对于调用顺序按定义顺序来的EventListener(如ServletRequestListener、ServletContextListener、HttpSessionListener)，那这个新的EventListener会添加到相应列表末尾。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;addListener(String&nbsp;className);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;&lt;T&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;EventListener&gt;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;addListener(T&nbsp;t);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;addListener(Class&nbsp;&lt;?&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;EventListener&gt;&nbsp;listenerClass);<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000; ">// 创建clazz对应的EventListener实例，一般这个新创建的EventListener会之后注册到ServletContext中。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;&lt;T&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;EventListener&gt;&nbsp;T&nbsp;createListener(Class&lt;T&gt;&nbsp;clazz)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException;&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回web.xml和web-fragment.xml配置文件中定义&lt;jsp-config&gt;的汇总，或返回null如果没有相关配置。看起来像Jetty并没有实现该方法。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;JspConfigDescriptor&nbsp;getJspConfigDescriptor();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 返回当前Web Application对应的ClassLoader实例。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;ClassLoader&nbsp;getClassLoader();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 定义角色名。角色名在ServletRegistration.Dynamic.setServletSecurity()和ServletRegistration.Dynamic.setRunAsRole()中默认定义，因而不需要重新使用这个方法定义。</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;declareRoles(String<img src="http://www.blogjava.net/Images/dot.gif" alt="" />&nbsp;roleNames);<br />
}</div><h2>
<span style="color: #ff6600;">ServletContext初始化
</span></h2>ServletContext的初始化从ContextHandler的doStart()方法开始，在其startContext()方法快结束时，会调用注册的ServletContextListener中的contextInitialized()方法，因而这里是用户对ServletContext初始化时做一些自定义逻辑的扩展点。<br /><br />在Servlet 3.0中还引入了ServletContainerInitializer接口，它定义了onStartup()方法，该方法会在WebAppContext中的startContext方法中的configure方法中通过ContainerInitializerConfiguration.configure()中被调用，该方法的调用要早于所有ServletContextListener
.contextInitialized()事件的触发。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;ServletContainerInitializer&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// 当ServletContext对应的Web Application初始化时，该方法会被调用。其中c参数是所有继承、实现在ServletContainerInitializer实现类定义的HandlesTypes注解中定义的类，<br />&nbsp; &nbsp; // 如果该注解定义的类数组中有注解，那么c参数还包含所有存在这个注解的类。如果实现ServletContainerInitializer接口的类在WEB-INF/lib的jar包中绑定，<br />&nbsp; &nbsp; // 那么该方法只会在对应的Web Application初始化被调用一次，如果实现ServletContainerInitializer接口的类在WEB-INF/lib以外定义，但是还可以被Servlet Container找到，<br />&nbsp; &nbsp; // 那么意味这这个jar包是多个Web Application共享的，因而该方法会在每个Web Application初始化时被调用。如果ServletContainerInitializer实现类没有定义HandlesTypes注解，<br />&nbsp; &nbsp; // 那么c参数为null。ServletContainerInitializer实现类的查找使用运行时service provider机制，然而对于定义在fragment jar包中的ServletContainerInitializer实现类，<br /></span><span style="color: #008000;">&nbsp; &nbsp; // 但是该jar在absolute ordering中被exclude了，那么该jar包会被忽略，即在web.xml中的absolute-ordering中没有包含相应的fragment名。<br /></span>&nbsp;<span style="color: #008000;">&nbsp; &nbsp;// 在查找满足HandlesTypes注解中定义的类数组的类实例集合时，由于有些JAR包是可选的，因而在加载Class时有时会遇到问题，此时Servlet Container可以选择忽略该错误，<br /></span>&nbsp;<span style="color: #008000;">&nbsp; &nbsp;//&nbsp;</span><span style="color: #008000;">但是需要提供配置已让Servlet Container决定是否要将这些错误打印到日志文件中。&nbsp;</span><br />&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;onStartup(Set&lt;Class&lt;?&gt;&gt;&nbsp;c,&nbsp;ServletContext&nbsp;ctx)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;ServletException;&nbsp;<br />}</div>要自定义ServletContainerInitializer逻辑，首先需要META-INF/services/目录下建立一个<span style="background-color: #ffffff; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 20px;">javax.servlet.ServletContainerInitializer文件，在该文件内部写入用户在定义的ServletContainerInitializer类，如：x.y.z.MyServletContainerInitializer，该MyServletContainerInitializer类必须实现ServletContainerInitializer接口，如果它有一些特定感兴趣的类，可以向其定义HandlesTypes注解，该注解的值可以是类实例、接口实例、注解实例，它表示所有继承自注解中定义的类、实现注解中定义的接口、有注解中定义的注解注解的类(注解在类、字段、方法中)都会收集成一个Set&lt;Class&lt;?&gt;&gt;类型，并传入onStartup()方法中，如果没有HandlesTypes注解，其onStartup()中Set&lt;Class&lt;?&gt;&gt;参数为null。<br /><br />在Jetty实现中，AnnotationConfiguration会查找到所有jar包中在META-INF/services/javax.servlet.ServletContainerInitializer中定义的ServletContainerInitializer(排除那些被absolute-ordering排除在外的fragment jar包中的定义)，使用这些查找到的ServletContainerInitializer创建ContainerInitializer实例，使用HandlesTypes注解中定义的Class数组初始化InterestedTypes字段，如果这个Class数组中有注解类型，则将所有存在这个注解类型的类(这个注解可以注解在该类的类、字段、方法中)添加到ContainerInitializer实例中的_annotatedTypeNames集合中(使用ContainerInitializerAnnotationHandler实现)；最后将他们注册到Context的一个属性中。同时在AnnotationConfiguration中还会向Context中注册一个属性，其值是Map，它包含了所有类对应的其子类或实现类。然后在ContainerInitializerConfiguration中，对每个在Context中注册的ContainerInitializer实例，对所有注册的_annotatedTypeNames，将该类以及该类的子类、实现类注册到_applicableTypeNames集合中；对所有注册的非注解类型的_interestedTypes，将其所有的子类、实现类注册到_applicableTypeNames集合中(在Jetty当前版本的实现中没有包含_interestedTypes中的类实例本身，在ServletContainerInitializer的注释中确实也没有说明要包含这些类本身，感觉这个有点不合理。。。)；最后调用ContainerInitializer中的callStartup()方法，它加载_applicableTypeNames集合中的所有类，并将其传入ServletContainerInitializer的onStartup()方法中(这里没有根据规范忽略不能加载成功的类实例)。</span></div><img src ="http://www.blogjava.net/DLevin/aggbug/413395.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-05-11 01:22 <a href="http://www.blogjava.net/DLevin/archive/2014/05/11/413395.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之ScopedHandler</title><link>http://www.blogjava.net/DLevin/archive/2014/05/07/413158.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Wed, 07 May 2014 15:49:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/05/07/413158.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/413158.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/05/07/413158.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/413158.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/413158.html</trackback:ping><description><![CDATA[<h2><span style="color: #ff6600;">概述</span></h2>
在很多框架的设计中，都有类似Channel链的设计，类似Decorator模式或Chain Of Responsibility模式，可以向这个Channel注册不同的Handler，用户请求可以穿越这个由多个Handler组成的Channel，执行响应的切面逻辑，在最后一个Handler或者另一个Processor处理用于自定义的业务逻辑，然后生成的响应可以逆着方向从这个Channel回来。Structs2中的Interceptor、Servlet中Filter都采用这种设计。这种设计为面向切面编程提供了遍历，然而目前的Handler设计更多的像是Chain Of Responsibility模式，它的处理逻辑只能从链头走到链尾，而没有返回的路程。引入ScopedHandler的目的就是用于解决这个问题。<br />
<h2><span style="color: #ff6600;">实现</span></h2>
Structs2中Interceptor实现使用传入ActionInvaction来调用Channel中的后继Interceptor：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;MyInterceptor&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;AbstractInterceptor&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;intercept(ActionInvocation&nbsp;ai)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;Exception&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Add&nbsp;user&nbsp;customized&nbsp;logic&nbsp;here&nbsp;when&nbsp;request&nbsp;come&nbsp;into&nbsp;the&nbsp;channel</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;ai.invoke();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Add&nbsp;user&nbsp;customized&nbsp;logic&nbsp;here&nbsp;when&nbsp;response&nbsp;pass&nbsp;out across the&nbsp;channel</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br />
}</div>
类似的，Servlet中的Filter实现也是使用FilterChain来调用Channel中后继的Filter：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;MyFilter&nbsp;<span style="color: #0000FF; ">implements</span>&nbsp;Filter&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;doFilter(ServletRequest&nbsp;request,&nbsp;ServletResponse&nbsp;response,&nbsp;FilterChain&nbsp;chain)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException,&nbsp;ServletException&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Add&nbsp;user&nbsp;customized&nbsp;logic&nbsp;here&nbsp;when&nbsp;request&nbsp;come&nbsp;into&nbsp;the&nbsp;channel</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;chain.doFilter(request,&nbsp;response);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally</span>&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Add&nbsp;user&nbsp;customized&nbsp;logic&nbsp;here&nbsp;when&nbsp;response&nbsp;pass&nbsp;out&nbsp;across&nbsp;the&nbsp;channel</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</div>
<br />
ScopedHandler采用了不同的实现方式，首先它继承自HandlerWrapper，因而它使用HandlerWrapper中的Handler字段来构建Handler链，然而并不是所有的Handler都是ScopedHandler，因而ScopedHandler内部还定义了_nextScope字段用于创建在这条Handler链表中的ScopedHandler链表，以及_outerScope字段用于将自己始终和这个ScopedHandler链表中的最外层ScopedHandler相连，对这个ScopedHandler的最外层列表，其_outScope字段为null。而ScopedHandler要实现的行为是，假设A、B、C都是ScopedHandler，并且它们组成链表：A-&gt;B-&gt;C，那么当调用A.handle()方法时的调用堆栈是：<br />
A.handle()<br />
|-A.doScope()<br />
|--B.doScope()<br />
|----C.doScope()<br />
|-----A.doHandle()<br />
|------B.doHandle()<br />
|-------C.doHandle()<br />
而如果A、B是ScopedHandler，X、Y是其他的Handler，并且它们组成链表：A-&gt;X-&gt;B-&gt;Y，那么当调用A.handle()方法时的调用栈是：<br />
A.handle()<br />
|-A.doScope()<br />
|--B.doScope()<br />
|---A.doHandle()<br />
|----X.handle()<br />
|-----B.doHandle()<br />
|------Y.handle()<br />
<br />
这种行为主要用于Servlet框架的实现，它可以保证在doScope()方法中做一些初始化工作，并且配置环境，而在后继调用中都可以使用这些配置好的环境，并且doHandle()的顺序还是使用原来定义的Handler链表顺序，即使有些Handler并不是ScopedHandler。<br />
<br />
在实现中，其Handler链表由HandlerWrapper构建，在doStart()方法中计算_nextScope字段以及_outerScope字段，在handle()方法中，如果_outerScope为null，则调用doScope()方法，否则调用doHandle()方法：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp; &nbsp; @Override<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;handle(String&nbsp;target,&nbsp;Request&nbsp;baseRequest,&nbsp;HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException,&nbsp;ServletException<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(_outerScope==<span style="color: #0000FF; ">null</span>)&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;doScope(target,baseRequest,request,&nbsp;response);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;doHandle(target,baseRequest,request,&nbsp;response);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</div>
在执行完doScope()方法后，调用nextScope()方法，该方法顺着_nextScope链表走，直到尽头，后调用doHandle()方法：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;nextScope(String&nbsp;target,&nbsp;Request&nbsp;baseRequest,&nbsp;HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response) <span style="color: #0000FF; ">throws</span>&nbsp;IOException,&nbsp;ServletException<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(_nextScope!=<span style="color: #0000FF; ">null</span>)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_nextScope.doScope(target,baseRequest,request,&nbsp;response);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(_outerScope!=<span style="color: #0000FF; ">null</span>)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_outerScope.doHandle(target,baseRequest,request,&nbsp;response);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;doHandle(target,baseRequest,request,&nbsp;response);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</div>
而doHandle()方法在完成是调用nextHandle()方法，它也沿着_nextScope链表走，只要_nextScope和_handler相同，则调用其doHandle()方法，但是如果_nextScope和_handler不同，则调用_handler中的handle()方法，用于处理在ScopedHandler链表中插入非ScopedHandler的情况：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;nextHandle(String&nbsp;target,&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;Request&nbsp;baseRequest,&nbsp;HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException,&nbsp;ServletException<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(_nextScope!=<span style="color: #0000FF; ">null</span>&nbsp;&amp;&amp;&nbsp;_nextScope==_handler)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_nextScope.doHandle(target,baseRequest,request,&nbsp;response);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(_handler!=<span style="color: #0000FF; ">null</span>)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_handler.handle(target,baseRequest,&nbsp;request,&nbsp;response);<br />&nbsp;&nbsp;&nbsp;&nbsp;}</div><h2><span style="color: #ff6600;">使用</span></h2>在ScopedHandler的测试用例中给出了一个非常好的例子。首先有一个TestHandler继承自ScopedHandler：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;TestHandler&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;ScopedHandler&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;_name;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;TestHandler(String&nbsp;name)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_name=name;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;doScope(String&nbsp;target,&nbsp;Request&nbsp;baseRequest,&nbsp;HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException,&nbsp;ServletException&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try&nbsp;</span>{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_history.append("&gt;S").append(_name);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">super</span>.nextScope(target,baseRequest,request,&nbsp;response);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally&nbsp;</span>{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_history.append("&lt;S").append(_name);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;doHandle(String&nbsp;target,&nbsp;Request&nbsp;baseRequest,&nbsp;HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException,&nbsp;ServletException&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try&nbsp;</span>{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_history.append("&gt;W").append(_name);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">super</span>.nextHandle(target,baseRequest,request,response);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally&nbsp;</span>{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_history.append("&lt;W").append(_name);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}</div>然后有非ScopedHandler的实现：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;OtherHandler&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;HandlerWrapper&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;String&nbsp;_name;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;OtherHandler(String&nbsp;name)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_name=name;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;handle(String&nbsp;target,&nbsp;Request&nbsp;baseRequest,&nbsp;HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException,&nbsp;ServletException&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try&nbsp;</span>{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_history.append("&gt;H").append(_name);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">super</span>.handle(target,baseRequest,request,&nbsp;response);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">finally&nbsp;</span>{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_history.append("&lt;H").append(_name);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}</div>查看一下Test Case的执行结果：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp; @Test<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;testDouble()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;Exception<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestHandler&nbsp;handler0&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;TestHandler("0");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OtherHandler&nbsp;handlerA&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;OtherHandler("A");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestHandler&nbsp;handler1&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;TestHandler("1");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OtherHandler&nbsp;handlerB&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;OtherHandler("B");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handler0.setHandler(handlerA);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handlerA.setHandler(handler1);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handler1.setHandler(handlerB);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handler0.start();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handler0.handle("target",<span style="color: #0000FF; ">null</span>,<span style="color: #0000FF; ">null</span>,<span style="color: #0000FF; ">null</span>);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handler0.stop();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;history=_history.toString();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.err.println(history);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals("&gt;S0&gt;S1&gt;W0&gt;HA&gt;W1&gt;HB&lt;HB&lt;W1&lt;HA&lt;W0&lt;S1&lt;S0",history);<br />&nbsp;&nbsp;&nbsp;&nbsp;}</div><img src ="http://www.blogjava.net/DLevin/aggbug/413158.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-05-07 23:49 <a href="http://www.blogjava.net/DLevin/archive/2014/05/07/413158.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之Handler总述</title><link>http://www.blogjava.net/DLevin/archive/2014/05/06/413129.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Tue, 06 May 2014 14:56:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/05/06/413129.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/413129.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/05/06/413129.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/413129.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/413129.html</trackback:ping><description><![CDATA[<h2><span style="color: #ff6600;">Handler概述</span></h2>Handler是Jetty中的核心接口，它用于处理所有连接以外的逻辑，比如对Servlet框架的实现，以及用户自定义的Handler等，它继承自LifeCycle和Destroyable接口，只有一个主要方法：handle，包含Request和Response实例。在<a href="http://www.blogjava.net/DLevin/archive/2014/03/29/411667.html">深入Jetty源码之Connection</a>中有写道在HttpConnection的handleRequest()方法中会最终调用Server的handle()或handleAsync()方法，并且传入HttpConnection自身作为参数以处理后续逻辑，在这里Server作为Handler的容器，在Server中以HttpConnection为参数的handle()和handleAsync()方法中，会调用向这个Handler容器中注册的所有Handler的handle()方法。即在使用Jetty时，我们首先要向Server注册相应的Handler实例。<br /><h2><span style="color: #ff6600;">Handler接口定义</span></h2>Handler的接口定义如下：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Handler&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;LifeCycle,&nbsp;Destroyable&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;处理一个HTTP请求，并最终将响应写入Response中。不同的实现有不同的功能和逻辑，如WebAppContext实现一个Servlet容器，<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;而ErrorHandler则向Response中写入一个包含错误码和原因的HTML页面。<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;关于参数：<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;target表示Request的目标，它可以时一个URI或一个名字。即Request中的pathInfo字段。<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;baseRequest表示在HttpConnection中创建并解析的最初的Request，它没有被包装。<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;request表示一个HttpServletRequest，它可以同baseRequest相同的实例，也可以是一个经过包装后的HttpServletRequest。<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;response表示一个HttpServletResponse，它可以是经过包装的HttpServletResponse或在HttpConnection创建的最原始的HttpServletResponse。</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;handle(String&nbsp;target,&nbsp;Request&nbsp;baseRequest,&nbsp;HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException,&nbsp;ServletException;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setServer(Server&nbsp;server);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Server&nbsp;getServer();<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;destroy();<br />}</div><h2><span style="color: #ff6600;">Handler类图</span></h2><img src="http://www.blogjava.net/images/blogjava_net/dlevin/Jetty_Handler2.jpg" alt="" height="901" width="1137" /><br /><h2><span style="color: #ff6600;">AbstractHandler</span></h2>AbstractHandler继承自AggregateLifeCycle并实现了Handler接口，是基本上所有Handler的基类。它的实现也非常简单，它定义了Server成员，并实现了getServer和setServer方法，在setServer实现中，如果已存在的Server引用和新设置的Server不同，则先将自身从已存在的Server的Container中移除，然后将自身添加到新的Server的Container中，并更新内部对Server的引用实例。在destroy方法中，也会将自身从Server引用实例的Container中移除。<br /><h2><span style="color: #ff6600;">DefaultHandler</span></h2>DefaultHandler直接继承自AbstractHandler，它可以用于Handler链表的末尾Handler，用于处理所有不能被其他Handler处理的请求：/favicon.ico -&gt; 显示jetty图标，/ -&gt; 显示404错误页面，并列出所有可用的ContextPath，任何其他请求显示404错误页面。图标的显示和可用ContextPath的列表显示都是可配的。<br /><h2><span style="color: #ff6600;">DumpHandler</span></h2>DumpHandler直接继承自AbstractHandler，用于测试和调试，显示Request消息内容。它显示的信息有PathInfo、ContentType、CharacterEncoding、RequestLine、Headers、Parameters、CookieName、Cookies、Attributes、Content等。<br /><h2><span style="color: #ff6600;">ErrorHandler</span></h2>ErrorHandler直接继承自AbstractHandler，用于处理错误页面，使用ContextHandler.setErrorHandler()或Server.addBean()注册。它显示出错代码、原因以及更详细的信息。其中异常栈从Request的javax.servlet.errro.exception中获取。<br /><h2><span style="color: #ff6600;">ErrorPageErrorHandler</span></h2>ErrorPageErrorHandler继承自ErrorHandler，它可以配置不同的Exception和Response Status Code到不同的页面。Exception类型从Request中的javax.servlet.error.exception_type或javax.servlet.error.exception属性中获取，Response Status Code从Request中的javax.servlet.error.status_code属性中获取。使用Exception类型、Response Status Code、Response Status Code Range从_errorPages中查找在web.xml文件中注册的映射，如果有找到，则使用ServletContext中的RequestDispatcher将当前的Request、Response派发的其error处理逻辑；否则使用ErrorHandler中的逻辑。<br /><h2><span style="color: #ff6600;">ResourceHandler</span></h2>ResourceHandler直接继承自AbstractHandler，用于处理静态资源以及If-Modified-Since头。使用PathInfo以及注册的或ContextHandler中的BaseResource作为Base查找Resource，如果找不到并且请求的类型是/jetty-stylesheet.css资源，则查找注册的或默认的stylesheet资源；如果查找到的资源是目录，如果URL不是以"/"结尾，则重定向到"URL/"，否则查找是否有welcome list中配置的页面存在以显示，否则列出文件列表或者显示403 Forbidden页面；对If-Modified-Since请求头，如果资源存在LastModified属性，并且比请求中设置的值要小或相等(以秒为单位)，返回304 Not Modified；根据文件名或PathInfo以及注册的MineTypes信息设置ContentType，设置Content-Length、Cache-Control、Last-Modified等响应头，将Resource内容写入Response中。<br /><br />顺便提及：Resource是Jetty中对静态资源的抽象，其子类有URLResource、FileResource、JarResource、JarFileResource、BadResource等。类似<a href="http://www.blogjava.net/DLevin/archive/2012/12/01/392325.html">Spring中对Resource抽象</a>，不详述。<br /><h2><span style="color: #ff6600;">AbstractHandlerContainer</span></h2>AbstractHandlerContainer继承自AbstractHandler，并实现了HandlerContainer接口。HandlerContainer接口定义如下：<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;HandlerContainer&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;LifeCycle&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;返回当前Handler包含的所有Handler</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Handler[]&nbsp;getHandlers();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;返回当前Handler和其所有子Handler包含的所有Handler</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Handler[]&nbsp;getChildHandlers();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;返回所有当前Handler和其所有子Handler包含的所有Handler中类型为指定类型的Handler</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Handler[]&nbsp;getChildHandlersByClass(Class&lt;?&gt;&nbsp;byclass);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000;">&nbsp;返回第一个所有当前Handler和其所有子Handler包含的所有Handler中类型为指定类型的Handler</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;&lt;T&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Handler&gt;&nbsp;T&nbsp;getChildHandlerByClass(Class&lt;T&gt;&nbsp;byclass);<br />}</div>AbstractHandlerContainer主要实现了两个方法：<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000;">&nbsp;将所有当前handler或其子handler中类型为byClass的Handler添加到list中，并返回该list实例</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">protected</span>&nbsp;Object&nbsp;expandHandler(Handler&nbsp;handler,&nbsp;Object&nbsp;list,&nbsp;Class&lt;Handler&gt;&nbsp;byClass);<br /><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;从root的HandlerContainer中找到handler所在的HandlerContainer实例，并且该HandlerContainer必须属于type类型</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;&lt;T&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;HandlerContainer&gt;&nbsp;T&nbsp;findContainerOf(HandlerContainer&nbsp;root,Class&lt;T&gt;type,&nbsp;Handler&nbsp;handler);</div><h2><span style="color: #ff6600;">HandlerCollection</span></h2>HandlerCollection继承自AbstractHandlerContainer，它使用Handler数组作为容器来存储Handler，并且可已配置是否在启动后还能修改这个容器，以及启动是是否并行启动：<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">final</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;_mutableWhenRunning;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">volatile</span>&nbsp;Handler[]&nbsp;_handlers;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">private</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;_parallelStart=<span style="color: #0000FF; ">false</span>;&nbsp;</div>在handle()方法实现中，遍历数组中所有的Handler，调用其handle()方法。<br />在setHandlers()和setServer()方法实现中，需要生成并分发handler的Relationship发生变化的事件给在Server中的Container中注册的Listener，以及更新相应Handler中对Server的引用。<br /><h2><span style="color: #ff6600;">HandlerList</span></h2>它继承自HandlerCollection，只是重写了handle()方法的逻辑，即在HandlerList中，它遍历整个Handlers
数组直到有异常发生或baseRequest的isHandled()返回true(什么时候baseRequest的handled属性会被设置为true呢？在发送请求之前或相应状态码被设置)；而HandlerCollection则会遍历整个Handlers数组直到一个RuntimeException发生或IOException发生或整个Handler数组遍历完成，如果中途有出现其他异常最后统一抛出。<br /><h2><span style="color: #ff6600;">ContextHandlerCollection</span></h2>ContextHandlerCollection继承自HandlerCollection，使用PathMap存储请求URI到ContextHandler的映射。<br /><h2><span style="color: #ff6600;">HotSwapHandler</span></h2>HotSwapHandler继承自AbstractHandlerContainer，支持动态的替换内部的Handler(即在设置Handler时，如果当前Handler已经启动，则并立即启动新设置的Handler，并停止原来的Handler)。它使用Composite模式，内部只是使用一个Handler来保存一个集合的Handler，因而如果要注册多个Handler，则这个Handler的类型需要HandlerContainer。<br /><h2><span style="color: #ff6600;">HandlerWrapper</span></h2>HandlerWrapper继承自AbstractHandlerContainer，它类似Composite模式，使用一个Handler本身来表达一个HandlerContainer，因而同HotSwapHandler，它只是包含一个Handler字段用与表示、存储Handler集合，并且其实现也和HotSwapHandler类似，所不同的是它不会动态的启动新设置的Handler，即它只是一个Handler的Wrapper。 <br /><h2><span style="color: #ff6600;">ConnectHandler</span></h2>ConnectHandler继承自HandlerWrapper，它实现了一个代理服务器。<br /><h2><span style="color: #ff6600;">DebugHandler</span></h2>DebugHandler继承自HandlerWrapper，用于测试时使用，在logs
目录中yyyy_mm_dd.debug.log文件的形式纪录请求的时间和URL以及响应的时间和响应状态码，并设置线程名为请求URL。<br /><h2><strong style="color: #ff6600;">IPAccessHandler</strong></h2>IPAccessHandler继承自HandlerWrapper，用于添加可以使用或需要组织的IP列表，并返回403 Forbidden响应。<br /><h2><strong style="color: #ff6600;">RequestLogHandler</strong></h2>RequestLogHandler继承自HandlerWrapper，在所有handler处理结束后，将Request和Response使用RequestLog类打印到日志中。<br /><h2><strong style="color: #ff6600;">StatisticsHandler</strong></h2>StatisticsHandler继承自HandlerWrapper，用于纪录一些统计信息，如请求数、请求时间、dispatched数与时间、suspended数、resume数、expire数、1xx到5xx的响应数、响应总的字节数等。<img src ="http://www.blogjava.net/DLevin/aggbug/413129.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-05-06 22:56 <a href="http://www.blogjava.net/DLevin/archive/2014/05/06/413129.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之Connector</title><link>http://www.blogjava.net/DLevin/archive/2014/05/01/413022.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Thu, 01 May 2014 10:40:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/05/01/413022.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/413022.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/05/01/413022.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/413022.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/413022.html</trackback:ping><description><![CDATA[<h2><span style="color: #ff6600;">Connector概述</span></h2>Connector是Jetty中可以直接接受客户端连接的抽象，一个Connector监听Jetty服务器的一个端口，所有客户端的连接请求首先通过该端口，而后由操作系统分配一个新的端口(Socket)与客户端进行数据通信(先握手，然后建立连接，但是使用不同的端口)。不同的Connector实现可以使用不同的底层结构，如Socket Connector、NIO&nbsp;Connector等，也可以使用不同的协议，如Ssl Connector、AJP Connector，从而在不同的Connector中配置不同的EndPoint和Connection。EndPoint用于连接(Socket)的读写数据(参见<a href="http://www.blogjava.net/DLevin/archive/2014/03/29/411666.html">深入Jetty源码之EndPoint</a>)，Connection用于关联Request、Response、EndPoint、Server，并将解析出来的Request、Response传递给在Server中注册的Handler来处理(参见<a href="http://www.blogjava.net/DLevin/archive/2014/03/29/411667.html">深入Jetty源码之Connection</a>)。在Jetty中Connector的有：SocketConnector、SslSocketConnector、Ajp13SocketConnector、BlockingChannelConnector、SelectChannelConnector、SslSelectChannelConnector、LocalConnector、NestedConnector等，其类图如下。<br /><h2><span style="color: #ff6600;">Connector类图</span></h2><img src="http://www.blogjava.net/images/blogjava_net/dlevin/Jetty_Connector.jpg" alt="" height="842" width="1138" /><br /><h2><span style="color: #ff6600;">Connector接口</span></h2>首先Connector实现了LifeCycle接口，在启动Jetty服务器时，会调用其的start方法，用于初始化Connector内部状态，并打开Connector以接受客户端的请求(调用open方法)；而在停止Jetty服务器时会调用其stop方法，以关闭Connector以及内部元件(如Connection等)以及做一些清理工作。因而open、close方法是Connector中用于处理生命周期的方法；对每个Connector都有name字段用于标记该Connector，默认值为：hostname:port；Connector中还有Server的引用，可以从中获取ThreadPool，并作为Handler的容器被使用(在创建HttpConnection时，Server实例作为构造函数参数传入，并在handleRequest()方法中将解析出来的Request、Response传递给Server注册的Handler)；Connector还定义了一些用于配置当前Connector的方法，如Buffer Size、Max Idle Time、Low Resource Max Idle Time，以及一些统计信息，如当前Connector总共处理过的请求数、总共处理过的连接数、当前打开的连接数等信息。<br />Connector的接口定义如下：<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Connector&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;LifeCycle&nbsp;{&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Connector名字，默认值hostname:port</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;getName();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;打开当前Connector</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;open()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;关闭当前Connector</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;close()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;对Server的引用</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setServer(Server&nbsp;server);<br />&nbsp;&nbsp;&nbsp;&nbsp;Server&nbsp;getServer();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;在处理请求消息头时使用的Buffer大小</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getRequestHeaderSize();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setRequestHeaderSize(<span style="color: #0000FF; ">int</span>&nbsp;size);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;在处理响应消息头时使用的Buffer大小</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getResponseHeaderSize();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setResponseHeaderSize(<span style="color: #0000FF; ">int</span>&nbsp;size);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;在处理请求消息内容时使用的Buffer大小</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getRequestBufferSize();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setRequestBufferSize(<span style="color: #0000FF; ">int</span>&nbsp;requestBufferSize);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;在处理响应消息内容使用的Buffer大小</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getResponseBufferSize();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setResponseBufferSize(<span style="color: #0000FF; ">int</span>&nbsp;responseBufferSize);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;在处理请求消息时使用的Buffer工厂</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;Buffers&nbsp;getRequestBuffers();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;在处理响应消息时使用的Buffer工厂</span><span style="color: #008000; "><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;Buffers&nbsp;getResponseBuffers();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">// User Data Constraint的配置可以是None、Integral、Confidential，对这三种值的解释：<br /></span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// None：A value of NONE means that the application does not require any transport guarantees.<br /></span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// Integral：A value of INTEGRAL means that the application requires the data sent between the client and server to be sent in such a way that it can't be changed in transit.<br /></span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// Confidential：A value of CONFIDENTIAL means that the application requires the data to be transmitted in a fashion that prevents other entities from observing the contents of the transmission on.<br /></span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// In most cases, the presence of the INTEGRAL or CONFIDENTIAL flag indicates that the use of SSL is required.</span><span style="color: #008000;">参考：</span><a href="http://docs.jboss.org/jbossas/docs/Server_Configuration_Guide/4/html/J2EE_Declarative_Security_Overview-Web_Content_Security_Constraints.html">这里</a><br />&nbsp; &nbsp;&nbsp;<span style="color: #008000;">// 如果配置了user-data-constraint为Integral或confidential表示所有相应请求都会重定向到使用Integral/Confidential Schema/Port构建的新的URL中。</span><span style="color: #008000;"><br /></span>&nbsp; &nbsp;&nbsp;<span style="color: #0000ff;">int</span>&nbsp;getIntegralPort();<span style="color: #008000;"><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;getIntegralScheme();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isIntegral(Request&nbsp;request);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getConfidentialPort();<br />&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;getConfidentialScheme();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isConfidential(Request&nbsp;request);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 在将HttpConnection交给Server中的Handlers处理前，根据当前Connector，自定义一些EndPoint和Request的配置，如设置EndPoint的MaxIdleTime，Request的timestamp，<br /></span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ 清除SelectChannelEndPoint中的idleTimestamp，检查forward头等。</span><br />&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;customize(EndPoint&nbsp;endpoint,&nbsp;Request&nbsp;request)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 主要用于SelectChannelConnector中，重置SelectChannelEndPoint中的idleTimestamp，即重新计时idle的时间。<br /></span>&nbsp; &nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/&nbsp;</span><span style="color: #008000; ">它在每个Request处理结束，EndPoint还未关闭，并且当前连接属于keep-alive类型的时候被调用。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;persist(EndPoint&nbsp;endpoint)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br /><br />&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ 底层的链接实例，如ServerSocket、ServerSocketChannel等。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;getConnection();<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/是否对"X-Forwarded-For"头进行DNS名字解析</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">boolean</span>&nbsp;getResolveNames();<br />&nbsp; &nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 当前Connector绑定的主机名、端口号等。貌似在Connector的实现中没有一个默认的主机名。<br /></span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/&nbsp;</span><span style="color: #008000; ">由于端口号可以设置为0，表示由操作系统随机的分配一个还没有被使用的端口，因而这里由LocalPort用于存储Connector实际上绑定的端口号；<br /></span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/&nbsp;</span><span style="color: #008000; ">其中-1表示这个Connector还未开启，-2表示Connector已经关闭。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;getHost();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setHost(String&nbsp;hostname);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setPort(<span style="color: #0000FF; ">int</span>&nbsp;port);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getPort();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getLocalPort();<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ Socket的最大空闲时间，以及在资源比较少(如线程池中的任务数比最大可用线程数要多)的情况下的最大空闲时间，当空闲时间超过这个时间后关闭当前连接(Socket)。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getMaxIdleTime();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setMaxIdleTime(<span style="color: #0000FF; ">int</span>&nbsp;ms);<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getLowResourceMaxIdleTime();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setLowResourceMaxIdleTime(<span style="color: #0000FF; ">int</span>&nbsp;ms);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ 是否当前Connector处于LowResources状态，即线程池中的任务数比最大可用线程数要多</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">boolean</span>&nbsp;isLowResources();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/*</span><span style="color: #008000; ">&nbsp;------------------------以下是一些获取和当前Connector相关的统计信息------------------------------------&nbsp;</span><span style="color: #008000; ">*/<br /></span>&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ 打开或关闭统计功能</span><br />&nbsp; &nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span>&nbsp;setStatsOn(<span style="color: #0000ff;">boolean</span>&nbsp;on);<br />&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ 统计功能的开闭状态</span><br />&nbsp; &nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">boolean</span>&nbsp;getStatsOn();<br />&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ 重置统计数据，以及统计开始时间</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">void</span>&nbsp;statsReset();<br />&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ 统计信息的开启时间戳</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">long</span>&nbsp;getStatsOnMs();<br /><br />&nbsp; &nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 当前Connector处理的请求数</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getRequests();<br />&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ 当前Connector接收到过的连接数</span><br />&nbsp; &nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">int</span>&nbsp;getConnections()&nbsp;;<br />&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ 当前Connector所有当前还处于打开状态的连接数</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">int</span>&nbsp;getConnectionsOpen()&nbsp;;<br />&nbsp; &nbsp;&nbsp;<span style="color: #008000;">/</span><span style="color: #008000;">/ 当前Connector历史上同时处于打开状态的最大连接数</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">int</span>&nbsp;getConnectionsOpenMax()&nbsp;;<br />&nbsp; &nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 当前Connector所有连接的持续时间总和</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;getConnectionsDurationTotal();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 当前Connector的最长连接持续时间</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;getConnectionsDurationMax();<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 当前Connector平均连接持续时间</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">double</span>&nbsp;getConnectionsDurationMean()&nbsp;;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 当前Connector所有连接持续时间的标准偏差</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">double</span>&nbsp;getConnectionsDurationStdDev()&nbsp;;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 当前Connector的所有连接的平均请求数</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">double</span>&nbsp;getConnectionsRequestsMean()&nbsp;;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 当前Connector的所有连接的请求数标准偏差</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">double</span>&nbsp;getConnectionsRequestsStdDev()&nbsp;;<br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">/</span><span style="color: #008000; ">/ 当前Connector的所有连接的最大请求数</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getConnectionsRequestsMax();<br />}</div><h2><span style="color: #ff6600;">AbstractConnector实现 </span></h2>Jetty中所有的Connector都继承自AbstractConnector，而它自身继承自HttpBuffers，HttpBuffers包含了Request、Response的Buffer工厂的创建以及相应Size的配置。AbstractConnector还包含了Name、Server、maxIdleTime以及一些统计信息的引用，用于实现Connector接口中的方法，只是一些Get、Set方法，不详述。除了Connector接口相关的配置，AbstractConnector还为定义了两个字段：_acceptQueueSize用于表示ServerSocket
或ServerSocketChannel中最大可等待的请求数，_acceptors用于表示用于调用ServerSocket或ServerSocketChannel中accept方法的线程数，建议这个数字小于或等于可用处理器的2倍。<br /><br />在AbstractConnector中还定义了Acceptor内部类，它实现了Runnable接口，在其run方法实现中，它将自己的Thread实例赋值给AbstractConnector中的_acceptorThread数组中acceptor号对应的bucket，并更新线程名为：&lt;name&gt; Acceptor&lt;index&gt; &lt;Connector.toString&gt;。然后根据配置的_acceptorPriorityOffset设置当前线程的priority。只要当前Connector处于Running状态，并且底层链接实例不为null，不断的调用accept方法()。在退出的finally语句快中清理_acceptorThread数组中相应的Bucket值为null，并将线程原来的Name、Priority设置回来。<br /><br />AbstractConnector实现了doStart()方法，它首先保证Server实例的存在；然后打开当前Connector(调用其open()方法)，并调用父类的doStart方法(这里是HttpBuffers，用于初始化对应的Buffers)；如果没有自定义的ThreadPool，则从Server中获取ThreadPool；最后根据acceptors的值创建_acceptorThread数组，将acceptors个Acceptor实例dispatch给ThreadPool。在doStop方法实现中，它首先调用close方法，然后对非Server中的ThreadPool调用其stop方法，再调用父类的doStop方法清理Buffers的引用，最后遍历_acceptorThread数据，调用每个Thread的interrupt方法。<br /><br />在各个子类的accept方法实现中，他们在获取客户端过来的Socket连接后，都会对该Socket做一些配置，即调用AbstractConnector的configure方法，它首先设置Socket的TCP_NODELAY为true，即禁用Nagle算法(关于禁用的理由可以参考：http://jerrypeng.me/2013/08/mythical-40ms-delay-and-tcp-nodelay/#sec-4-2，简单的，如果该值为false，则TCP的数据包要么达到TCP Segment Size，要么收到一个Ack，才会发送出去，即有Delay)；然后如果设置了_soLingerTime，则开启Socket中SO_LINGER选项，否则，关闭该选项(SO_LINGER选项用于控制关闭一个Socket的行为，如果开启了该选项，则在关闭Socket时会等待_soLingerTime时间，此时如果有数据还未发送完，则会发送这些数据；如果关闭了该选项，则Socket的关闭会立即返回，此时也有可能继续发送未发送完成的数据，具体参考：http://blog.csdn.net/factor2000/article/details/3929816)。<br /><br />在ServerSocket和ServerSocketChannel中还有一个SO_REUSEADDR的配置，一般来说当一个端口被释放后会等待两分钟再被使用，此时如果重启服务器，可能会导致启动时的绑定错误，设置该值可以让端口释放后可以立即被使用(具体参考：http://www.cnblogs.com/mydomain/archive/2011/08/23/2150567.html)。在AbstractConnector中可以使用setReuseAddress方法来配置，默认该值设置为true。<br /><br />AbstractConnector中还实现了customize方法，它在forwarded设置为true的情况下设置相应的attribute：javax.servlet.request.cipher_suite, javax.servlet.request.ssl_session_id，以及Request中对应Host、Server等头信息。这个逻辑具体含义目前还不是很了解。。。。<br /><br />最后关于统计数据的更新方法，AbstractConnector定义了如下方法：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">protected</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;connectionOpened(Connection&nbsp;connection)<br /><span style="color: #0000FF; ">protected</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;connectionUpgraded(Connection&nbsp;oldConnection,&nbsp;Connection&nbsp;newConnection)<br /><span style="color: #0000FF; ">protected</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;connectionClosed(Connection&nbsp;connection)</div><h2><span style="color: #ff6600;">SocketConnector实现</span><span style="color: #ff6600;"><br /></span></h2>有了AbstractConnector的实现，SocketConnector的实现就变的非常简单了，它保存了一个EndPoint的Set，表示所有在这个Connector下正在使用的EndPoint，然后是ServerSocket，在open方法中创建，并在getConnection()方法中返回，还有一个localPort字段，当ServerSocket被创建时从ServerSocket实例中获取，并在getLocalPort()方法中返回。在close方法中关闭ServerSocket，并设置localPort为-2；在accept方法中，调用ServerSocket的accept方法，返回一个Socket，调用configure方法对新创建的Socket做一些基本的配置，然后使用该Socket创建ConnectorEndPoint，并调用其dispatch方法；在customize方法中，在调用AbstractConnector的customize方法的同时还设置ConnectorEndPoint的MaxIdleTime，即设置Socket的SO_TIMEOUT选项，用于配置该Socket的空闲可等待时间；在doStart中会先清理ConnectorEndPoint的集合，而在doStop中会关闭所有还处于打开状态的ConnectorEndPoint。<br /><h2><span style="color: #ff6600;">SelectChannelConnector实现</span></h2>SelectChannelConnector内部使用ServerSocketChannel，在open方法中创建ServerSocketChannel，配置其为非blocking模式，并设置localPort值；在accept方法中调用ServerSocket的accept方法获得一个SocketChannel，配置该Channel为非blocking模式，调用AbstractChannel的configure方法做相应Socket配置，最后将该SocketChannel注册给ConnectorSelectManager；在doStart方法中，它会初始化ConnectorSelectManager的SelectSets值为acceptors值、MaxIdleTime、LowResourceConnections、LowResourcesMaxIdleTime等值，并启动该Manager，并dispatch acceptors个线程，不断的调用Manager的doSelect方法；在close方法中会先stop ConnectorSelectManager，然后关闭ServerSocketChannel，设置localPort为-2；在customize方法中会清除SelectChannelEndPoint的idleTimestamp，重置其MaxIdleTime以及重置Request中的timestamp的值；在persist方法中会重置SelectChannelEndPoint中idleTimestamp的值。<br /><h2><span style="color: #ff6600;">BlockingChannelConnector实现</span></h2>BlockingChannelConnector实现类似SocketConnector，不同的是它使用ServerSocketChannel，并且其EndPoint为BlockingChannelEndPoint。所不同的是它需要在doStart方法中启动一个线程不断的检查所有还在connections集合中的BlockingChannelEndPoint是否已经超时，每400ms检查一次，如果超时则关闭该EndPoint。<br /><br />其他的Connector的实现都比较类似，而SSL相关的Connector需要也只是加入了SSL相关的逻辑，这里不再赘述。<img src ="http://www.blogjava.net/DLevin/aggbug/413022.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-05-01 18:40 <a href="http://www.blogjava.net/DLevin/archive/2014/05/01/413022.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之HttpGenerator</title><link>http://www.blogjava.net/DLevin/archive/2014/04/20/412699.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sun, 20 Apr 2014 07:58:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/04/20/412699.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/412699.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/04/20/412699.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/412699.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/412699.html</trackback:ping><description><![CDATA[基于抽象和聚合原则，Jetty中需要一个单独的类来专门处理HTTP响应消息和请求消息的生成和发送，Jetty的作者将该抽象（接口）命名为Generator，它有两个实现类：HttpGenerator和NestedGenerator。其类图如下：<br /><img src="http://www.blogjava.net/images/blogjava_net/dlevin/HttpGenerator_ClassDiagram.jpg" alt="" height="497" width="1295" /><br />
<h2><span style="color: #ff9900;">Generator接口</span></h2>
HTTP请求消息分为请求行、消息报头、请求正文三部分，HTTP响应消息分为状态行、消息报头、消息正文。在HTTP请求消息和响应消息格式的唯一区别是请求行和状态行，因而只需要将这一行的内容区分开来，其他的可以共享逻辑。<br />
<br />
<p><strong style="color: #ff9900;">请求行和响应行设置</strong><strong><br />
</strong>请求行包括请求方法、URI、HTTP协议版本，响应行包括HTTP协议版本、状态码、状态短语，因而在Generator接口中定义了各自的设置方法：</p>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->&nbsp; &nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;设置_method、_uri字段，如果当前_version是HTTP/0.9，则_noContent置为true</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setRequest(String&nbsp;method,&nbsp;String&nbsp;uri);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;设置_status、_reason字段，对_reason字段，将所有'\r',&nbsp;'\n'替换成空格('&nbsp;')。清除_method字段。如果Generator已经开始生成效应消息，则不可再调用该方法。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setResponse(<span style="color: #0000FF; ">int</span>&nbsp;status,&nbsp;String&nbsp;reason);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;设置HTTP协议版本，这里的值9表示HTTP/0.9,&nbsp;10表示HTTP/1.0，11表示HTTP/1.1。对HTTP/0.9的请求消息，不能包含请求内容。如果Generator已经开始生成效应消息，则不可再调用该方法。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setVersion(<span style="color: #0000FF; ">int</span>&nbsp;version);</div>
<br />
<strong style="color: #ff9900;">HTTP消息报头设置</strong><strong><br />
</strong>在Generator中，通过completeHeader中的HttpFields参数传入HTTP消息报头，而其中的allContentAdded参数用于检查并设置_last字段状态。<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008000;">//</span><span style="color: #008000; ">通过传入的HttpFields参数设置HTTP消息报头，allContentAdded参数用于检查并设置_last字段状态。</span><span style="color: #008000; "><br />
</span><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;completeHeader(HttpFields&nbsp;fields,&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;allContentAdded)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException<br />
<span style="color: #0000FF; ">void</span>&nbsp;setDate(Buffer&nbsp;timeStampBuffer);<br />
<span style="color: #0000FF; ">void</span>&nbsp;setSendServerVersion(<span style="color: #0000FF; ">boolean</span>&nbsp;sendServerVersion);<br />
<span style="color: #0000FF; ">void</span>&nbsp;setContentLength(<span style="color: #0000FF; ">long</span>&nbsp;length);<br />
<span style="color: #0000FF; ">void</span>&nbsp;setPersistent(<span style="color: #0000FF; ">boolean</span>&nbsp;persistent);</div>
<span style="background-color: #eeeeee;"></span>在该方法的实现中：<br />
1. 向_header缓存中写入请求行或响应行（对HTTP/0.9的特殊处理不详述）。对HTTP响应消息，如果状态码是[100-200)、204、304，这些响应消息不能有响应消息体。对状态码是100的响应消息还不能有其他消息头。<br />
2. 如果设置了_date值(状态码在200及以上，这里貌似木有考虑请求消息)，在消息头中包含"Date"消息头。<br />
3. 对fields参数中的所有消息头，依次写入到_header缓存中(消息头名移除'\r', '\n', ':'字符，消息头值移除'\r', '\n'字符)。对"Content-Length"头，记录_contentLength字段；对"Content-Type"为"multipart/byteranges"头，设置_contentLength为SLEF_DEFINING_CONTENT；对"Transfer-Encoding"头，并且_contentLength的值为CHUNKED_CONTENT，则添加"Transfer-Encoding: chunked\r\n"头(或用户自定义的以"chunked"开头的值)；对"Server"头，且设置了"sendServerVersion"字段，则添加"Server"头；对"Connection"头，在请求消息中直接设置该头，同时更新"keep_alive"的值("close"-&gt;keep_alive=false, "keep-alive"-&gt;keep_alive=true)，在响应消息中，更新"keep_alive"和"_persistent"的值，对"upgrade"值，直接添加，而对其他值，根据"keep_alive"和"_persistent"的值以及HTTP版本号添加"close"或"keep-alive"的值，以及用户自定义的值；根据当前_contentLength值设置"Content-Length"头；最后添加"\r\n"到_header缓存表示消息头结束。<br />
4. 将_state状态从STATE_HEADER更新到STATE_CONTENT。<br />
<br />
<strong style="color: #ff9900;">HTTP消息体设置</strong><br />
Generator中有两个方法用于向其添加HTTP消息体：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">void</span>&nbsp;addContent(Buffer&nbsp;content,&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;last)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
<span style="color: #0000FF; ">boolean</span>&nbsp;addContent(<span style="color: #0000FF; ">byte</span>&nbsp;b)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;</div>
这两个方法的实现：<br />
1. 如果当前已存在未刷新的_content内容或者_contentLength为CHUNKED_CONTENT，则先刷新缓存。<br />
2. 更新_content字段和_contentWritten字段。<br />
3. 将_content的值写入_buffer字段中。<br />
在刷新缓存时：<br />
1. 准备Buffer：将_content值写入_buffer中，并清除_content引用；如果_contentLength为CHUNKED_CONTENT，设置_bufferChunk为true，并且对chunked内容，先写入16进制的size，紧跟"\r\n"，然后是正真的内容；对最后一个chunk，添加"\r\n\r\n"。<br />
2. 然后根据_header, _buffer, _content状态，将它们中的内容写入到Endpoint中。<br />
3. 如果当前状态是STATE_FLUSHING，则将_state状态置为STATE_END。<br />
<br />
<strong style="color: #ff9900;">HTTP消息完成生成</strong><br />
Generator调用complete方法表示生成已经完成：<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;complete()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException</div>
在该方法中，它将_state状态设置为STATE_FLUSHING，并刷新缓存。<br />
<br />
<strong style="color: #ff9900;">Generator中的其他方法</strong><br />
在Generator/HttpGenerator中还有一些发送响应消息的方法：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">void</span>&nbsp;sendError(<span style="color: #0000FF; ">int</span>&nbsp;code,&nbsp;String&nbsp;reason,&nbsp;String&nbsp;content,&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;close)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;send1xx(<span style="color: #0000FF; ">int</span>&nbsp;code)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException<br />
<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;sendResponse(Buffer&nbsp;response)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException</div>
其中sendError是Generator接口中的方法，它是一个工具方法用于一次将一个HTTP响应消息写入到Endpoint中：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->&nbsp; &nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;sendError(<span style="color: #0000FF; ">int</span>&nbsp;code,&nbsp;String&nbsp;reason,&nbsp;String&nbsp;content,&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;close)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(close)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_persistent=<span style="color: #0000FF; ">false</span>;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(!isCommitted())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setResponse(code,&nbsp;reason);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(content&nbsp;!=&nbsp;<span style="color: #0000FF; ">null</span>) &nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;completeHeader(<span style="color: #0000FF; ">null</span>,&nbsp;<span style="color: #0000FF; ">false</span>);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addContent(<span style="color: #0000FF; ">new</span>&nbsp;View(<span style="color: #0000FF; ">new</span>&nbsp;ByteArrayBuffer(content)),&nbsp;Generator.LAST);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #0000FF; ">else&nbsp;</span>{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;completeHeader(<span style="color: #0000FF; ">null</span>,&nbsp;<span style="color: #0000FF; ">true</span>);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;complete();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}</div>sendResponse方法是HttpGenerator中的方法，它将response参数直接作为响应消息体，并设置_state为STATE_FLUSHING，是一个工具方法。<br />send1xx方法是HttpGenerator中的方法，它将1xx的响应消息直接写入到Endpoint中。<img src ="http://www.blogjava.net/DLevin/aggbug/412699.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-04-20 15:58 <a href="http://www.blogjava.net/DLevin/archive/2014/04/20/412699.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之HttpParser</title><link>http://www.blogjava.net/DLevin/archive/2014/04/19/411673.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sat, 19 Apr 2014 10:31:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/04/19/411673.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/411673.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/04/19/411673.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/411673.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/411673.html</trackback:ping><description><![CDATA[<h2><span style="color: #ff6600;">概述</span></h2>
Jetty作为HTTP服务器，服务器和客户端以HTTP协议格式通信，Jetty使用Parser（HttpParser）来抽象HTTP请求消息和响应消息的解析类引擎。在HttpParser实现中，它采用有限状态机算法：定义了21中状态，每解析一个字符，就根据当前的状态做相应的处理，并决定是否要迁移到下一个状态，直到HTTP请求消息或响应消息解析完成。HttpParser采用事件驱动机制，它定义了EventHandler类，用户可以通过注册的EventHandler实例获取相应的消息：请求行解析完成(startRequest)、响应行解析完成(startResponse)、每个消息头解析完成(parsedHeader)、所有消息头解析完成(headerComplete)、消息内容解析完成(content)、整个消息(请求消息或响应消息)解析完成(messageComplete)。<br />
<h2><span style="color: #ff6600;">Parser接口定义</span></h2>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Parser&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000;">&nbsp;重置Parser的内部状态，以重用Parser实例，如果returnBuffers为true，则将内部Buffer回收。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;reset(<span style="color: #0000FF; ">boolean</span>&nbsp;returnBuffers);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;当前Parser是否已经解析完成。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isComplete();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;当前Parser是否处于Idle状态，它还处于初始状态，解析还没有开始。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isIdle();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;内部Buffer是否还有内容没有解析。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isMoreInBuffer()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;开始解析已接收到的消息，返回-1表示解析到流的末位，0表示没有该次调用没有解析任何消息，&gt;0表示这次调用总共解析过的字节数。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;parseAvailable()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
}</div>
<h2><span style="color: #ff6600;">EventHandler定义</span></h2>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;EventHandler&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;消息内容解析完成</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;content(Buffer&nbsp;ref)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;所有消息头解析完成</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;headerComplete()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;整个消息(请求消息或响应消息)解析完成</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;messageComplete(<span style="color: #0000FF; ">long</span>&nbsp;contentLength)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;每个消息头解析完成</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;parsedHeader(Buffer&nbsp;name,&nbsp;Buffer&nbsp;value)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;请求行解析完成</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;startRequest(Buffer&nbsp;method,&nbsp;Buffer&nbsp;url,&nbsp;Buffer&nbsp;version)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;响应行解析完成</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">abstract</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;startResponse(Buffer&nbsp;version,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;status,&nbsp;Buffer&nbsp;reason)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
}</div><div><h4><strong>EventHandler的实现类</strong></h4>在HttpConnection的内部类RequestHandler类实现了HttpParser.EventHandler类，以作为HttpParser使用时的回调。<br /><strong>startRequest：</strong>重置当前HttpConnection状态，HttpRequest的时间戳，设置新解析出来的RequestMethod、URI、version信息。</div><strong>parsedHeader：</strong>将每个HTTP头(name, value)对添加到_requestFields字段中，并检查某些头的存在性以及其值的合法性。<br />1. 如果&#8220;host&#8221;头存在，则设置_host为true。<br />2. 对&#8220;Expect&#8221;头，如果其值是&#8220;100-continue&#8221;，设置_expect100Continue为true，若值是&#8220;102-processing&#8221;，设置_expect102Processing值为true，当信息不足时，设置_expect为true。<br />3. 对&#8220;Accept-Encoding&#8221;和&#8220;User-Agent&#8221;头，只能是预定义的值。<br />4. 对&#8220;Content-Type&#8221;头只能是预定义的值，并且根据该值设置_charset字段。<br />5. 对&#8220;Connectin&#8221;头，如果是&#8220;close&#8221;值，则设置HttpGenerator的persistent属性为false，并且设置_responseFields的&#8220;Connection&#8221;值为&#8220;close&#8221;，否则为&#8220;keep-alive&#8221;。<br /><strong>headerComplete：</strong>在HTTP消息头解析结束后，对AsyncEndpoint，调用其scheduleIdle()方法，设置HttpGenerator中的HTTP version字段，以及当前请求是否为HEAD请求，如果当前Server配置了sendDateHeader，则设置HttpGenerator的Date字段为HttpRequest的时间戳(在startRequest方法调用是设置)。对HTTP/1.1，如果没有设置Host头，直接返回400响应(调用_generator的completeHeader和complete方法)；如果expect为true，表示Expect头设置有问题，直接返回417响应(调用_generator的completeHeader和complete方法)。设置_charset字段，对CHUNK请求立即开始处理请求(handleRequest)，否则延迟到消息读取完成。<br /><strong>content：</strong>对AsyncEndPoint，调用其scheduleIdle()方法，如果请求还未开始处理，则立即开始处理请求。<br /><strong>messageComplete：</strong>如果请求还未开始处理，则立即开始处理请求。<br /><br /><strong style="color: red;">注：</strong>这里并没有在content方法中保存消息体的内容，在Jetty中使用HttpInput类从HttpParser中直接读取消息体的内容(通过HttpInput的read方法调用HttpParser.blockForContent()方法)。<br /><h2><span style="color: #ff6600;">HttpPaser有限状态机实现</span></h2>
在HttpParser中定义了21中状态，其中STATE_END以前的状态用于解析HTTP头消息，而STATE_END以后的状态用于解析HTTP消息体。它们各自的状态迁移图如下。<br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/HttpParser_HeaderState.jpg" alt="" height="969" width="1218" /><br />
<div style="text-align: center;"><strong>HttpParser在解析HTTP消息头时的状态迁移图</strong></div>
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/HttpParser_ContentState.jpg" alt="" height="575" width="946" /><br />
<div style="text-align: center;"><strong>HttpParser在解析HTTP消息体时的状态迁移图</strong></div>
<h2><span style="color: #ff9900;">HttpParser在HttpConnection类中的使用</span></h2>
HttpParser在HttpConnection中的handle方法被调用时用于解析客户端过来的HTTP请求消息。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #0000FF; ">if</span>&nbsp;(!_parser.isComplete())&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;parsed=_parser.parseAvailable();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>&nbsp;(parsed&gt;0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;progress=<span style="color: #0000FF; ">true</span>;<br />}</div><img src ="http://www.blogjava.net/DLevin/aggbug/411673.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-04-19 18:31 <a href="http://www.blogjava.net/DLevin/archive/2014/04/19/411673.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之Connection</title><link>http://www.blogjava.net/DLevin/archive/2014/03/29/411667.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sat, 29 Mar 2014 06:42:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/03/29/411667.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/411667.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/03/29/411667.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/411667.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/411667.html</trackback:ping><description><![CDATA[<h2><span style="color: #ff6600;">概述</span></h2>
当Jetty中的Connector收到一个客户端的连接时(ServerSocket或ServerSocketChannel的accept()方法返回)，Connector会首先创建一个ConnectedEndPoint用于和连接的底层(Socket、Channel)打交道(读写数据)，在创建的ConnectedEndPoint时会同时使用该EndPoint创建相应类型的Connection，然后会创建一个Task仍给线程池，最终线程池会启动一个线程启动这个Task，而在这个Task中调用Connection中的handle()方法，以处理当前的连接请求，在这个Task中，它会持续的调用Connection中的handle()方法直到连接关闭或Connector停止。在ConnectorEndPoint中，它自己就实现了Runnable接口，因而可以将它自己丢给线程池，而在SelectChannelEndPoint中则交给SelectorManager来管理客户端连接过来的Channel，并调用Connection的handle方法。
<br />
<h2><span style="color: #ff6600;">Connection接口定义</span></h2>
Connection的定义如下：
<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Connection&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Connection中的核心逻辑，在HttpConnection中，它使用HttpParser解析请求数据，HttpParser采用事件相应机制，可以通过注册HttpParser.EventHandler(RequestHandler)填充HttpConnection中的需要从请求消息中获取的信息，最后在messageComplete()事件相应方法中调用handleRequest()方法将解析后的Request请求交由Server实例的handle()方法处理。在Jetty中，Server是HandlerWrapper子类，它存储了所有注册的Handler，从而将最终的处理流程传导给所有注册的Handler。在所有注册的Handler处理完成后，Connection中的handle()方法会继续执行，使用HttpGenerator、NestedGenerator将缓存的数据刷新到EndPoint中。如果当前请求的相应状态是101(Switching&nbsp;Protocols)，则handle方法返回的Connection实例是从注册的以"org.eclipse.jetty.io.Connection"为key的实例，也正是因为这个相应状态码的存在，这个handle方法的返回值是一个Connection。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;Connection&nbsp;handle()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;除了handle方法，Connection中还有一些提供了一些包含Connection状态的方法：<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;返回Connection创建的时间戳。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">long</span>&nbsp;getTimeStamp();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;当前Connection是否处于Idle状态，如HttpParser、HttpGenerator都处于Idle状态。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isIdle();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;当前Connection是否处于Suspended状态，用于Continuation机制。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isSuspended();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;当Connection关闭时会调用这个方法。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;closed();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;当连接的Idle时间超时后调用该方法，在HttpConnection中，该方法会关闭EndPoiont。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;idleExpired();<br />
}</div>
<h2>
<span style="color: #ff6600;">Connection类图</span></h2>
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/Jetty-Connection.jpg" alt="" height="397" width="685" /><br />
<br />
<h2><span style="color: #ff6600;">HttpConnection实现</span></h2>
HttpConnection是Jetty中对Connection的主要实现，它表示Http客户端和服务器的一次连接，用于将Request、Response、EndPoint联系在一起。同时HttpConnection也是在避免使用pooling的方式下重用Request、Response、HttpParser、HttpGenerator、HttpFields(requestFields、responseFields)、Buffer、HttpURI等（因为Jetty保证了每一次连接只创建一个HttpConnection实例，这是一个可以学习的点，不用pooling方式的重用，以进一步提升性能）。另外，HttpConnection还有对Connector和Server实例的引用，并且用request字段记录了该Connection总共处理的请求数（在headerComplete回调函数中自增）。<br />
<br />
如果请求包含Expect头，并且其值是100-continue，表示客户端希望在请求被正真处理前发送一个响应以表示是否能处理该请求，因而在第一次调用getInputStream时表示服务器已经准备好开始处理请求消息体了，此时在返回ServletInputStream之前，服务器要发送100 Continue响应消息给客户端（通过调用HttpGenerator中的send1xx()方法）。在Jetty中，HttpInput类继承自ServletInputStream，它从HttpParser中读取请求消息体数据。<br />
如果请求头包含Expect头，并且它的值是102-processing，此时服务器可能会发送102状态码的响应，表示请求正在被处理，之后会发送最终的响应。在Jetty中，可以通过Response中的sendError()方法，传入102的状态码以发送102状态码的响应（使用HttpGenerator中的send1xx()方法）。<br />
<br />
handle()方法是HttpConnection中的核心方法，在每一个连接到来时，Connector会创建一个Runnable实例，将该Runnable实例扔到线程池中，在该Runnable的run()方法实现中不断的调用Connection的handle()方法直到当前连接或Connector关闭。在该方法的实现中：<br />
<ol style="margin: 1px;">
     <li>它首先设置_handling字段，表示当前正在处理，并且将当前HttpConnection实例设置到__currentConnection的ThreadLocal变量中。<br />
     </li>
     <li>循环处理请求消息直到EndPoint关闭或者在more_in_buffer为true（初始值为true）。</li>
     <li>如果当前Request处于Async状态，并且还Async状态还没有结束，直接调用handleRequest()方法，如果Async状态结束了，但是HttpParser还没有结束，则继续使用HttpParser解析，在解析过程中，可能会在headerComplete()、content()、messageComplete()回调函数中调用handleRequest()方法（具体参见<a href="http://www.blogjava.net/DLevin/archive/2014/04/19/411673.html">HttpParser的实现</a>）；而后在HttpGenerator已经Commit(所有的响应头已经准备好，并已经写入到EndPoint中)，但是还没有完成的情况下，将HttpGenerator中的数据Flush到EndPoint中；此时如果EndPoint还存在输出缓存，则将其Flush到底层链路中。</li>
     <li>如果当前Request没有处于Async状态，如果HttpPaser还没有结束，使用HttpParser解析，在解析过程中，可能会在headerComplete()、content()、messageComplete()回调函数中调用handleRequest()方法（具体参见<a href="http://www.blogjava.net/DLevin/archive/2014/04/19/411673.html">HttpParser的实现</a>）；而后在HttpGenerator已经Commit(所有的响应头已经准备好，并已经写入到EndPoint中)，但是还没有完成的情况下，持续的将HttpGenerator中的数据Flush到EndPoint中，如果EndPoint还存在输出缓存，则将其Flush到底层链路中；如果HttpGenerator已经处于完成状态，但是EndPoint中还有输出缓存数据，此时将这些数据Flush到底层链路，如果写完缓存中的数据，将progress设置为true，表示handle方法需要继续处理。</li>
     <li>在这些过成中如果出现任何HttpException，则使用HttpGenerator发送错误响应码给客户端(使用sendError()方法，并关闭EndPoint)。</li>
     <li>如果HttpParser中还有数据未处理或者EndPoint中还有输入数据未处理，则循环继续。<br />
     </li>
     <li>如果此时HttpParser已经处理完成，HttpGenerator已经处理完成，并且EndPoint中的输出缓存中已经没有任何数据：1. 如果响应状态码时101 Switching Protocols，且在Request存在org.eclipse.jetty.io.Connection的Connection实例，则新的Connection从Request的该Attribute中获取，并重置HttpParser和HttpGenerator；2. 如果Request不存在该Attribute的Connection，HttpGenerator非persisent状态或EndPoint的InputStream已经关闭，则重置HttpParser，关闭EndPoint，设置more_in_buffer为false，重置当前HttpConnection。</li>
     <li>如果HttpParser处于idle状态，并且EndPoint的InputStream已经关闭，则关闭当前EndPoint，并设置more_in_buffer为false。</li>
     <li>如果Request的Async处于启动状态，则设置more_in_buffer为false。</li>
     <li>如果EndPoint是AsyncEndPoint，Generator已经Commit，但是还未Complete，则该EndPoint schedule一个write操作。</li>
     <li>最后，清理_handle字段和__currentConnection的ThreadLocal字段。</li>
</ol>
<br />
handleRequest()是HttpConnection在对请求消息头解析完成后执行的真正处理逻辑方法：<br />
<ol style="margin: 1px;">
     <li>对任何Request还没有处理完成，并且Server不为null且处于Running状态，循环处理。</li>
     <li>设置Request的handled为false，以及PathInfo字段（如果pathInfo为null，又不是Connect请求，则为400 Error）。</li>
     <li>如果_out字段不为null，reopen it。<br />
     </li>
     <li>如果Request处于initial状态，设置Request的DispatcherType为REQUEST，使用当前的EndPoint和Request实例配置Connector，并调用使用当前HttpConnection作为参数调用Server的handle()方法；否则，设置Request的DispatcherType为ASYNC，调用Server的handleAsync()方法(传入当前HttpConnection做为参数)。</li>
     <li>对任何非ContinuationThrowable异常，设置Request的handled为true，error为true，对HttpException使用Response发送响应状态码给客户端，而对Throwable，使用HttpGenerator发送400或500状态码给客户端。</li>
     <li>如果此时Request处于为完成状态，调用AsyncContinuation.doComplete()方法；如果100 Continue响应没有发送给客户端，则清除该状态，但是如果此时Response还没有Commit，则设置HttpGenerator的persistent为false，表示客户端并没有发送数据过来，我们可以关闭该连接了；如果EndPoint关闭了，则调用Response的complete方法；如果EndPoint没有关闭并且有error，直接关闭EndPoint；如果EndPoint没有关闭，也没有error，但是HttpGenerator没有Commit，Request也没有被handle，则使用resonse发送404 Resource Not Found响应消息，之后调用Response的complete方法；最后设置Request的handled为true。</li>
</ol>
<br />
commitResponse()方法，用于控制HttpGenerator的执行流程：<br />
<ol style="margin: 1px;">
     <li>在HttpGenerator还没有Commit之前（即响应状态行和响应消息头还没写入到EndPoint中）时，先调用HttpGenerator的setResponse()方法设置状态行。</li>
     <li>然后调用HttpGenerator的completeHeader()方法将响应消息头写入到EndPoint中。</li>
     <li>最后调用HttpGenerator的complete方法，不断的将HttpGenerator中的缓存写入到EndPoint中。</li>
</ol><br />flushResponse()方法只是调用了commitResponse方法。
<br /><br />HttpOutput时Jetty中继承自ServletOutputStream的类，它使用AbstractorGenerator向底层EndPoint中写入数据。<br /><br />Output时HttpConnection中的内部类，它继承自HttpOutput，它在调用close/flush时会先调用commitResponse/flushReponse方法，保证响应消息先写状态行，然后是响应消息头，最后才是响应消息体。该类还实现了sendContent方法，其参数可以是HttpContent类型或Resource类型，该方法是一个Util方法，它会自动设置Content-Type、Content-Length、Last-Modified等头，并将HttpContent或Resource对应的数据写入到EndPoint中。<img src ="http://www.blogjava.net/DLevin/aggbug/411667.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-03-29 14:42 <a href="http://www.blogjava.net/DLevin/archive/2014/03/29/411667.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之EndPoint</title><link>http://www.blogjava.net/DLevin/archive/2014/03/29/411666.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sat, 29 Mar 2014 06:34:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/03/29/411666.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/411666.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/03/29/411666.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/411666.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/411666.html</trackback:ping><description><![CDATA[<h2><span style="color: #ff6600;">概述</span></h2>
在Jetty中，使用Connector来抽象Jetty服务器对某个端口的监听。在Connector启动时，它会启动acceptors个Acceptor线程用于监听在Connector中配置的端口。对于客户端的每次连接，Connector都会创建相应的EndPoint来表示该连接，一般在创建EndPoint的同时会同时创建Connection，这里EndPoint用于和Socket打交道，而Connection用于在从Socket中读取到数据后的处理逻辑以及生成响应数据的处理逻辑。<br />
<br />
不同的Connector会创建不同的EndPoint和Connection实例。如SocketConnector创建ConnectorEndPoint和HttpConnection，SslSocketConnector创建SslConnectorEndPoint和HttpConnection，SelectChannelConnector创建SelectChannelEndPoint和SelectChannelHttpConnection，SslSelectChannelConnector创建SslSelectChannelEndPoint和SelectChannelHttpConnection，BlockingChannelConnector创建BlockingChannelEndPoint和HttpConnection等。<br />
<br />
<h2><span style="color: #ff6600;">EndPoint接口定义<br />
</span></h2>
Jetty中EndPoint接口定义如下：
<br />
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;EndPoint&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;EndPoint是对一次客户端到服务器连接的抽象，每一个新的连接都会创建一个新的EndPoint，并且在这个EndPoint中包含这次连接的Socket。由于EndPoint包含底层的连接Socket，因而它主要用于处理从Socket中读取数据和向Socket中写入数据，即对应EndPoint接口中的fill和flush方法。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;从Socket中读取数据，并写入Buffer中直到数据读取完成或putIndex到Buffer的capacity。返回总共读取的字节数。在实现中，StreamEndPoint使用Buffer直接从Socket的InputStream中读取数据，而ChannelEndPoint则向Channel读取数据到Buffer。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;fill(Buffer&nbsp;buffer)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;将Buffer中的数据（从getIndex到putIndex的数据）写入到Socket中，同时清除缓存（调用Buffer的clear方法）。在实现中，StreamEndPoint使用Buffer直接向Socket的OutputStream写入数据，而ChanelEndPoint则将Buffer中的数据写入Channel中。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;flush(Buffer&nbsp;buffer)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;类似上面的flush，它会将传入的header、buffer、trailer按顺序写入Socket中（OutputStream或者Channel）。返回总共写入的字节数。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;flush(Buffer&nbsp;header,&nbsp;Buffer&nbsp;buffer,&nbsp;Buffer&nbsp;trailer)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;当在处理HTTP/1.0请求时或当前Request中的KeepAlive的值为false时，在处理完成当前请求后，需要调用shutdownOutput()方法，关闭当前连接；或在处理当前请求时出现比较严重的错误、Socket超时时。在调用完shutdownOutput()方法后，isOutputShutdown()方法返回true。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;shutdownOutput()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isOutputShutdown();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;当Server无法从当前连接(Socket)中读取数据时(即read返回-1)时，调用shutdownInput()方法以关闭当前连接，此时isInputShutdown()返回true。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;shutdownInput()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isInputShutdown();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;当Socket超时或在读写Socket过程中出现任何IO错误时，Server会直接调用close()方法以关闭当前连接。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;close()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br /><span style="color: #008000;">&nbsp; &nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;当前Connection是否已经打开，对ChannelEndPoint来说表示Channel.isOpen()返回true，对SocketEndPoint来说，表示Socket没有被关闭。</span><span style="color: #008000;"><br /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">public</span>&nbsp;<span style="color: #0000ff;">boolean</span>&nbsp;isOpen();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;对StreamEndPoint来说，它的读写是阻塞式的，但是对ChannelEndPoint来说，如果它内部的channel是SelectableChannel，那么这个Channel的读写可以配置成非阻塞的(通过SelectableChannel.isBlocking()方法判断)。因而对SelectChannelEndPoint需要使用blockReadable()方法来阻塞直到超时。返回true表示阻塞读取失败，此时HttpParser会关闭这个EndPoint，并抛出异常。blockWritable()方法类似blockReadable()用于SelectChannelEndPoint以等待有数据写入到Channel中，如果返回false，表示在指定的时间内没有数据可写入Channel中(即超时)，此时会关闭该EndPoint，并抛出异常。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isBlocking();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;blockReadable(<span style="color: #0000FF; ">long</span>&nbsp;millisecs)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;blockWritable(<span style="color: #0000FF; ">long</span>&nbsp;millisecs)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;对SslSelectChannelEndPoint，它是Buffered，因而它的isBuffered()方法返回true，而isBufferingInput()和isBufferingOutput()根据内部的_inNIOBuffer和_outNIOBuffer字段的hasContent()方法判断是否返回true或false。对其他类型的EndPoint来说，这三个方法都返回false。而flush()方法则将_outNIOBuffer中缓存的数据写入Channel中。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isBufferred();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isBufferingInput();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isBufferingOutput();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;flush()&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;EndPoint还定义了一些和EndPoint相关链的信息和状态：<br /></span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;返回该EndPoint内部使用的传输工具，如ChannelEndPoint内部使用Channel，而SocketEndPoint内部使用Socket。该方法用于对内部传输工具的配置。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Object&nbsp;getTransport();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;用于配置Socket的SO_TIMEOUT的时间，即等待客户连接的超时时间。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getMaxIdleTime();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;setMaxIdleTime(<span style="color: #0000FF; ">int</span>&nbsp;timeMs)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;返回当前EndPoint所在服务器的IP地址、主机名、端口号以及客户端的IP地址、主机名、端口号。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getLocalAddr();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getLocalHost();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getLocalPort();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getRemoteAddr();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;getRemoteHost();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getRemotePort();<br />
}</div>
<h2>
<span style="color: #ff6600;">EndPoint类图</span></h2>
<h2><img src="http://www.blogjava.net/images/blogjava_net/dlevin/Jetty-EndPoint.jpg" alt="" height="534" border="0" width="1256" /><br /><span style="color: #ff6600;">EndPoint接口定义概述</span></h2>EndPoint最主要的方法从底层传输链路中读取数据并填入Buffer中的fill方法，以及将Buffer中的数据写入底层传输链路的flush方法；读数据对应Input，写数据对应Output，可以单独的关闭Input或Output，并提供方法判断Input或Output是否已经被关闭；可以用close方法关闭EndPoint，也可以通过isOpen方法判断是否这个EndPoint是否已经被关闭；可以以阻塞的方式读写EndPoint，并判断当前EndPoint是否处于阻塞状态（主要用于SelectChannelEndPoint中）；对SslSelectChannelEndPoint来说，它在读写时都可能内部缓存数据，因而EndPoint中定义了一些方法用于判断当前EndPoint是否有输入/输出换成，以及使用flush将缓存中的数据写入到底层链路中；对底层Socket，EndPoint还可以配置其最长的空闲时间；最后EndPoint还提供一些方法用于获取本地和远程的地址、主机名、端口号，以及获取底层传输类，如Socket、Channel等。<br /><br /><h2><span style="color: #ff6600;">StreamEndPoint实现</span></h2>StreamEndPoint采用古老的Stream方法从Socket中读写数据，它包含InputStream和OutputStream，分别表示读写数据流；它永远是阻塞式读写，因而isBlocking、blockReadable、blockWritable永远返回true；它也不会在内部缓存读写数据，因而isBufferingInput、isBufferingOutput、isBufferred永远返回false，而flush方法直接调用OutputStream的flush方法；对fill实现，直接使用传入的Buffer从InputStream中读取数据；对flush实现，直接将Buffer中的数据写入到OutputStream中；close方法同时关闭InputStream和OutputStream，并将成员变量置为null；对StreamEndPoint本身，没有本地或远程的地址、主机名、端口号信息。<br /><br /><strong>SocketEndPoint</strong>是StreamEndPoint的子类，它从Socket中获取InputStream和OutputStream，以及本地和远程的地址、主机名、端口号；而isInputShutdown、isOutputShutdown、shutdownInput、shutdownOutput等方法直接调用Socket中相应的方法；getTransport直接返回Socket实例；setMaxIdleTime方法同时设置Socket的SO_TIMEOUT值；当空闲超时，只关闭Input。 <br /><br /><strong>ConnectorEndPoint</strong>继承自SocketEndPoint，它是SocketConnector的内部类，每一个客户端的连接请求创建一个ConnectorEndPoint实例，在创建ConnectorEndPoint的同时，会在内部创建一个HttpConnection实例；它还实现了ConnectedEndPoint，因而可以从外部设置Connection实例；在读数据时，如果遇到EOF，表示连接已经断开，因而关闭当前EndPoint；在关闭EndPoint时，cancel当前Connection中Request实例的AsyncContinuation。ConnectorEndPoint还实现了Runnable接口，在其run方法的实现中，它首先更新处理的Connection的引用计数，然后保存当前Connection实例，在SocketConnector已经启动，并且ConnectorEndPoint未被关闭的状态下循环调用Connection的handle方法，在每个循环开始前检查当前Connector是否处于Low Resources状态（如线程池的可用线程已经不多），此时更新EndPoint的MaxIdleTime为当前Connector的LowResourcesMaxIdleTime的值，以减少一些连接的空闲等待时间；对任何Exception，关闭当前EndPoint；最后更新Connector中的一些统计信息，将当前Connection从Connector的当前正在处理的connections集合中移除，如果此时Socket还未关闭，读取Socket中的数据直到数据读完或超过MaxIdleTime，此时如果Socket还未关闭，则关闭当前Socket。而在Connector创建ConnectorEndPoint时，会调用其dispatch方法，将其自身仍给相应的线程池处理，以在某个时间在另一个线程中调用其run方法。<br /><br /><strong>SslConnectorEndPoint</strong>继承自ConnectorEndPoint，它在关闭Input和Output时会同时关闭整个EndPoint，而在执行真正的处理逻辑前有一个handle shake的过程。<br /><h2><span style="color: #ff6600;">ChannelEndPoint实现</span></h2>ChannelEndPoint采用NIO实现，从Channel中读写数据。在创建ChannelEndPoint时传入ByteChannel，如果传入的ByteChannel是SocketChannel，则同时纪录Socket实例，以及获取本地、远程的地址信息，并设置MaxIdleTime值为SO_TIMEOUT值。如果该ByteChannel是SelectableChannel类型（ServerSocketChannel、SocketChannel、DiagramChannel、SinkChannel、SourceChannel），并且其isBlocking()方法返回false，表示该Channel是非阻塞式的读写，否则这个Channel是阻塞式的读写，但是默认情况下，blockReadable、blockWritable直接返回true，表示阻塞式的读写。对非SslSelectChannelEndPoint的EndPoint不会在内部缓存数据，因而isBufferred、isBufferingOutput、isBufferingInput直接返回false，而flush方法为空实现；对SocketChannel，在设置MaxIdleTime时，同时将该值设置到底层Socket的SO_TIMEOUT的值中；getTransport直接返回底层channel实例；shutdownInput、shutdownOutput、isInputShutdown、isOutputShutdown使用Socket实现；fill实现只支持NIOBuffer，它使用Channel将数据写入内部的ByteBuffer中；flush实现使用Channel将ByteBuffer中的数据写入到Channel中，或使用GatheringByteChannel将多个ByteBuffer同时写入到Channel中。<br /><br /><strong>BlockingChannelEndPoint</strong>类是BlockingChannelConnector的内部类，它继承自ChannelEndPoint，并实现了ConnectedEndPoint和Runnable接口。在创建BlcokingChannelEndPoint时，同样也会创建HttpConnection实例；每次调用fill、flush方法时，都会更新_idleTimestamp的值为当前时间戳(该值也会在每一次Connection开始重新被处理时更新)，在BlockingChannelConnector启动时会生成一个Task，它没400毫秒遍历一次所有正在处理的EndPoint，如果发现有EndPoint已经超时（checkIdleTimestamp()方法，即空闲时间超过MaxIdleTime），则调用其idleExpired()方法，将该EndPoint关闭；BlockingChannelConnector在接到一个连接后，先会设置SocketChannel的blockingChannel为true，然后使用这个SocketChannel创建一个BlockingChannelEndPoint，并调用其dispatch()方法，将它丢到一个线程池中，在BlockingChannelEndPoint的run方法实现中，首先更新一些统计数据，纪录当前正在处理的EndPoint；只要当前EndPoint还处于打开状态，先更新_idleTimestamp为当前时间戳，然后如果当前ThreadPool处于LowOnThread状态，将timeout时间更新为LowResourcesMaxIdleTime，而后调用Connection的handle方法；对任何Exception，直接关闭EndPoint；在最后退出时，如果EndPoint还未关闭，读取EndPoint的数据，直到超时，并强制关闭EndPoint。<br /><br /><div><strong>SelectChannelEndPoint类</strong>在SelectChannelConnector中被使用，它继承自ChannelEndPoint，并实现了ConnectedEndPoint和AsyncEndPoint接口，SelectChannelConnector采用NIO中多路复用的机制，因而实现会比较复杂一些。在创建Connector时，首先创建ConnectorSelectorManager实例(_manager)，在SelectChannelConnector启动时，设置_manager的SelectSets(acceptors)、MaxIdleTime、LowResourcesConnections、LowResourcesIdleTime，然后启动_manager，并且启动acceptors个线程，只要SelectChannelConnector处于Running状态，就不断的调用_manager.doSelect()方法。ConnectorSelectorManager在启动时会创建_selectSets个SelectSet；而doSelect方法会调用根据传入的索引号对应的SelectSet的doSelect()方法。当客户端的连接到来后，SelectChannelConnector首先会配置SocketChannel的configureBlocking为false，然后将该SocketChannel注册到_manager中，在注册过程中，根据当前的SelectSet索引值找到相应的SelectSet(之后索引自增)，然后调用SelectSet的addChange(传入SocketChannel)和wakeup方法。因而这里最重要的就是SelectSet的实现，它是SelectorManager中的一个内部类。</div><div>SelectSet类的实现中，它内部有一个Selector，一个ConcurrentLinkedQueue的changes队列，以及SelectChannelEndPoint到SelectSet的集合(它用于调用SelectChannelEndPoint中的checkIdleTimestamp()方法以检查并关闭处于Idle Timeout的SelectChannelEndPoint)。SelectSet使用addChange()方法添加需要改变状态的对象，这些对象有EndPoint、ChannelAndAttachment、SocketChannel、Runnable。在doSelect()方法中，首先检查changes队列中是否有对象，如果有SelectChannelEndPoint对象，则调用其doUpdateKey()方法；如果是SocketChannel对象，则注册OP_READ操作到Selector中，创建新的SelectChannelEndPoint，attach新创建的SelectChannelEndPoint到SelectionKey中，调用SelectChannelEndPoint的schedule()方法。对ChannelAndAttachment对象，如果其Channel是SocketChannel，并且处于Connected状态，则类似对SocketChannel对象的处理，否则，注册OP_CONNECT操作到Selector；如果是Runnable对象，则dispatch该Runnable对象。然后调用Selector的selectNow()方法，如果没有任何可用的事件，则计算出等待时间，然后带等待时间的调用Selector的select()方法；遍历所有Selected Keys，对Invalid的SelectionKey，直接调用其attach的SelectChannelEndPoint的doUpdateKey()方法，否则对类型是SelectChannelEndPoint的attachment调用其schedule()方法，对connectable的SelectionKey创建新的SelectChannelEndPoint并调用schedule()方法，否则创建新的SelectChannelEndPoint并对readable的SelectionKey调用其schedule()方法。</div><div></div><div>SelectChannelEndPoint采用NIO的非阻塞读写方式，而NIO基于Channel的非阻塞操作是基于注册的操作集(OP_READ, OP_WRITE, O_CONNECT, OP_ACCEPT)以从Selector中选出已经可用的SelectionKey(包含对应的Channel、interestOps、readable、writable、attachment等)，之后可以使用对应的Channel以及根据SelectionKey中对应的已经可用的操作执行相应的操作(如读写)，因而SelectChannelEndPoint的其中一个任务是要实时的更新当前它感兴趣的操作集，并重新像Selector中注册。 SelectChannelEndPoint使用updateKey()方法跟新感兴趣操作集合，并且它只关注OP_READ和OP_WRITE操作，在实现时，OP_READ只需要在Socket的输入没有关闭，且还没有dispatch或当前处于readBlocked状态下才需要关注；OP_WRITE只需要在Socket的输出没有关闭，且writable为false(当需要向Channel中写数据，但是还没有写完的情况下)或当前处于writeBlocked状态下才需要关注；如果和当前已注册的操作集相同，则不需要重新注册，否者将自身通过SelectSet的addChange()方法添加到SelectSet中，在SelectSet的doSelect()方法中会最终调用SelectChannelEndPoint中的doUpdateKey()方法，该方法的实现：1. 当Channel处于Open状态，存在感兴趣的操作，SelectionKey为null或invalid，如果Channel已经注册了，重新调用updateKey()方法(感觉这里一般不会被调用到，如果被调用到了，则可能出现死循环)，否则将Channel重新向Selector中重新注册interestOps的操作集(如果出错，则canncel SelectionKey，并且从SelectSet中销毁当前EndPoint)。2. 当Channel处于Open状态，存在感兴趣的操作，SelectionKey存在且valid，则直接使用interestOps更新SelectionKey的感兴趣集(调用SelectionKey的interestOps()方法)。3. 当Channel处于Open状态，不存在感兴趣的操作，清空SelectionKey的interestOps，或清理SelectionKey引用。4. 如果Channel处于关闭状态，则canncel SelectionKey，并从SelectSet中销毁当前EndPoint。</div><div>对阻塞读写(readBlocked、writeBlocked)，在blockReadable()、blockWritable()方法中，会设置readBlocked、writeBlocked为true，调用updateKey()方法，然后计算等待时间并进入等待(调用wait方法)，如果因为超时而退出等待，则返回false，否则返回true(在返回时设置readBlocked、writeBlocked为false)；当调用SelectChannelEndPoint的schedule()方法时，它会更新readBlocked、writeBlocked、interestOps的值(同时使用该值更新SelectionKey中的状态)，并调用notifyAll()方法唤醒blockReadable()、blockWritable()方法：1. 如果SelectionKey为null或invalid，readBlocked、writeBlocked设置为false，调用notifyAll()，并返回；2. 如果readBlocked或writeBlocked为true，使用SelectionKey的readable、writable更新readBlocked和writeBlocked的值，调用notifyAll()，如果已经dispatched，清除所有interestOps，并返回；3. 如果还没有dispatched，直接清除所有interestOps，并返回；4. 如果注册了OP_WRITE，并且已经可写，则清除OP_WRITE操作，设置writable为true；5. 如果还没有dispatched，则调用dispatch()方法。在dispatch()方法中，它设置dispatched为true，并将handler扔给ThreadPool(在handler调用Connection的handle()方法，由于SelectChannelEndPoint的生命周期是在SelectManager维护，并且dispatch()方法可能被多次调用，因而没有在handler的handle()方法中判断EndPoint的close状态，并循环的调用Connection的handle()方法，而是在每次handle()方法结束后退出当前线程，在下次schedule()时会使用重新将handler扔给ThreadPool以支持AsyncContinuation的实现，并且AsyncEndPoint接口的定义也是用于AsyncContinuation的实现，这个将在以后的博客中详述)。在flush()方法中，如果没有任何数据能写入Channel时，设置writable为false(从而在updateKey()方法中能将OP_WRITE注册到SelectionKey的interestOps中)，并在没有dispatch的情况下调用updateKey。最后清理Selector中的selectedKeys，expire所有timeout中注册的Task(使用scheduleTimeout()方法注册)，依次调用检查EndPoints中是否已经TimeOut。</div><div>在SelectChannelEndPoint的构建中，它使用SocketChannel、SelectSet、SelectionKey构建，内部从SelectSet中获取SelectorManager，并使用SelectorManager 创建Connection实例，初始化_dispatched、_redispatched为false，_open、_writable为true，_readBlocked、_writeBlocked为false，_interestOps为0，最后更新_idleTimestamp为当前时间。当一个客户端连接到来后，SelectChannelConnector会向SelectorManager(SelectSet)中注册一个SocketChannel，当后台线程调用SelectSet中的doSelect()方法时，它使用该SocketChannel，向该SelectSet中的Selector注册OP_READ得到一个SelectionKey，并使用这个SocketChannel、当前SelectSet、以及这个SelectionKey创建一个SelectChannelEndPoint，而后调用SelectChannelEndPoint的schedule()方法。</div><div><br /><strong>SslSelectChannelEndPoint类</strong>采用Buffer的形式先将数据读写到内部缓存中，然后使用SSLEngine来wrap或unwrap(encode/decode)数据。这里不再详述。</div><img src ="http://www.blogjava.net/DLevin/aggbug/411666.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-03-29 14:34 <a href="http://www.blogjava.net/DLevin/archive/2014/03/29/411666.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之Buffer</title><link>http://www.blogjava.net/DLevin/archive/2014/03/29/411662.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Sat, 29 Mar 2014 05:30:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/03/29/411662.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/411662.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/03/29/411662.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/411662.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/411662.html</trackback:ping><description><![CDATA[<h2 style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; line-height: 20.799999237060547px;"><span style="color: #ff6600;">概述</span></h2>
在Jetty中Buffer是对Java中Stream IO中的buffer和NIO中的buffer的抽象表示，它主要用于缓存连接中读取和写入的数据。在Jetty中，对每个连接使用Buffer从其InputStream中读取字节数据，或将处理后的响应字节写入OutputStream中，从而Jetty其他模块在处理请求和响应数据时直接和Buffer打交道，而不需要关注底层IO流。
<br />
<br />
<h2 style="color: #4b4b4b; font-family: georgia, verdana, Arial, helvetica, sans-seriff; line-height: 20.799999237060547px;"><span style="color: #ff6600;">Buffer接口定义</span></h2>
Jetty中Buffer接口定义如下：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">interface</span>&nbsp;Buffer&nbsp;<span style="color: #0000FF; ">extends</span>&nbsp;Cloneable&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;基于Buffer主要用于向当前连接读写数据，因而它定义了两个核心的方法：readFrom和writeTo。其中readFrom方法从InputStream中读取字节数据，writeTo方法将响应字节写入OutputStream中，即向Connection中读取和写入数据，写完后清理当前Buffer。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;readFrom(InputStream&nbsp;in,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;max)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;writeTo(OutputStream&nbsp;out)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;IOException;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;在Buffer从InputStream（Connection）中读取数据后，Buffer接口还提供了很多不同的方法用于从Buffer中读取或写入字节数据。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Jetty中的Buffer是一个FIFO的字节队列，它的设计类似NIO中Buffer的设计：每次get操作都从getIndex开始，并且getIndex会向前移动读取的字节数的长度；每次的peek操作也getIndex开始，但是peek操作不会使getIndex向前移动；每次put操作都从putIndex开始，并且putIndex会向前移动写入的字节数的长度；每次poke操作也会从putIndex开始，但是poke操作不会使putIndex向前移动；mark操作会以getIndex作为基准设置markIndex的值，从而在reset时会将getIndex重置到之前mark过的位置；capacity表示该Buffer最多可存储的字节数，而length表示从getIndex到putIndex还存在的字节数。并且Buffer永远保证以下关系总是成立：<strong>markIndex&lt;=getIndex&lt;=putIndex&lt;=capacity</strong></span><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;get();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;get(<span style="color: #0000FF; ">byte</span>[]&nbsp;b,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;offset,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;length);<br />
&nbsp;&nbsp;&nbsp;&nbsp;Buffer&nbsp;get(<span style="color: #0000FF; ">int</span>&nbsp;length);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;getIndex();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;mark();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;mark(<span style="color: #0000FF; ">int</span>&nbsp;offset);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;markIndex();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;peek();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;peek(<span style="color: #0000FF; ">int</span>&nbsp;index);<br />
&nbsp;&nbsp;&nbsp;&nbsp;Buffer&nbsp;peek(<span style="color: #0000FF; ">int</span>&nbsp;index,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;length);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;peek(<span style="color: #0000FF; ">int</span>&nbsp;index,&nbsp;<span style="color: #0000FF; ">byte</span>[]&nbsp;b,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;offset,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;length);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;poke(<span style="color: #0000FF; ">int</span>&nbsp;index,&nbsp;Buffer&nbsp;src);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;poke(<span style="color: #0000FF; ">int</span>&nbsp;index,&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;b);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;poke(<span style="color: #0000FF; ">int</span>&nbsp;index,&nbsp;<span style="color: #0000FF; ">byte</span>&nbsp;b[],&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;offset,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;length);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;put(Buffer&nbsp;src);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;put(<span style="color: #0000FF; ">byte</span>&nbsp;b);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;put(<span style="color: #0000FF; ">byte</span>[]&nbsp;b,<span style="color: #0000FF; ">int</span>&nbsp;offset,&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;length);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;put(<span style="color: #0000FF; ">byte</span>[]&nbsp;b);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;putIndex();<br />
<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000ff;">int</span>&nbsp;length();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">void</span>&nbsp;clear();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">void</span>&nbsp;reset();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">void</span>&nbsp;setGetIndex(<span style="color: #0000ff;">int</span>&nbsp;newStart);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">void</span>&nbsp;setMarkIndex(<span style="color: #0000ff;">int</span>&nbsp;newMark);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">void</span>&nbsp;setPutIndex(<span style="color: #0000ff;">int</span>&nbsp;newLimit);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;一个Buffer还有独立的两种状态：access级别和volatile。<br />
access级别有：IMMUTABLE，表示当前Buffer所有的Index和内容都不能被改变；READONLY，表示当前Buffer是只读的，即getIndex和markIndex可以被改变，而putIndex以及Buffer内容不可以；READWRITE，表示所有的Index以及Buffer的内容可以被改变。<br />
volatile状态表示当前Buffer是否会通过其他路径被修改，默认情况下，ByteArrayBuffer、DirectNIOBuffer等是NON_VOLATILE状态，而View是VOLATILE状态（除非View内部的Buffer是IMMUTABLE）。VOLATILE的状态感觉不是一个比较严谨的概念，比如对一个View它是VOLATILE的，但是在这种情况下，它内部包装的Buffer应该也变成VOLATILE状态，并且在所有的View被回收后，其内部包装的Buffer应该重新变成NON_VOLATILE状态。要实现这种严谨逻辑应该是可以做的到的，只是会比较麻烦，而且貌似也没必要，因而Jetty并没有尝试着去这样做。<br />
<br />
</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;返回包含当前Buffer从getIndex到putIndex内容的Buffer，并且返回的Buffer不可以被其他路径修改。如果当前Buffer是NON_VOLATILE，则直接返回当前Buffer（这个实现是不严谨的，因为在这种情况下，其实有两个Buffer实例可以修改同一份数据），否则，克隆一个新的Buffer，这样对新的Buffer的修改不会影响原Buffer的内容。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;Buffer&nbsp;asNonVolatileBuffer();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;返回一个只读的Buffer（View），该只读的Buffer的读取不会影响原来Buffer的Index。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;Buffer&nbsp;asReadOnlyBuffer();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;拷贝一个不可修改的ByteBuffer。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;Buffer&nbsp;asImmutableBuffer();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;返回一个可修改的Buffer，并且对返回的Buffer的内容修改会影响原有的Buffer。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;Buffer&nbsp;asMutableBuffer();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;当前Buffer是否不可被修改，即Buffer内容和所有Index都不能被修改。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isImmutable();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;当前Buffer是否是只读的。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isReadOnly();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;是否当前Buffer内容可以通过其他路径被修改，比如View一般情况下是VOLATILE状态（除非View内部的Buffer是IMMUTABLE）。</span><span style="color: #008000; "><br />
</span>&nbsp; &nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;isVolatile();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;除了以上的操作，Buffer还提供了一些其他用于操作Buffer内部字节的方法：<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">如果内部使用字节数组表示，返回该字节数组，否则，返回null。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">byte</span>[]&nbsp;array();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;获取从getIndex到putIndex的字节数组，其长度为length。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">byte</span>[]&nbsp;asArray();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;如果当前Buffer是对另一个Buffer的包装，则返回内部被包装的Buffer实例，否则返回当前Buffer本身。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;Buffer&nbsp;buffer();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;capacity();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;返回当前Buffer剩余的空间，即capacity－putIndex。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;space();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;清除Buffer内容，即设置getIndex和putIndex为0，以及markIndex为-1。<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;整理Buffer内容，即将markIndex&nbsp;&gt;=&nbsp;0&nbsp;?&nbsp;min(getIndex,&nbsp;markIndex)&nbsp;:&nbsp;getIndex到putIndex的内容移动到Buffer的起始位置，同时修改相应的getIndex、markIndex、putIndex。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;compact();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;当前Buffer是否有可用字节，即是否putIndex&gt;getIndex。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">boolean</span>&nbsp;hasContent();<br />
<br />
&nbsp; &nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;跳过n个字节，即getIndex增加min(remaining(),&nbsp;n)的值。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;skip(<span style="color: #0000FF; ">int</span>&nbsp;n);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;切割出当前Buffer从getIndex到putIndex的View，一般来说它是volatile的（除非它是immutable类型的Buffer）。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;Buffer&nbsp;slice();<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;切割出当前Buffer从markIndex到putIndex的View，一般来说它是volatile的（除非它是immutable类型的Buffer）。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;Buffer&nbsp;sliceFromMark();<br />
&nbsp;&nbsp;&nbsp;&nbsp;Buffer&nbsp;sliceFromMark(<span style="color: #0000FF; ">int</span>&nbsp;length);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000;">&nbsp;返回包含当前Buffer状态和内容的字符串。</span><span style="color: #008000; "><br />
</span>&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;toDetailString();<br />
&nbsp; &nbsp;&nbsp;<span style="color: #0000ff;">boolean</span>&nbsp;equalsIgnoreCase(Buffer&nbsp;buffer);<br />
&nbsp;}</div>
<h2>
<span style="color: #ff6600;">类图</span></h2>
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/JettyIO-Buffer.jpg" alt="" height="932" border="0" width="1000" /><br />
<h2><span style="color: #ff6600;">主要实现类</span></h2>
<strong>
AbstractBuffer：<br />
</strong>所有Buffer的基类，是对Buffer接口的基本实现。<br />
<strong>
ByteBuffer：<br />
</strong>它继承自AbstractBuffer主要的非NIO的Buffer实现，内部使用字节数组做缓存，直接读InputStream和写OutputStream。<br />
<strong>
DirectNIOBuffer：<br />
</strong>它实现了NIOBuffer接口，继承自AbstractBuffer，内部使用Direct的ByteBuffer做缓存，使用ReadableByteChannel和WritableByteChannel分别对InputStream（readFrom传入）和OutputStream（writeTo传入）包装，并在这两个方法中使用包装后的Channel读写数据。<br />
<strong>
IndirectNIOBuffer：<br />
</strong>它继承自ByteBuffer，内部使用非direct的ByteBuffer做缓存，并且它也直接对InputStream和OutputStream读写。<br />
<strong>
RandomAccessFileBuffer：<br />
</strong>它继承自AbstractBuffer，内部使用RandomAccessFile做缓存。<br />
<strong>
View：<br />
</strong>它继承自AbstractBuffer，内部使用另一个Buffer作为缓存，并且对非IMMUTABLE的Buffer，很多时候，它是VOLATILE。View如其名，它是对内部Buffer的视图，对View内容以及Index的修改会影响内部Buffer中相应的值。<br />
<br />
<h2><span style="color: #ff6600;">Buffers</span></h2>
Buffers是Buffer的抽象工厂，它用于创建Header的Buffer和Body的Buffer，并且可以根据给定的size获得相应的Buffer实例，在Buffer使用完成后，还可以通过returnBuffer方法将它归还个Buffers以复用。在创建Buffers子类时，可以将指定Header和Body各自Buffer的类型，从而在内部创建相应Buffer时会创建相应类型的Buffer，支持的Buffer类型有：BYTE_ARRAY、DIRECT、INDIRECT。<br />
<br />
<strong>
Jetty中有两个Buffers的实现：</strong>PooledBuffers和ThreadLocalBuffers。<br />
<br />
<strong>PooledBuffers</strong>使用ConcurrentLinkedQueue构建各自的Header、Body、Other类型的Buffer池，它有一个maxSize值用于控制该Buffers中包含的所有类型的Buffer的总数。
<strong>ThreadLocalBuffers</strong>将Header、Body、Other类型的Buffer保存在ThreadLocal中。<br />
<br />
Jetty还提供了<strong>BuffersFactory</strong>用于创建不同类型的Buffers：通过在参数中maxSize是否大于等于0以决定是使用PooledBuffers还是ThreadLocalBuffers。
<br />
<br />
<h2><span style="color: #ff6600;">BufferCache/BufferDateCache</span></h2>
Jetty还为Buffer提供了两个特殊的类：BufferCache和BufferDateCache。<br /><br /><strong>
BufferCache<br /></strong>用于存储一个可以使用存储的String值、索引值等获取相应的Buffer实例，主要用于HttpHeaders、HttpMethods等一些预定义的值。<br /><strong>
BufferDateCache<br /></strong>继承自DateCache，它存储了上一次使用一个long类型的date值格式化出的Buffer实例，从而实现部分复用（复用在同一秒得到的Request请求时创建的Buffer，因为时间也只能在这种情况下被复用，因而才会有这样的实现），在Request类中使用。<img src ="http://www.blogjava.net/DLevin/aggbug/411662.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-03-29 13:30 <a href="http://www.blogjava.net/DLevin/archive/2014/03/29/411662.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Jetty源码之HTTP协议</title><link>http://www.blogjava.net/DLevin/archive/2014/03/17/409906.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Mon, 17 Mar 2014 10:26:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2014/03/17/409906.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/409906.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2014/03/17/409906.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/409906.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/409906.html</trackback:ping><description><![CDATA[在计算机网络中，如果两台机器要通信，他们首先要定义通信数据的格式，这样在服务器收到客户端的请求消息时，它才能正确的解析请求的内容，然后根据请求内容处理逻辑，并将相应消息传递会客户端；此时，客户端也要根据已定义的响应数据格式解析响应消息。在浏览器和HTTP服务器之间的通信数据格式使用HTTP协议定义。<br />
<br />
<h2><span style="color: #ff9900;">请求消息</span></h2>
其中请求消息的格式为：<br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/HTTP_Request_Message_Format.png" alt="" height="676" width="1204" /><br />
例子：<br />
<pre style="font-size: 10pt; line-height: 18.66666603088379px; border-color: #c0c0c0 #c0c0c0 #c0c0c0 #6ce26c; border-width: 1px 1px 1px 5px; border-style: dashed dashed dashed solid; padding: 8px; white-space: pre-wrap; word-wrap: break-word; font-family: Consolas, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', 宋体; margin-top: 10px; margin-bottom: 10px; color: #333333; background-color: #ffffff;">POST /index.jsp?articleId=1234&amp;articleName=GoodArticle HTTP/1.1
Host: www.blogjava.com
Content-Length: 74
Content-Type: application/x-www-form-urlencoded
myText1=hello+world&amp;myText2=%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C</pre>
请求消息由三部分组成：<strong>请求行、消息报头、请求正文</strong>。<br />
<strong>请求行</strong>格式为：请求方法、空格、URL、版本号、回车、换行。请求方法集：GET、POST、HEAD、PUT、DELETE、TRACE、CONNECT、OPTIONS。URL中'?'之后的值用于表达请求参数。版本号可以是：HTTP/1.0、HTTP/1.1。<br />
<strong>消息报头</strong>格式为：报头名字、冒号(':')、空格、报头值、回车、换行。消息报头用于传递元数据信息，用于表达消息正文的类型、编码格式、缓存等，以回车换行结束。当遇到一个空行（只有回车换行），表示消息报头结束。<br />
<strong>消息正文</strong>，可以是任意定义的格式，它接在消息报头后（空行之后）。<br />
<br />
请求消息是否包含消息体由Content-Length或Transfer-Encoding决定，如果规范定义的请求方法不允许包含消息体，则在请求消息中不可以包含消息体。Server在解析时，如果请求方法不支持消息体，则在请求消息中包含的消息体会被忽略。<br />
<p style="font-weight: bold;"><strong style="color: #800000;">请求方法：</strong></p>
<p>HTTP1.1定义的请求方法有</p>
<p><strong style="font-weight: bold; color: #339966;">OPTIONS：<br />
</strong><span>请求查询服务器的性能，或查询与资源相关的选项和需求。<br />
</span></p>
<div>该方法的Response不可缓存。当前版本HTTP不支持该请求方法包含消息实体。如果该请求包含请求消息体，HTTP服务器将会抛弃这些信息。如果Request-URI为*，该请求类似&#8220;ping&#8221;或&#8220;no-op&#8221;操作。</div>
<p><strong style="font-weight: bold; color: #339966;">GET：<br />
</strong><span>请求获取Request-URI所标识的资源。<br />
</span></p>
<div>响应消息可缓存。在GET请求消息中，如果包含If-Modified-Since、If-Unmodified-Since、If-Match、If-None-Match、If-Range字段，则该请求称为&#8220;条件GET&#8221;。</div>
<p><strong style="font-weight: bold; color: #339966;">HEAD：<br />
</strong><span>请求获取由Request-URI所标识的资源的响应消息报头。<br />
</span></p>
<div>响应消息可缓存。</div>
<p><strong style="font-weight: bold; color: #339966;">POST：<br />
</strong><span>在Request-URI所标识的资源后附加新的数据。<br />
</span></p>
<div>如果在HTTP服务器中有创建新的资源，则该方法的响应状态码必须是201（Created），并且响应消息实体包含描述请求的状态，以及一个Location响应消息头。该方法的响应消息不可被缓存。</div>
<p><strong style="font-weight: bold; color: #339966;">PUT：<br />
</strong><span>请求服务器存储一个资源，并用Request-URI作为其标识。<br />
</span></p>
<div>如果Request-URI指向一个已存在的资源，那么包含的消息实体被视为已存在资源的新版本，如果Request-URI没有对应的资源，则HTTP服务器可以通过该URI创建相应资源，此时HTTP服务器响应201（Created）状态码。如果修改了已存在的资源，则返回200（OK）或204（No Content）。HTTP服务器不可以忽略Content-*消息头（如Content-Range），如果HTTP服务器不能理解该消息头，则返回501（Not Implemented）响应消息码。</div>
<p><strong style="font-weight: bold; color: #339966;">DELETE：<br />
</strong><span>请求服务器删除Request-URI所标识的资源。<br />
</span></p>
<div>即使该方法的返回状态码表明该操作以执行成功，客户端还是不能保证该方法需要删除的操作已经被执行了。但是HTTP服务器必须保证在返回响应给客户端的时候，HTTP服务器已经打算删除这个资源或把它移动到一个不可访问的位置。成功的响应码为200（OK），并且响应消息实体中可以包含一些描述信息；202（Accept）表明这个操作还没被完全执行；204（No Content）表示这个操作已经执行完成，但是没有响应消息实体。该方法的响应消息不可被缓存。</div>
<p><strong style="font-weight: bold; color: #339966;">TRACE：<br />
</strong><span>请求服务器会送收到的请求信息，主要用于测试或诊断。<br />
</span></p>
<div>该方法以200返回标识成功。该请求消息不可包含请求消息实体。该方法的Response必须在响应消息实体中包含所有的请求消息，其相应消息的Content-Type值为：message/http，该Response不可被缓存。</div>
<p><strong style="font-weight: bold; color: #339966;">CONNECT：<br />
</strong><span>保留将来使用。</span></p>
<p><strong style="font-weight: bold; color: #339966;">扩展的方法：<br />
</strong><span>用户自定义扩展方法。</span></p>
<p style="font-weight: bold;"><strong></strong><br />
</p>
<p>如果Server能识别某个请求方法但是不允许该请求方法，则应该返回405（Method Not Allowed）响应状态。如果Server无法识别某个请求方法或者当前Server没有实现这个请求方法，则应该返回501（Not Implemented）状态码。</p>
<p><strong>Request－URI支持的值有</strong>：＊｜absoluteURI｜abs_path｜authority</p>
<ol style="margin-top: 1px;">
     <li><strong>＊</strong>表示请求不应用于某个特定的资源，并且只对于某些不需要应用于特定资源的请求方法，如：OPTIONS * HTTP/1.1</li>
     <li><span style="font-weight: bold;">absoluteURI：</span>当客户端是向一个代理发送请求时需要使用absoluteURI，然后这个代理会转发这个请求，并返回响应。虽然按规范，HTTP1.1客户端只发送absoluteURI到代理服务器，但是为了在将来的HTTP版本中可以允许请求都转换成absoluteURI，所有HTTP1.1 Server必须可以解析absoluteURI风格的请求：GET&nbsp;http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1
     </li>
     <li><strong>authority</strong>只在CONNECT请求方法中使用。</li>
     <li><strong style="font-weight: bold;">abs_path：</strong>用于表示Server的资源，而Server本身的信息在Host消息头中表示：
     <p>GET /put/www/TheProject.html HTTP/1.1</p>
     <p>Host: www.w3.org</p>
     </li>
</ol>
<p style="font-weight: bold;"><strong>Resource Identification rules：</strong></p>
<ol style="margin-top: 1px;">
     <li>如果Request－URI是absoluteURI，并且这个absolute的host和server的host相同，则忽略Host头。</li>
     <li>如果Request－URI不是absoluteURI，并且请求消息包含Host头，host由Host消息头决定。</li>
     <li>如果1或2中的host不是一个合法的host，则返回400（Bad Request）响应消息。</li>
</ol>
<h2><span style="color: #ff9900;">响应消息</span></h2>
响应消息的格式定义为：<br />
<img src="http://www.blogjava.net/images/blogjava_net/dlevin/HTTP_Response_Message_Format.png" alt="" height="656" width="1222" /><br />
<br />
例子：<br />
<pre style="font-size: 10pt; line-height: 18.66666603088379px; border-color: #c0c0c0 #c0c0c0 #c0c0c0 #6ce26c; border-width: 1px 1px 1px 5px; border-style: dashed dashed dashed solid; padding: 8px; white-space: pre-wrap; word-wrap: break-word; font-family: Consolas, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', 宋体; margin-top: 10px; margin-bottom: 10px; color: #333333; background-color: #ffffff;"><span style="color: #000000; font-family: Georgia, 'Times New Roman', Times, sans-serif; font-size: 14px; line-height: 25.200000762939453px; white-space: normal;">HTTP/1.1 200 OK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //请求成功</span>
<span style="color: #000000; font-family: Georgia, 'Times New Roman', Times, sans-serif; font-size: 14px; line-height: 25.200000762939453px; white-space: normal;">Server: Microsoft-IIS/5.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //web服务器</span>
<span style="color: #000000; font-family: Georgia, 'Times New Roman', Times, sans-serif; font-size: 14px; line-height: 25.200000762939453px; white-space: normal;">Date: Thu,08 Mar 200707:17:51 GMT</span>
<span style="color: #000000; font-family: Georgia, 'Times New Roman', Times, sans-serif; font-size: 14px; line-height: 25.200000762939453px; white-space: normal;">Connection: Keep-Alive&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span style="color: #000000; font-family: Georgia, 'Times New Roman', Times, sans-serif; font-size: 14px; line-height: 25.200000762939453px; white-space: normal;">Content-Length: 23330</span>
<span style="color: #000000; font-family: Georgia, 'Times New Roman', Times, sans-serif; font-size: 14px; line-height: 25.200000762939453px; white-space: normal;">Content-Type: text/html</span>
<span style="color: #000000; font-family: Georgia, 'Times New Roman', Times, sans-serif; font-size: 14px; line-height: 25.200000762939453px; white-space: normal;">Expries: Thu,08 Mar 2007 07:16:51 GMT</span>
<span style="color: #000000; font-family: Georgia, 'Times New Roman', Times, sans-serif; font-size: 14px; line-height: 25.200000762939453px; white-space: normal;">Set-Cookie: ASPSESSIONIDQAQBQQQB=BEJCDGKADEDJKLKKAJEOIMMH; path=/</span>
<span style="color: #000000; font-family: Georgia, 'Times New Roman', Times, sans-serif; font-size: 14px; line-height: 25.200000762939453px; white-space: normal;">Cache-control: private</span><span style="color: #000000; font-family: Georgia, 'Times New Roman', Times, sans-serif; font-size: 14px; line-height: 25.200000762939453px; white-space: normal;"></span></pre>
响应消息也有三部分组成：<strong>状态行、消息报头、响应正文</strong>。<br />
<strong>状态行</strong>格式：版本号、空格、状态、空格、状态短语、回车、换行。版本号可以是：HTTP/1.0、HTTP/1.1。状态号和状态短语由HTTP协议定义，状态号有5中取值可能：<br />
1xx：指示信息--表示请求已经接收，继续处理。<br />
2xx：成功--表示请求已经被成功接收、理解、处理。<br />
3xx：重定向--要完成请求，必须进行更进一步操作。<br />
4xx：客户端错误--请求有语法错误或请求无法实现。<br />
5xx：服务器端错误--服务器未能实现合法的请求。<br />
常见的状态号和状态短语有：<br />
200 OK --请求成功。<br />
304 Not Modified --资源没有改变。<br />
400 Bad Request --客户端请求有语法错误，不能被服务器理解。<br />
401 Unauthorized --请求未经授权（和WWW-Authenticate报头一起使用）。<br />
403 Forbidden --服务器收到请求，但是拒绝提供服务。<br />
404 Not Found --请求资源不存在。<br />
500 Internal Server Error --服务器发生不可预期的错误。<br />
503 Server Unavailable --服务器当前不能处理客户端的请求，一段时间后可能恢复正常。<br />
<strong>响应报头</strong>和请求报头格式一样：报头名、冒号（':'）、空格、报头值、回车、换行。用于记录响应消息的元数据，表达响应消息的长度、编码方式、Cookiee等信息。遇到一个空行（只有回车换行）表示响应消息报头结束。<br />
<strong>响应消息正文</strong>紧随响应消息报头（在空行后），它可以是任意的内容，由客户端解析。<br />
<br />
在响应消息中是否包含消息体是由请求方法和响应状态码决定，所有对HEAD请求方法的响应消息不能包含任何消息体，即使在响应消息中可能会包含实体消息头，以至于有人会认为这个响应消息包含消息体。所有1XX（informational）、204（no content）、304（not modified）响应消息不能包含消息体。所有其他的响应消息都包含消息体，即使有些时候消息体的长度是0。<br />
<h2><span style="color: #ff9900;">杂记</span></h2>
协议本身，最终要的在于消息格式，HTTP协议的请求消息和响应消息已经详细说明了，剩下的就是一些具体细节的问题，比如URI的格式、各种消息报头代表的含义、响应状态号对应的含义等。因为时间有限，不做整理，所以只是一些阅读协议的杂记。<br />
<br />
<strong>URI</strong>（Uniform Resource Identifiers），又名：UDI（Universal Document Identifiers），是URL（Uniform Resource Locators）和URN（Unifrom Resource Names）的组合。从HTTP协议的角度，URL只是一个由字符串组成的用于名称、位置等的标识符。在HTTP协议中使用URL作为定位符，它的格式为：http://${host}[:${port}][${abs_path}[?${query}]]<br />
<br />
<strong>Date/Time格式</strong>：因为历史原因，HTTP支持三种日期、时间格式：<br />
Sun, 06 Nov 1994 08:49:37 GMT &nbsp; &nbsp; ; RFC 822, updated by RFC 1123<br />
Sunday, 06-Nov-94 08:49:37 GMT &nbsp; ; RFC 850, obsolted by RFC 1036<br />
Sun Nov 6 08:49:37 1994 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; ANSI C's asctime() format<br />
其中第一种格式是推荐的网络格式，而且它是固定长度的。HTTP1.1客户端和服务器端需要能接收所有以上三种日期格式，但是只生成第一种日期格式。所有HTTP日期、时间都必须是格林威治时间（GMT，Greenwich Mean Time），在HTTP中，GMT和UTC（Coordinated Universal Time）时间相同。<br />
<br />
<strong>编码集</strong>：同MINE格式规范定义。在Cotent-Type头中定义。<br />
<br />
<strong>内容编码</strong>：主要用于对消息实体是否压缩、采用什么压缩算法的表示。在HTTP1.1中使用Accept-Encoding和Content-Encoding头中定义，支持的值有：gzip、compress（废弃）、deflate（zlib格式）、identity（默认不压缩，只能用于Accept-Encoding中，不能用于Content-Encoding）。这些支持的格式在IANA（Internet Assigned Numbers Authority）中注册。<br />
<br />
<strong>传输编码（transfer-coding）</strong>：用表示可以、需要应用到实体主体以确保通过网络&#8220;安全传输&#8221;的编码转换。这与内容编码不同，传输编码是消息而非原始实体的属性。所有传输编码值大小写无关，它类似于MINE编码中的Content-Transfer-Encoding。可用的值为：chunked，identity，gzip，compress，deflat。HTTP/1.0不支持。<br />
<br />
<strong>媒体类型</strong>：HTTP通过Content-Type和Accept头部域以提供可扩展的数据类型。值格式：<br />
${type}/${subtype};${paramName}=${paramValue};....<br />
<br />
<strong>Product符号</strong>：用于允许通信应用程序通过软件名称和版本号来标识它自己，比如：<br />
User-Agent: CERN-LineMode/2.15 libwww/2.17b3<br />
Server: Apache/0.84<br />
<br />
<strong>qvalue</strong>：使用[0-1]的值来表达参数的重要性，0表示不可接受，该值的小数部分不可操作三位。<br />
<br />
<strong>语言标签</strong>：用于表达消息实体的自然语言，用Accept-Language和Content-Language字段表达。它的值可以是：en、en-US、en-cockney、i-cherokee、x-pig-latin等。<br />
<br />
<strong>实体标签</strong>：用于比较相同请求资源的两个或多个实体的比较。如If-Match、If-None-Match、If-Range等头部域名。<br />
<br />
<strong>范围标签</strong>：HTTP/1.1允许客户端值请求响应实体的某部分（范围）作为响应消息，如Range、Content-Range头部域，他们的单位在HTTP/1.1中只支持byte。<br />
<h2><span style="color: #ff9900;">消息报头详解</span></h2>
在请求消息和响应消息中都有消息报头，消息报头在HTTP1.1协议中（RFC2616）有三种类型的头：<strong>通用头（General Header）、请求头（Request Header）、响应头（Response Header）、实体头（Entity Header）</strong>。其格式为：header-name: header-value。其中header-name大小写无关，以一个空行（只包含回车和换行）结束。header-value可以以任意数量的LWS开头（一般是一个空格）。消息头可以以至少一个SP或HT开头的方式扩展成多行（原文：header fields can be extended over multiple lines by preceding each extra line with at least one SP or HT，感觉理解的有问题....）。相同的header-name可以重复出现。<br />
<p><strong style="color: #800000;">通用消息头：</strong></p>
<p><strong style="color: #339966;">Cache-Control:&nbsp;<br /></strong></p><div>指定缓存指令。如请求相关的指令：no-cache、no-store、max-age、max-stale、min-fresh、no-transform、only-if-cached、cache-extension，响应相关的指令：public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age、s-maxage、cache-extension。</div>
<p><strong style="color: #339966;">Connection:<br /></strong>客户端通过发送包含close值的Connection头，表达在这次请求结束后，Server可以关闭这个连接，此时Server如果选择发送响应后关闭连接，则在响应消息中需要包含值为close的Connection头。<strong>&nbsp;<br /></strong></p><div>允许发送者指定当前Connection的一些选项。HTTP/1.1只定义了close的值，表示响应返回后，当前Connection将会被关闭。</div>
<p><strong style="color: #339966;">Date:&nbsp;<br /></strong></p><div>表示消息发送的时间，你的描述格式由RFC822定义。例如Mon, 31 Dec 2001 04:25:57GMT</div>
<p><strong style="color: #339966;">Pragma:&nbsp;<br /></strong></p><div>用于包含实现相关的指令。如no-cache</div>
<p><strong style="color: #339966;">Trailer:&nbsp;<br /></strong></p><div>表示指定的头在chunked消息的尾部。</div>
<p><strong style="color: #339966;">Transfer-Encoding:&nbsp;<br /></strong></p><div>消息在传输时使用的编码。如chunked。</div>
<p><strong style="color: #339966;">Upgrade:&nbsp;<br /></strong></p><div>允许客户端指定它额外支持的传输协议，如果服务器发现更新的传输协议更合适当前请求，则它可以将当前传输协议转换成更新的传输协议。如HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11等。</div>
<p><strong style="color: #339966;">Via:&nbsp;<br /></strong></p><div>用于网关或代理服务器，以指示客户端和服务器之间的中间协议和接收者。</div>
<p><strong style="color: #339966;">Warning:&nbsp;<br /></strong></p><div>用于添加一些额外的状态或转换信息。</div>
<p><strong></strong><br />
</p>
<p><strong style="color: #800000;">请求消息头：</strong></p>
<p>请求消息头允许客户端传递一些额外关乎客户端信息给Server，这些字段类似在方法调用中的参数。</p>
<p><strong style="color: #339966;">Accept:&nbsp;<br /></strong></p><div>指定当前请求响应可以接受的媒体类型，以逗号间隔。如：&#8220;audio/*; q=0.2, audio/basic, text/html, */*&#8221;。</div>
<p><strong style="color: #339966;">Accept-Charset:&nbsp;<br /></strong></p><div>指定当前请求响应可以接受的字符编码集，以逗号间隔。如&#8220;iso-8859-5, Unicode-1-1; q=0.8&#8221;。</div>
<p><strong style="color: #339966;">Accept-Encoding:&nbsp;<br /></strong></p><div>类似Accept，定义消息实体的编码方式。如&#8220;compress, gzip, *, identity; q=0.5&#8221;等。</div>
<p><strong style="color: #339966;">Accept-Language:&nbsp;<br /></strong></p><div>类似Accept，定义自然语言的限制。如&#8220;da, en-gb; q=0.8, en; q=0.7&#8221;等。</div>
<p><strong style="color: #339966;">Authorization:&nbsp;<br /></strong></p><div>客户端向服务器传递认证信息。</div>
<p><strong style="color: #339966;">Expect:<br /></strong>客户端发送一个包含100-continue值的Expect字段头，以在不发送真正消息实体的情况下测试服务器是否能接收这个消息。此时Server响应417（Expectation Failed）或100（Continue），然后客户端决定是否要继续发送请求消息体。<br /></p><div>请求消息头，用于指定客户端对服务器端响应行为的需求。如100-continue、102-processing等。</div>
<p><strong style="color: #339966;">From:&nbsp;<br /></strong></p><div>请求头，指定用户的email地址。</div>
<p><strong style="color: #339966;">Host:&nbsp;<br /></strong></p><div>请求头，指定服务器的主机名和端口。如：www.w3.org:8080</div>
<p><strong style="color: #339966;">If-Match:&nbsp;<br /></strong></p><div>请求头，用于条件请求方法。</div>
<p><strong style="color: #339966;">If-Modified-Since:&nbsp;<br /></strong></p><div>请求头，用于条件请求方法：请求变体自从指定的时间内没有发生改变。</div>
<p><strong style="color: #339966;">If-None-Match:&nbsp;<br /></strong></p><div>请求头，用于条件请求方法。</div>
<p><strong style="color: #339966;">If-Range:&nbsp;<br /></strong></p><div>表示如果实体没有变法，则发送给客户端指定部分的实体。</div>
<p><strong style="color: #339966;">If-Unmodified-Since:&nbsp;<br /></strong></p><div>如果实体在指定时间内没有发生变化，则直接发送响应，否则返回412（Precondition Failed）的响应。</div>
<p><strong style="color: #339966;">Max-Forwards:&nbsp;<br /></strong></p><div>请求头，指定最大可以被代理、网关服务器转发的次数。</div>
<p><strong style="color: #339966;">Proxy-Authorization:&nbsp;<br /></strong></p><div>请求头，用于客户端包含对代理服务器的认证信息。</div>
<p><strong style="color: #339966;">Range:&nbsp;<br /></strong></p><div>指示范围，如bytes=0-499</div>
<p><strong style="color: #339966;">Referer:&nbsp;<br /></strong></p><div>请求头，允许客户端指定当前URI是从哪个URI中获得的。</div>
<p><strong style="color: #339966;">TE:&nbsp;<br /></strong></p><div>请求头，用于指示在响应中希望接收的扩展传输编码。如deflate、trailers, deflate;q=0.5等。</div>
<p><strong style="color: #339966;">User-Agent:&nbsp;<br /></strong></p><div>请求头，用于添加客户端软件信息。</div>
<p><strong></strong><br />
</p>
<p><strong style="color: #800000;">响应消息头</strong></p>
<p>响应消息头允许Server传递一些关于响应的额外信息给客户端。</p>
<p><strong style="color: #339966;">Accept-Ranges:&nbsp;<br /></strong></p><div>响应头，允许服务器指定它可接受的请求范围。如&#8220;bytes&#8221;、&#8220;none&#8221;等。</div>
<p><strong style="color: #339966;">Age:&nbsp;<br /></strong></p><div>响应头，当代理服务器用自己缓存的实体去响应请求时，该头部表示该实体从产生到现在经过多少时间了。该数值的代为秒。</div>
<p><strong style="color: #339966;">ETag:&nbsp;<br /></strong></p><div>响应消息头，用于指定请求变体中的实体标签的当前值。如xyzzy，W/xyzzy等。</div>
<p><strong style="color: #339966;">Location:&nbsp;<br /></strong></p><div>响应头，用于指示接收方重定向。</div>
<p><strong style="color: #339966;">Proxy-Authenticate:&nbsp;<br /></strong></p><div>响应头，在407（Proxy Authenticate Required）响应中，它包含代理服务器需要的验证模式和参数。</div>
<p><strong style="color: #339966;">Retry-After:&nbsp;<br /></strong></p><div>响应头，通503（Service Unavailable）响应一起使用，用于指定服务器预计不可用时间；或者3xx，用于指定客户端在重定向之前等待的时间。</div>
<p><strong style="color: #339966;">Server:&nbsp;<br /></strong></p><div>响应头，用于添加服务器软件信息。</div>
<p><strong style="color: #339966;">Vary:<br /></strong></p><div>用于指示用于决定当响应是最新时，是否cache可以用于接下来的响应并且不用验证的请求字段集合。</div>
<p><strong style="color: #339966;">WWW-Authenticate:&nbsp;<br /></strong></p><div>响应头，用于401（Unauthorized）响应消息中，用于指定服务器需要的认证模式和参数。</div>
<p><strong></strong><br />
</p>
<p><strong style="color: #800000;">实体消息头</strong></p>
<p>实体消息头属于实体的一部分，是实体的元数据。</p>
<p><strong style="color: #339966;">Allow:&nbsp;<br /></strong></p><div>实体头，列出所有对当前Request-URI指定资源支持的方法。</div>
<p><strong style="color: #339966;">Content-Encoding:&nbsp;<br /></strong></p><div>实体头，用于指定实体内容的编码方式。如gzip、identity等。</div>
<p><strong style="color: #339966;">Content-Language:&nbsp;<br /></strong></p><div>实体头，用于定义实体内容的自然语言。如da、en、mi等。</div>
<p><strong style="color: #339966;">Content-Length:&nbsp;<br /></strong></p><div>实体头，用于指定实体内容的长度。</div>
<p><strong style="color: #339966;">Content-Location:&nbsp;<br /></strong></p><div>实体头，用于指定资源所在的URI。</div>
<p><strong style="color: #339966;">Content-MD5:&nbsp;<br /></strong></p><div>实体头，用于表示实体内容的数字摘要。</div>
<p><strong style="color: #339966;">Content-Range:&nbsp;<br /></strong></p><div>实体头，用于指定实体内容的范围。如bytes 0-499/1234（即单位 范围/总长度）。</div>
<p><strong style="color: #339966;">Content-Type:&nbsp;<br /></strong></p><div>实体头，用于指定实体内容的媒体类型。如text/html; charset=ISO-8859-4等。</div>
<p><strong style="color: #339966;">Expires:&nbsp;<br /></strong></p><div>实体头，用于响应在多少时间后在Cache中失效。</div>
<p><strong style="color: #339966;">Last-Modified:&nbsp;<br /></strong></p><div>实体头，用于指定服务器认为当前变体的修改时间。</div>
<p><strong></strong><br />
</p>
<h2><strong style="color: #ff9900;">响应状态码详解</strong></h2>
<p><strong>1xx: Informational - Request received, continuing process</strong><br />
</p>
<div>这是一个临时性的响应，在HTTP/1.0协议中不存在，因而不可以向HTTP/1.0的客户端发送该状态码响应。客户端必须在获得正常响应之前能接收一个或多个1xx响应，即使它并没有预计会收到1xx响应。该响应码只有状态行和可选的响应消息头，没有响应消息实体。</div>
<ol style="margin-top: 1px;">
     <li><strong style="color: #339966;">100</strong><span style="color: #339966;"><strong> - Continue</strong><br />
     <div><span style="color: #000000;">客户端应该继续它的请求，这个暂时的响应用于通知客户端初始的请求已经被服务器接受，并且暂时没有被拒绝。此时客户端会继续发送剩余的请求，或者当所有请求已经发送完成时忽略该响应码。服务器必须在请求结束时发送一个最终的响应。</span></div>
     </span></li>
     <li><span style="color: #339966;"><strong>101 - Switching Protocols</strong><br />
     <div><span style="color: #000000;">服务器理解并打算执行客户端的请求，并且使用&#8220;Upgrade&#8221;字段头用户表示服务器会在这个连接中的协议升级到&#8220;Upgrade&#8221;头标识的版本号。</span></div>
     </span></li>
</ol>
<p><strong>2xx: Success - The action was successfully received, understood, and accepted<br />
</strong></p>
<div>这个系列的响应状态码表示客户端的请求已经成功的接收并处理。</div>
<ol style="margin-top: 1px;">
     <li><span style="color: #339966;"><strong>200 - OK</strong><br />
     <div><span style="color: #000000;">请求成功处理。</span></div>
     </span></li>
     <li><span style="color: #339966;"><strong>201 - Created</strong><br />
     <div><span style="color: #000000;">请求成功处理并且新的资源被创建。新创建的资源可以使用URI标识，并且该URI在响应消息的Location头中。服务器在返回201响应时必须保证新的资源已经被创建，如果服务器在返回响应时还没来得及创建新的资源，服务器应该返回202（Accepted）响应。</span></div>
     <div><span style="color: #000000;">201响应还可以包含&#8220;ETag&#8221;响应头，表示实体标签的当前值。</span></div>
     </span></li>
     <li><span style="color: #339966;"><strong>202 - Accepted</strong><br />
     <div><span style="color: #000000;">请求被接受并处理，但是处理还未完成。这个请求不一定被成功执行，并且也不会在有结果后重新异步发送响应消息。该响应状态主要用于一些类似batch的操作，当客户发送请求以后，不需要继续保持和服务器的连接。返回的消息实体需要包含请求当前的状态以及一个指向状态监视器或客户能得到结果的估计值。</span></div>
     </span></li>
     <li><span style="color: #339966;"><strong>203 - Non-Authoritative Information</strong><br />
     <div><span style="color: #000000;">响应消息实体头部返回的元信息不是在原始服务器有效的集合，而是从本地或第三方中拷贝收集。当前的集合可能是原始集合的子集或超集，这个响应码不是必须的，可以使用200（OK）替代。</span></div>
     </span></li>
     <li><span style="color: #339966;"><strong>204 - No Content</strong><br />
     <div><span style="color: #000000;">服务器已经成功的完成请求，该请求没有消息实体，只是返回一些最新的元信息。</span></div>
     </span></li>
     <li><span style="color: #339966;"><strong>205 - Reset Content</strong><br />
     <div><span style="color: #000000;">服务器已经成功的完成请求，客户端必须重置由该请求引起的文档视图。该响应主要用于清除用于之前输入的表单。该响应不可以包含消息实体。</span></div>
     </span></li>
     <li><span style="color: #339966;"><strong>206 - Partial Content</strong><br />
     <div><span style="color: #000000;">服务器已经成功完成&#8220;Partial GET&#8221;的请求。该响应的请求必须包含&#8220;Range&#8221;头，以及可选的&#8220;If-Range&#8221;头。响应必须包含以下头：Content-Range（或值为multipart/byteranges的Content-Type头）、Date、ETag或Content-Location、Expires、Cache-Control、Vary等。</span></div>
     </span></li>
</ol>
<p style="font-weight: bold;"><strong>3xx: Redirection - Further action must be taken in order to complete the request<br />
</strong></p>
<div>这个系列的状态码表示为了完成当前请求，客户端必须要有进一步的处理。如果接下来的请求方法是GET或者HEAD，客户端可以自行发送接下来的请求，而不需要用于干预。并且客户端应该能检测到死循环以减少网络的堵塞。</div>
<ol style="margin-top: 1px;">
     <li><span style="color: #339966;"><strong>300 - Multiple Choices</strong><br />
     <div><span style="color: #000000;">当前请求包含多个资源，并且在返回消息中包含每个资源的location信息。客户端可以根据一定的算法自行选择使用那个资源（没有定义算法）。服务器也可以指定一个推荐的选择（在Location头中），客户端可能会使用这个值重定向。</span></div>
     </span></li>
     <li><span style="color: #339966;"><strong>301 - Moved Permanently</strong><br />
     <div><span style="color: #000000;">请求的资源已经被永久的移动到一个新的URI上。客户端可以自动跳转到新的URI上。新的URI需要在响应消息的Location头中包含。</span></div>
     </span></li>
     <li><span style="color: #339966;"><strong>302 - Found</strong><br />
     <div><span style="color: #000000;">请求的资源临时的存在于另一个URI中。因为这个重定向还可能会改变，客户端需要继续使用旧的URI。临时的URI需要包含在响应消息的Location头中。</span></div>
     </span></li>
     <li><span style="color: #339966;"><strong>303 - See Other</strong><br />
     <div><span style="color: #000000;">请求的响应可以使用另一个URI中获得，并且必须使用GET方法获取另一个URI上的响应。该响应码主要用于将一个POST产生的输出重定向到一个新选择的资源上。新的URI需要在Location响应头中给出。</span></div>
     </span></li>
     <li><span style="color: #339966;"><strong>304 - Not Modified</strong><br />
     <div><span style="color: #000000;">如果客户端发送一个&#8220;Conditional GET&#8221;请求，并且该请求是被允许的，但是它所对应的文档没有改变，则服务器返回该响应。该响应不能包含消息体，但必须包含一些消息头：Date、ETag、Content-Location、Expires、Cache-Control、Vary。</span></div>
     </span></li>
     <li><span style="color: #339966;"><strong>305 - Use Proxy</strong><br />
     <div><span style="color: #000000;">请求的资源必须通过Proxy使用Location响应头中的URI访问。<br />
     </span></div>
     </span></li>
<li><span style="color: #339966;"><strong>306 - Unused<br /></strong><div><span style="color: #000000;">以前版本使用，现在已经不使用，但是响应码保留。</span></div>
     </span></li><li><span style="color: #339966;"><strong>307 - Temporary Redirect</strong><br /><div><span style="color: #000000;">请求的资源临时的指向另一个URI，但是由于这个重定向可能会在将来被更改，因而客户端需要继续使用原来的URI。临时的URI在Location响应头中指定。</span></div>
     </span></li>
</ol>
<p style="font-weight: bold;"><strong>4xx: Client Error - The request contains bad syntax or connote be fulfilled<br /></strong></p><div>这个系列的响应码用于表示客户端错误请求，并且在响应实体消息中需要包含出错原因的解释（对HEAD的响应除外）。</div>
<ol style="margin-top: 1px;">
     <li><span style="color: #339966;"><strong>400 - Bad Request</strong><br /><div><span style="color: #000000;">语法错误，请求不能被服务器理解。</span></div></span></li>
     <li><span style="color: #339966;"><strong>401 - Unauthorized</strong><br /><div><span style="color: #000000;">请求需要包含用于认证。响应必须包含WWW-Authenticate头，包含请求认证需要的信息。客户端可以使用包含Authorization头重新发送请求。</span></div></span></li>
     <li><span style="color: #339966;"><strong>402 - Payment Required</strong><br /><div><span style="color: #000000;">为将来使用保留。</span></div></span></li>
     <li><span style="color: #339966;"><strong>403 - Forbidden</strong><br /><div><span style="color: #000000;">服务器拒绝该请求。如果服务器希望让客户端知道拒绝的原因，可以将原因放在响应消息体重，如果服务器想暴露该原因，则可以返回404（Not Found）响应。</span></div></span></li>
     <li><span style="color: #339966;"><strong>404 - Not Found</strong><br /><div><span style="color: #000000;">服务器没有发现任何匹配的请求URI。如果服务器知道某些资源已经永久的被移出，并且没有重定向地址，则需要返回410（Gone）响应。该响应也可以用于服务器不想暴露客户请求被拒绝的原因。</span></div></span></li>
     <li><span style="color: #339966;"><strong>405 - Method Not Allowed</strong><br /><div><span style="color: #000000;">请求方法不被对请求的资源允许。在响应消息中必须包含Allow头，指定请求资源允许的请求方法。</span></div></span></li>
     <li><span style="color: #339966;"><strong>406 - Not Acceptable</strong><br /><div><span style="color: #000000;">请求的资源产生的响应包含了不被Accept请求头指定的特性。</span></div></span></li>
     <li><span style="color: #339966;"><strong>407 - Proxy Authentication Required</strong><br /><div><span style="color: #000000;">类似401（Unauthorized），表示客户端必须在Proxy中通过认证。Proxy必须返回Proxy-Authenticate头，包含请求认证需要的信息。</span></div></span></li>
     <li><span style="color: #339966;"><strong>408 - Request Time-out</strong><br /><div><span style="color: #000000;">服务器已经准备好并在等待，但是客户端在指定的时间里没有发送请求。</span></div></span></li>
     <li><span style="color: #339966;"><strong>409 - Conflict</strong><br /><div><span style="color: #000000;">因为和资源当前状态冲突而导致请求没有完成。该响应码只有在用户知道任何解决这个冲突，并且重新提交请求时产生。</span></div></span></li>
     <li><span style="color: #339966;"><strong>410 - Gone</strong><br /><div><span style="color: #000000;">请求的资源已经不在服务器上，并且没有更进一步的重定向地址。</span></div></span></li>
     <li><span style="color: #339966;"><strong>411 - Length Required</strong><br /><div><span style="color: #000000;">请求消息必须包含Content-Length消息头。</span></div></span></li>
     <li><span style="color: #339966;"><strong>412 - Precondition Failed</strong><br /><div><span style="color: #000000;">服务器对一个或多个请求消息头的测试失败。</span></div></span></li>
     <li><span style="color: #339966;"><strong>413 - Request Entity Too Large</strong><br /><div><span style="color: #000000;">请求消息太大。如果这个条件是临时的，则服务器需要包含Retry-After响应头，表示这个响应时临时的，并在指定的时间以后重试。</span></div></span></li>
     <li><span style="color: #339966;"><strong>414 - Request-URI Too Large</strong><br /><div><span style="color: #000000;">请求的URI太长。</span></div></span></li>
     <li><span style="color: #339966;"><strong>415 - Unsupported Media Type</strong><br /><div><span style="color: #000000;">请求消息格式不被支持。</span></div></span></li>
     <li><span style="color: #339966;"><strong>416 - Request range not satisfiable</strong><br /><div><span style="color: #000000;">在请求包含Range头，不包含If-Range头，并且请求的资源不在Range指定的范围中。响应头中需要包含Content-Range表示指定资源当前的长度。</span></div></span></li>
     <li><span style="color: #339966;"><strong>417 - Expectation Failed</strong><br /><div><span style="color: #000000;">Expect请求头指定的值不能匹配服务器的逻辑。</span></div></span></li>
</ol>
<p style="font-weight: bold;"><strong>5xx: Server Error - The server failed to fulfill an apparently valid request<br /></strong></p><div>这个系列的响应码用于表示服务器存在错误，不能完成相应的请求。服务器需要在响应消息体中包含出错的描述信息。</div>
<ol style="margin-top: 1px;">
     <li><span style="color: #339966;"><strong>500 - Internal Server Error</strong><br /><div><span style="color: #000000;">服务器内部错误。</span></div></span></li>
     <li><span style="color: #339966;"><strong>501 - Not Implemented</strong><br /><div><span style="color: #000000;">服务器没有实现当前请求。如没有实现对应的请求方法。</span></div></span></li>
     <li><span style="color: #339966;"><strong>502 - Bad Gateway</strong><br /><div><span style="color: #000000;">代理或网关服务器从上游服务器中接收到一个不合法的响应。</span></div></span></li>
     <li><span style="color: #339966;"><strong>503 - Service Unavailable</strong><br /><div><span style="color: #000000;">服务器因为临时负载过重或处于维护状态而不能处理请求。该响应暗示服务器当前的状态是临时的，如果服务器知道什么时候恢复可用状态，则可以包含Retry-After响应头，如果没有包含Retry-After头，则客户端可以把它视为500（Internal Server Error）来处理。</span></div></span></li>
     <li><span style="color: #339966;"><strong>504 - Gateway Time-out</strong><br /><div><span style="color: #000000;">代理服务器或网关服务器在指定的时间内没有收到上游服务器的响应。</span></div></span></li>
     <li><span style="color: #339966;"><strong>505 - HTTP Version not supported</strong><br /><div><span style="color: #000000;">服务器不支持或拒绝支持请求消息中指定的HTTP版本。</span></div></span></li>
</ol>
<br />
<strong style="font-weight: bold;">参考：</strong><br />
RFC2616<br />
RFC1867<br />
<div>http://blog.zhaojie.me/2011/03/html-form-file-uploading-programming.html<br />
<div>http://www.cnblogs.com/li0803/archive/2008/11/03/1324746.html<br />
<div>http://www.360doc.com/content/10/0930/17/3668821_57590979.shtml</div>
</div>
</div><img src ="http://www.blogjava.net/DLevin/aggbug/409906.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2014-03-17 18:26 <a href="http://www.blogjava.net/DLevin/archive/2014/03/17/409906.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>