我们知道?strong style="color: black; background-color: #ffff66;">tomcat的整体框架了Q?也明白了里面都有些什么组Ӟ 以及各个lg是干什么用的了?/span>
http://www.csdn.net/Develop/read_article.asp?id=27225
我想Q接下来我们应该M解一?tomcat 是如何处理jsp和servleth的?/span>
1. 我们以一个具体的例子Q来跟踪TOMCATQ?/span>看看它是如何?/span>Request一层一层地递交l下一个容器,q最后交l?/span>Wrapper来处理的?/span>
?/span>http://localhost:8080/web/login.jspZ?/span>
Q以下例子,都是?/span>tomcat4 源码为参考)
q篇心得主要分ؓ3个部分: 前期Q?中期Q?和末期?/span>
前期Q讲解了在浏览器里面输入一个URLQ是怎么?strong style="color: black; background-color: #ffff66;">tomcat抓住的?/span>
中期Q讲解了?strong style="color: black; background-color: #ffff66;">tomcat抓住后,又是怎么在各个容器里面穿梭, 最后到达最后的处理地点?/span>
末期Q讲解到达最后的处理地点后,又是怎么具体处理的?/span>
2?/span> 前期 Request的born.
在这里我先简单讲一下requestq个东西?/span>
我们先看着q个URLQ?/span>http://localhost:8080/web/login.jsp 它是动用?080端口来进行socket通讯的?/span>
我们知道, 通过
InputStream in = socket.getInputStream() ?/span>
OutputStream out = socket.getOutputStream()
可以实现消息的来来往往了?/span>
但是如果把Streaml应用层看,昄操作h不方ѝ?
所以,?strong style="color: black; background-color: #ffff66;">tomcat 的Connector里面Q?socket被封装成了Request和Responseq两个对象?/span>
我们可以单地把Request看成发到服务器来的数据Q把Response看成惛_出服务器的数据?/span>
但是q样又有其他问题了啊Q?Requestq个对象是把socket装h了, 但是他提供的又东西太多了?/span>
诸如Request.getAuthorization(), Request.getSocket()?nbsp;像Authorization q种东西开发h员拿来基本上用不太着Q而像socketq种东西Q暴露给开发h员又有潜在的危险?而且啊, 在Servlet Specification里面标准的通信cLServletRequest和HttpServletRequestQ而非q个RequestcR? So, So, So. Tomcat必须得捣持捣持Request才行?最?strong style="color: black; background-color: #ffff66;">tomcat选择了用捣持模式(应该叫适配器模式)来解册个问题。它把org.apache.catalina.Request 捣持成了 org.apache.coyote.tomcat4.CoyoteRequest?而CoyoteRequest又实CServletRequest和HttpServletRequest q两U接口?q样提供给开发h员需要且刚刚需要的Ҏ(gu)了?/span>
ok, ?/span>我们?tomcat的顶层容?- StandardEngin 的invoke()Ҏ(gu)q里讄一个断点, 然后讉K
http://localhost:8080/web/login.jspQ?我们来看看在前期都会路过哪些地方Q?/span>
1. run(): 536, java.lang.Thread, Thread.java
CurrentThread
2. run():666, org.apache.tomcat.util.threads.ThreadPool$ControlRunnable, ThreadPool.java
ThreadPool
3. runIt():589, org.apache.tomcat.util.net.TcpWorkerThread, PoolTcpEndpoint.java
ThreadWorker
4. processConnection(): 549
org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler, Http11Protocol.java
http protocol parser
5. Process(): 781, org.apache.coyote.http11.Http11Processor, Http11Processor.java
http request processor
6. service(): 193, org.apache.coyote.tomcat4.CoyoteAdapter,CoyoteAdapter.java
adapter
7. invoke(): 995, org.apache.catalina.core.ContainerBase, ContainerBase.java
StandardEngin
1. ȝE?/span>
2. 启动U程?
3. 调出U程池里面空闲的工作U程?/span>
4. ?080端口传过来由httpd协议装的数据,解析成Request和Response对象?/span>
5. 使用Http11Processor来处理request
6. ?/span>Http11Processor里面Q?又会call CoyoteAdapter来进行适配处理Q把Request适配成实CServletRequest和HttpServletRequest接口?/span>CoyoteRequest.
7. Cq里Q前期的L拔皮工作基本上搞定Q可以交l?/span>StandardEngin 做核心的处理工作了?/span>
3. 中期?在各个容器间的穿梭?/span>
Request在各个容器里面的I梭大致是这样一U方式:
每个容器里面都有一个管道(piplineQ, 专门用来传送Request用的?/span>
道里面又有好几个阀门(valveQ, 专门用来qoRequest用的?/span>
在管道的低部通常都会放上一个默认的阀们?q个阀们至会做一件事情,是把Request交给子容器?/span>
让我们来惌一下:
当一个Requestq入一个容器后Q?它就在管道里面流动,波罗~ 波罗~ 波罗~ 地穿q各个阀门。在到最后一个阀门的时候,吧唧~ 那个该死的阀门就把它扔给了子容器?然后又开?波罗~ 波罗~ 波罗~ ... 吧唧~.... 波罗~ 波罗~ 波罗~ ....吧唧~....
是通过q种方式Q?Request 走完了所有的容器。( 感觉有点像消化系l,最后一个地Ҏ(gu)点像那里~ Q?/span>
OKQ?让我们具体看看都有些什么容器, 各个容器里面又都有些什么阀门,q些阀们都Ҏ(gu)们的Request做了些什么吧Q?br />3.1 StandardEngin 的pipeline里面攄是:StandardEnginValve
在这里,VALVE做了三g事:
1. 验证传递过来的request是不?/span>httpservletRequest.
2 验证传递过来的 request 是否携带?/span>host header信息.
3 选择相应?/span>hostd理它。(一般我们都只有一个host:localhostQ也是127.0.0.1Q?/span>
Cq个地方Q?/span>我们?/span>request已l完成了?/span>Enginq个部分的历史命,通向前途未卜的下一站: host了?/span>
3.2 StandardHost 的pipline里面攄是: StandardHostValve
1. 验证传递过来的request是不?/span>httpservletRequest.
2. Ҏ(gu)Request来确定哪?/span>Context来处理?/span>
Context其实是webappQ?/span>比如http://localhost:8080/web/login.jsp
q里web是Context|!
3. 既然定了是哪个Context了,那么应该把那个Context?/span>classloader付给当前U程了?/span>
Thread.currentThread().setContextClassLoader(context.getLoader().getClassLoader());
q样request只看得见指定的context下面?/span>classes啊, jar啊这些,而看不见tomcat本n的类Q?/span>什?/span>Engin啊, Valve啊?/span>不然q得了啊Q?/span>
4. 既然requestCq里了,看来用户是准备访问webq个web app了,咋们得更C下这个用Lsession不是Q?Ok , qmanager更新一下用Lsession信息
5. 交给具体的Context 容器ȝl处理Request.
6. Context处理完毕了,?/span>classloaderq回来?/span>
3.3 StandardContext 的pipline里面攄是: StandardContextValve
1. 验证传递过来的request是不?/span>httpservletRequest.
2. 如果request意图不轨Q想要访?/span>/meta-inf, /web-infq些目录下的东西Q呵呵,没有用D!
3. q个时候就会根?/span>Request到底?/span>ServletQ?/span>q是jspQ?/span>q是静态资源来军_到底用哪U?/span>Wrapper来处理这?/span>Reqeust了?/span>
4. 一旦决定了到底用哪U?/span>WrapperQ?/span>OKQ交l那?/span>Wrapper处理?/span>
4. 末期?不同的需求是怎么处理?
StandardWrapper
之前对Wrapper没有做过讲解Q其实它是这样一U东ѝ?/span>
我们在处理Request的时候,可以分成3U?/span>
处理静态的Q?org.apache.catalina.servlets.DefaultServlet
处理jsp的:org.apache.jasper.servlet.JspServlet
处理servlet的:org.apache.catalina.servlets.InvokerServlet
不同的requestqq?U不同的servletd理?/span>
Wrapper是对它们的一U简单的装Q有了Wrapper后,我们可以轻村֜拦截每次的Request。也可以Ҏ(gu)地调用servlet的init()和destroy()Ҏ(gu)Q?便于理嘛!
具体情况是这么滴Q?/span>
如果request是找jsp文gQStandardWrapper里面׃装一个org.apache.jasper.servlet.JspServletd理它?/span>
如果request是找 静态资?QStandardWrapper里面׃装一个org.apache.jasper.servlet.DefaultServlet d理它?/span>
如果request是找servlet QStandardWrapper里面׃装一个org.apache.jasper.servlet.InvokerServlet d理它?/span>
StandardWrapper同样也是容器Q既然是容器Q?那么里面一定留了一个管道给requestȝQ管道低部肯定也有一个阀??)Q用来做最后一道拦截工?
在这最底部的阀门里Q其实就主要做了两g?
一是启动过滤器Q让request在N个过滤器里面{一通,如果OKQ?那就PASS?否则p到其他地方去了?/span>
二是servlet.service((HttpServletRequest) request,(HttpServletResponse) response); q个Ҏ(gu).
如果?JspServletQ?那么先把jsp文g~译成servlet_xxx, 再invoke servlet_xxx的servie()Ҏ(gu)?/span>
如果?DefaultServletQ?q接找到静态资源,取出内容Q?发送出厅R?/span>
如果?InvokerServletQ?p用那个具体的servlet的service()Ҏ(gu)?/span>
ok! 完毕?/span>
?: StandardWrapper 里面的阀门是最后一道关口了?如果q个阀门欲意把request交给StandardWrapper 的子容器处理?对不P 在设计考虑的时候, Wrapperp考虑成最末的一个容器, 压根儿就不会lWrapperd子容器的ZQ? 如果是要调用addChild(), 立马抛出IllegalArgumentExceptionQ?/span>
参考:
<http://jakarta.apache.org/tomcat/>
<http://www.onjava.com/pub/a/onjava/2003/05/14/java_webserver.html>
?span style="background: #ffff66 none repeat scroll 0% 50%; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">Tomcat中得到更多-Tomcat的源?span style="background: #99ff99 none repeat scroll 0% 50%; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">分析
赉|?/span>
本文的作者通过?strong>TomCat源代码的研究Q向读者描qC?strong>tomcat3.3?strong>TomCat4.0中在设计斚w使用的不同设计理念和模式Q希望对q大开发者在设计自己的系l时有所帮助?/span>
关于Tomcat的基本情?/span>
众所周知Tomcat是一个免费的开放源码的Serlvet容器Q它是Apache基金会的Jakarta目中的一个核心项目,也是sun公司官方推荐的servlet和jsp容器Q同时它q获得过多种荣誉。servlet和jsp的最新规范都可以?strong>tomcat的新版本中得到实现?strong>Tomcath轻量U和灉|嵌入到应用系l中的优点,所以得Cq泛的应用。在Tomcat的发展中QSun?999q六月宣布参与Jakarta目?strong>Tomcat servlet容器和Jsp引擎的开发,使得Tomcat?.x?.x版之间系l设计上发生了比较大的变化?strong>Tomcat的其他信息我׃多说了。有兴趣的朋友可以访问http://jakarta.apache.org/ 的官方网站得到更多的信息?/span>
因ؓ工作的原因,我改写了Tomcat的一些代码,所以我_略的研I了一?strong>Tomcat3.3?strong>Tomcat4.0的源码,深深地被q个开放Y件的设计和实现吸引,感觉到这个Y件中有许多值得我们学习和借鉴的地斏V我把自q理解介绍l大家算是抛砖引玉,不和偏差还望大家批评指正。下面就来让我们看看? Tomcat那里我们可以得到什么?/span>
?span style="background: #ffff66 none repeat scroll 0% 50%; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">Tomcat中学习设计模?/span>
Tomcat的设计和实现处处体现着设计模式的思想Q它的基本流E是首先通过解析xml格式的配|文Ӟ获得pȝ的配|和应用信息Q然后加载定制的lg模块提供各种pȝ服务。系l的各部分功能都是通过可以配置的组件模块来实现的?strong>Tomcat实现中像ObserverQFacadeQAdapterQ?Singleton{多U设计模型在Tomcat的源码中随处可见Qؓ我们提供了一个很好的学习设计模式的^台。我主要介绍一?strong>Tomcat中程序流E控制所采用的设计模式,q是一个程序运行的框架。前面提到由于Sun公司的参与,Tomcat虽然基本的流E没有变化,但是Tomcat3.3?Tomcat4.0版本之间在概念上q是有很大地不同的?strong>Tomcat3.3整体上是模块化的设计Q?strong>Tomcat4.0可以看作是采用面向组件技术进行设计的。组件是比模块更高的一个层ơ。我们可以通过比较它们之间的不同来了解实现一个服务器软g可以采用的设计模式和实现方式?/span>
Tomcat3.3的基?span style="background: #a0ffff none repeat scroll 0% 50%; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">l构设计
Tomcat3.3采用的是一U模块化的链状的控制l构Q它的主要设计模式有Q?/span>
Chain of responsibilityQ责任链Q?/span>
作ؓ一个基于请求响应模式的服务器,?strong>Tomcat3.3中采用一U链状处理的控制模式。请求在链上的各个环节上传递,在Q一环节上可以存在若q个"监听?处理它。这样做的目的是避免h的发送者和接受者之间的直接耦合Q从而可以ؓ其他的对象提供了参与处理h的机会。采用这个方式不但可以通过"监听?"实现pȝ功能Q而且可以通过d新的"监听?对象实现pȝ功能的扩展?/span>
InterceptorQ监听器Q?/span>
"监听?是一个过M用的名称Q它可以看作 "模块(module)"的同义词。它?strong>Tomcat功能模块构徏和扩展的方式?strong>Tomcat3.3的大部分功能都是通过"监听?实现的。在 Tomcat中提供了一U简单的钩子QHookQ机Ӟ监听器对钩子中感兴趣的事件进行注册,事g发生后通过钩子唤醒已注册的"监听?对象Q?监听? 对象?strong>Tomcat内核事gq行处理。这些模块都是围l着"责Q??{略"的模式进行设计。通过"监听?你可以监听各U特D事Ӟq而控制处理请求的各个步骤---解析Q认证,授权Q会话,响应提交Q缓冲区提交{等?/span>
StrategyQ策略)
所谓策略是?定义一l规则,按照规则q行对象装Q得他们只在规则内部进行交?。通过{略模式使得Tomcat作ؓ一个开源项目在开攄境下的开发和演变变得更轻松。通过q种模式把复杂的法分成模块然后不同的开发组提供各自的实现。从而实现调用模块的代码和模块的具体实现代码的分别开发。这样可以我们专注于问题的重点Qƈ且减问题之间的依赖性。在Tomcat中大量采用了{略的设计模式,通过q种方式每一U服务都提供了多U的实现Q例?strong>Tomcat中有2Q?U认证模块)Q在代码完成后可以从E_性和性能表现的考虑选择更好的实现。策略模式对开攑ּ环境下的软g开发非常有用?/span>
我们通过化的cd(见图一)和时序图(见图?Q描qC?strong>Tomcat3.3的程序流E控制如何通过监听器和责Q铑֮现?/span>
?1.?化的cd
关于cd的简单说明:
BaseInterceptorQ?/span>
是所有监听器的基c,描述了基本的模块功能和对各种事g的缺省处理?/span>
ContextManageQ?/span>
pȝ的核心控制对象,q行h处理和系l配|。它l护了全局的属性、web应用的内容和全局模块{多U信息,责Q铄钩子实现也在其中?/span>
PoolTcpConnectorQ?/span>
一个处理TCPq接的连接器对象Q从BaseInterceporz。它包括一个具体处理socketq接的PoolTcpEndPointcd象?/span>
PoolTcpEndPointQ?/span>
处理实际的tcpq接。它有一个连接池对象ThreadPool和运行在独立U程中的应用逻辑cTcpWorkThread?/span>
TcpWorkTheadQ?/span>
处理socketq接事务Q调用接口TcpConnectionHandler中的h处理Ҏ(gu)?/span>
Http10InterceptorQ?/span>
从PoolTcpConnectorzQ实CTcpConnectionHandler接口Q是一个真正的监听器对象。它按照Http1.0的协议标准对tcpq接q行处理Q调用核心对象ContextManage的服务方法?/span>
?2.?化的时序?/span>
关于时序图中需要说明的地方Q?/span>
4. BaseInterceptor ri[];
5. //取得注册对象
6. ri=defaultContainer.getInterceptors(Container.H_postReadRequest);
7. //执行注册对象的对消息的处理方?/span>
for( int i=0; i< ri.length; i++ ) { status=ri[i].postReadRequest( req ); ......}
Tomcat3.3的基本程?strong>l构是采用上面介绍的方式设计的。它l我们的设计和开发提供了一个很好的思\Q通过q种模式可以L的实C个事仉动的Z模块化设计的应用E序。各个功能通过模块实现Q通过对责任链上的消息和处理步骤的改动或者添加新的监听器对象可以非常方便的扩?strong>Tomcat的功能。所以这是一个非常好的设计?/span>
Tomcat4.0的基?span style="background: #a0ffff none repeat scroll 0% 50%; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">l构设计
虽然Tomcat3.x已经实现了一个非常好的设计体p,但是在Sun公司加入后, Tomcat4.0中还是引入了不同的实现方式。主要的区别?strong>Tomcat4.0采用了面向组件的设计方式Q?Tomcat4.0中的功能是由lg提供的,控制程通过lg之间的通讯完成。这不同?strong>Tomcat3.3中的Z模块的链式控?strong>l构?/span>
?向组件的技?CO)是比面向对象的技?OOP)更高一层的抽象Q它融合了面向对象的优点Q加入了安全性和可扩展的模块设计Q可以更好的映射问题域空 间。采用面向组件的设计会带来很多好处,可以提高复用性、降低耦合度和通过l装构成pȝ{等。面向组件编E中有许多概念与原来面向对象的编E是不同的,?如:
Message(消息)Q定义抽象操作; Method(Ҏ(gu))Q定义具体的操作Q?Interface(接口)Q一l消息的集合Q?Implementation(实现)Q一l方法的集合Q?Module(模块)Q静态的数据l构, Type(cd)Q动态的数据l构?/span>
软glg不同与功能模块,它具有以下特性:
?span>Java 语言中对面向lg~程的支持是通过JavaBeans模型获得的。JavaBeanlg框架提供了对事g和属性的支持?strong>Tomcat4.0的组件的是通过JavaBean技术实现的。这是它?strong>Tomcat3.3中最大的不同。下面我们来看一?strong>Tomcat4.0是如何通过面向lg~程来实现程序流E控制的?/span>
面向lg~程时设计组件是关键Q从Tomcat4.0中可以看Z要用了以下的设计模式:
Separation of ConcernsQSOCQ?/span>
设计lg时应该从不同的问题领域,站在不同的观点上分析Q把每一U属性分别考虑。D一个例子FileLoggerlgQ它用于把系l日志信息保存到文gpȝ中。按照这U模?strong>分析Q我们从不同的角度看待它Q它如何启动服务、停止服务和q行通讯Q它的具体的功能有哪些?别的lg可以发给它哪些消息?Zq些考虑QFileLoggerlg应该实现两种接口QLifecycleQ生存期接口Q和LoggerBaseQ功能接口)?/span>
Inversion of ControlQIOCQ这个模式定义的是,lgL通过外部q行理的。组仉要的信息L来源于外部,实际上组件在生存期的各个阶段都是被创建它的组件管理的。在Tomcat4.0中就是通过q种lg之间的相互控制和调用实现各个功能的?/span>
按照q些模式分析得到?strong>Tomcat4.0中的lg是既有共性又有特性。共性是Lifecycle接口Q特性是不同的功能接口。其中生存期接口处理lg的整个生存期中的各个阶段的事?功能接口提供l其他的lg使用?/span>
具体的功能如何实现我在这里不多介l了Q我主要介绍一?strong>Tomcat4.0中组件的Lifecycle接口的设计。Lifecycle接口实现了组件的生存期管理,控制理和通讯。创Z个Y件组件和创徏一个JavaBean对象一P可以参考JavaBeanq行理解。我通过一个模拟的 Lifecycle接口lg的类图来描述?见图?
?3.?Lifecycle接口lgcd
Ҏ(gu)拟的Lifecycle接口lg的类囄说明
通过上面?strong>分析我们可以看到lg成ؓTomcat4.0中的核心概念Q系l的功能都是通过lg实现的,lg之间的通讯构成了系l的q行控制机制。我们把 Tomcat3.3中模块化的链状控制机制和Tomcat4.0的面向组件的设计q行比较Q就会发?strong>Tomcat4.0在设计思想上Y件组件的概念非常明确?strong>Tomcat4.0?strong>Tomcat3.3最主要的区别就在于此。至于面向对象和面向lg的关pd区别Q我在这里就不介l了Q有兴趣的朋友可以找到很多这斚w的资源?/span>
?span style="background: #ffff66 none repeat scroll 0% 50%; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">Tomcat源码中得到高效的软glg
Tomcat不但为我们提供了设计和实现系l时的新思\Q同时因为它是由lg或者模块构成的Q所以它qؓ我们提供了大量可用的高效软glg。这些组仉可以在我们的E序开发中使用。我单列举一些,需要时可以直接从源码中取得?/span>
通过以上?strong>Tomcat的简单的介绍Q我们可以看出,作ؓ一个开放源码的目Q?strong>Tomcat不但为我们提供了一个应用的q_Q同时它qؓ我们提供了一个学习和研究设计模式、面向组件技术等理论的实践^台?/span>