我们知道了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是怎么被tomcat抓住的?/span>
中期Q讲解了被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不方ѝ?
所以,在tomcat 的Connector里面Q?socket被封装成了Request和Responseq两个对象?/span>
我们可以单地把Request看成发到服务器来的数据Q把Response看成惛_出服务器的数据?/span>
但是q样又有其他问题了啊Q?Requestq个对象是把socket装h了, 但是他提供的又东西太多了?/span>
诸如Request.getAuthorization(), Request.getSocket()?nbsp;像Authorizationq种东西开发h员拿来基本上用不太着Q而像socketq种东西Q暴露给开发h员又有潜在的危险?而且啊, 在Servlet Specification里面标准的通信cLServletRequest和HttpServletRequestQ而非q个RequestcR?So, So, So. Tomcat必须得捣持捣持Request才行?最后tomcat选择了用捣持模式(应该叫适配器模式)来解册个问题。它把org.apache.catalina.Request 捣持成了 org.apache.coyote.tomcat4.CoyoteRequest?而CoyoteRequest又实CServletRequest和HttpServletRequest q两U接口?q样提供给开发h员需要且刚刚需要的Ҏ了?/span>
ok, ?/span>我们?tomcat的顶层容?- StandardEngin 的invoke()Ҏ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,最后一个地Ҏ点像那里~ Q?/span>
OKQ?让我们具体看看都有些什么容器, 各个容器里面又都有些什么阀门,q些阀们都Ҏ们的Request做了些什么吧Q?/span>
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. Ҏ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。也可以Ҏ地调用servlet的init()和destroy()Ҏ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个Ҏ.
如果?JspServletQ?那么先把jsp文g~译成servlet_xxx, 再invoke servlet_xxx的servie()Ҏ?/span>
如果?DefaultServletQ?q接找到静态资源,取出内容Q?发送出厅R?/span>
如果?InvokerServletQ?p用那个具体的servlet的service()Ҏ?/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>