您的位置:首页 > 其它

Servlet工作原理解析

2016-02-24 11:14 531 查看
4000
目录  ---写在前面---Servlet的使用与侧重点---Servlet的工作原理 a---Servlet容器怎样工作(以Tomcat为例) b---Web应用在servlet容器中如何启动 c---Servlet容器怎样解析web.xml中定义的servlet d---Servlet容器怎样管理servlet生命周期 e---用户的请求是怎样分配到指定servlet进行处理的
写在前面: 现在Java Web开发中已经很少单纯用到Servlet去开发一个Web应用了,比较多的是整合几个框架去开发。从而很多介绍Java Web开发的书籍对Servlet都是一略而过,仅仅介绍一下生命周期和几个常用方法即止。实际上,大多框架都是基于基础工具去封装起来的,像Struts就是对servlet与filter的封装,Hibernate对JDBC的封装等等。所以,在学会框架的使用之后(在没有深入了解基础类的工作原理情况下),应该深入了解一下这些基础知识。从而对框架中出现的问题有一个理性的认识与解决。 说Servlet工作原理之前先说说Servlet的使用吧。先描绘一个大致的外在功能轮廓,再深入到内在的工作原理。使用: 继承GenericServlet抽象类或HttpServlet抽象类(一般继承后者),然后重写下面方法的其中一个。即可完成一个Servlet 注意: servlet 通常运行在多线程服务器上,因此应该意识到 servlet
必须处理并发请求并小心地同步对共享资源的访问。享资源包括内存数据(比如实例或类变量)和外部对象(比如文件、数据库连接和网络连接)。


doGet
,如果 servlet 支持 HTTP GET 请求

doPost
,用于 HTTP POST 请求

doPut
,用于 HTTP PUT 请求

doDelete
,用于 HTTP DELETE 请求

init
destroy
,用于管理 servlet 的生命周期内保存的资源

getServletInfo
,servlet 使用它提供有关其自身的信息 

侧重点: Servlet规范有2个包,javax.servlet和java.servlet.http两个包加起来不过34个接口与类,因此开发人员应该通过J2EE API文档熟知每个类与接口的具体意思。特别偏重下面几个(HttpServletRequest与HttpServletResponse最重要)


HttpServlet
ServetConfig
ServletContext
Filter
FilterConfig
FilterChain
RequestDispatcher
HttpServletRequest
HttpServletResponse
HttpSession

一些 Listenser 类

Servlet工作原理:1---Servlet容器怎样工作(以Tomcat为例)2---Web应用在servlet容器中如何启动3---Servlet容器怎样解析web.xml中定义的servlet4---Servlet容器怎样管理servlet生命周期5---用户的请求是怎样分配到指定servlet进行处理的现在从上面几个问题去解析Servlet的工作原理:  1、什么是容器?容器就是程序运行时需要的环境。Tomcat是servlet的运行环境,所以Tomcat是servlet容器。 那么Tomcat是怎样启动的呢?说到这里需要先介绍一下Servlet容器和Web容器的区别。Sevrlet容器是用来管理servlet的生命周期,而web容器,即web服务器是用来管理和部署Web应用的。Tomcat就是一个开源的Servlet容器,也是一个web容器---用于处理静态html,css等。接着介绍一下Tomcat的架构,如下图。

1.server层代表整个servlet容器,用于启动与监听服务端事件2.service是由一个engine和一个或多个connector组成,这些connector共享一个Engine来处理请求3.connector将在某个指定端口监听客户的请求,把从socket传送来的数据封装成request传递给engine,并从engine处获得响应返回给客户。Tomcat通常会用到两种Connector: a) Http Connector 在端口8080处侦听来自客户browser的http请求。 
b) AJP Connector 在端口8009处侦听来自其它WebServer(Apache)的servlet/jsp代理请求。 3.engine负责处理来自相关联的service的所有请求,处理后返回给service,connector作为两者中间媒介出现   engine下可以配置多个虚拟主机,当engine获得一个请求时将这个请求匹配给对应的虚拟主机上处理4.Host虚拟主机与某个网络域名(domain name)相匹配。一个主机下可以部署一个或者多个web应用,每个应用对应一个context,有一个context path。当host获得一个请求时将这个请求匹配到某个context上,将请求交给context处理,这种方法叫最长匹配。path==“”时即匹配所有无法匹配到context的请求。5.context对应一个应用,由一个或多个servlet组成。context创建时将根据web.xml载入servlet类。     了解完tomcat的架构,然后介绍tomcat是怎样启动的。对于engine, host, context来说,它们都属于容器,当接收到客户端请求的时候,请求会被传递到容器中,在一个容器中处理完毕之后,会被传递给下一个容器处理。因此,我们可以这样理解tomcat,总的来说,tomcat就是一种自上而下,一个容器里面又嵌套包含了另一个子容器的结构。所以,在tomcat启动的时候,我们也可以想象,它必定要先启动父容器,然后再启动子容器,在启动每一层容器的时候,还会启动容器中的一些相关组件,当所有的容器与组件都安装启动完毕,那么tomcat就启动完毕了。 
 因此,很容易理解,tomcat 启动的第一步就是进行容器的装配,就是把父容器和子容器拼装起来,并且安装上相关的组件,这很像一个车间装配的过程。 

当一切装配齐全,机器已经在各个工人的手中完全组装好了,那么接下来的一步,我们只需要按下开关,机器就可以工作啦。多么方便哪! 

1、 一切事情的起点都源于org.apache.catalina.startup.Bootstrap的“引导”。Bootstrap负责对catalina的配置文件路径进行了一番指导,指定了三种类型的classLoader,接下来catalina就可以用这三种类型的classLoader来负责装配容器了。然后Bootstrap用反射机制调用了org.apache.catalina.startup.Catalina的process方法,引导catalina进行启动。 

2、 Catalina的工作首先是用digester来装配各个容器与组件(degester是Jakarta子项目Commons下的一个模块,支持基于规则的对任意XML文档的处理,提供了一种将XML与Java对象进行映射的方便方法),这个装配就像我们上面说的那样,就是把server下的service进行安装,然后依次把service下的engine,host,context这些容器以及容器中的各种组件按照父子关系一一拼装。这些配置文件的来源都是Bootstrap之间就已经告知了的。在这里它只负责组装。 
接着,catalina会对server进行初始化工作,主要就是把service中配置的connector进行初始化(HTTP与AJP)。 
然后调用server的start方法,启动tomcat server。 
最后,为server注册一个hook程序,检测当server shutdown的时候,关闭tomcat的各个容器。 

3、 进入server的start方法。 
启动server的容器的三个lifecycle事件:BEFORE_START_EVENT,START_EVENT,AFTER_START_EVENT。 
启动server的子容器service。 

4、 进入service的start方法。启动engine与connector。 

5、 下面就开始进入engine了。 
之前说过,engine, host与context都是容器,它们都继承自Container类。它们既然都是一种container,那么在处理手法上一定又很多类似的地方,因此,tomcat使用了ContainerBase这个类,把它作为engine, host与context的父类,让这些容器都可以通过super.start()方法来达到大部分主要逻辑的复用。 

那么,我们首先就来看一下这个ContainerBase中都做了些什么,也就可以知道容器大致都怎么处理请求的。 
a) 触发启动前事件(BEFORE_START_EVENT)。 
b) 设置标签,表示该容器已启动。 
c) 启动容器中的各个组件,如loader, logger, manager, cluster, realm, resources等。 
d) 启动当前容器的子容器。 
e) 启动当前容器的管道pipeline*。 
f) 触发启动中事件(START_EVENT)。 
g) 触发启动后事件(AFTER_START_EVENT)。 
*:pipeline:当一个容器要把从上一级传递过来的需求转交给子容器的时候,它就会把这个需求放进容器的管道(pipeline)里面去,这个管道里面呢有多个阀门机关(value),而需求在管道里面流动的时候,就会被管道里面的各个阀门拦截下来,只有满足了过关的要求,阀门才会放行。比如管道里面放了两个阀门,第一个阀门叫做“access_allow_vavle”,也就是说需求流过来的时候,它会看这个需求是哪个IP过来的,如果这个IP已经在黑名单里面了,OK,立马拦截,这个需求最远就只能走到这里了,不可能再往下走了!第二个阀门叫做“defaul_access_valve”,它会做例行的检查,如果通过的话,OK,把需求传递给当前容器的子容器。 就是通过这种方式, 需求就在各个容器里面传递,流动, 最后抵达目的地的了。 

以上就是ContainerBase中进行的一些处理。尽管大部分内容都是共用的,但每个容器还是有一些自己特别的处理的,这些各个容器特有的任务都会放在调用ContainerBase之前进行处理。在engine中的特别处理包括engine自己的log以及mbean的处理等等。 

6、 Host是engine的子容器,所以host也会调用ContainerBase的start()方法。 
而host的特殊处理主要就是往pipeline里面安装了一个errorReportValue的阀门。这个errorReportValue的作用主要就是用来检查response的。需求在被Engine传递给Host后, 会继续传递给Context做具体的处理。 这里需求其实就是作为参数传递的Request, Response。所以在context把需求处理完后,通常会改动response。而这个org.apache.catalina.valves.ErrorReportValve的作用就是检察response是否包含错误, 如果有就做相应的处理。 

7、 终于到了Context了。Context的启动是从 StandardContext的start()开始的。下面我们一步一步来看StandardContext的start()中都做了些什么。 

a) 触发启动前事件(BEFORE_START_EVENT)。 
b) 设置web app的具体目录webappResources。 
c) 为context指定loader,Loader就是用来指定这个context会用到哪些类啊,哪些jar包啊这些什么的。 
d) GetCharsetMapper(),得到字符编码格式,tomcat自己有一个默认的配置文件用来设置默认情况下的字符编码格式,如果用户没有自定义的话,就采用默认的配置,一般为为/org/apache/catalina/util/CharsetMapperDefault.properties。 
e) postWorkDirectory (),创建临时文件目录。Tomcat下面有一个work目录,用来存放临时文件。这个步骤就是在那里创建一个目录,一般说来会在%CATALINA_HOME%/work/Standalone\localhost\这个地方生成一个目录。 
f) Binding thread(),负责绑定当前线程与context。首先要转换class loader,因为之前需要的是tomcat下的所有class和lib,接下来需要的就是当前context,也就是web app的class和lib了,所以要重新设置当前的的contextClassLoader,同时要记录下旧的class loader。然后就要进行线程的绑定了。 

Java代码  

threadBindings.put(Thread.currentThread(), context);  
threadNameBindings.put(Thread.currentThread(), name);  

threadBindings和threadNameBindings都是HashTable,这两步操作把当前线程与当前的这个context绑定起来。接下来这个线程就作为这个web app的主线程了。 
g) 启动当前context的loader。 
h) 重置logger并启动它。 
i) 若存在子容器,启动子容器,并启动其管道pipeline。 
j) 触发START_EVENT事件监听, 
Java代码  

lifecycle.fireLifecycleEvent(START_EVENT, null);  

在这个事件监听里面会启动ContextConfig的start()事件,ContextConfig是用来配置web.xml的。比如这个Context有多少Servlet,又有多少Filter,就是在这里给Context装上去的。ContextConfig主要做了这些工作: 

Java代码  

defaultWebConfig();    //每个context要配置一个默认的web.xml,就是omcat/conf/web.xml,这样container servlet才能被加载。  
applicationWebConfig();    //配置web app自己的web.xml  
validateSecurityRoles();   //验证访问角色的安全性。就是web app的部署权限,通常我们会通过访问/admin 或者/manager来进入应用的部署界面,一般用户是admin或者manager才能访问。访问的用户以及可以访问的资源都是可以限制的,这些都可以通过权限验证来实现。  
authenticatorConfig();   //配置自动认证  

k) 为context创建welcome files,通常是这三个启动文件:index.html、index.htm、index.jsp,它们就被默认地绑在了这个context上。 
l) 触发AFTER_START_EVENT事件。 
m) 配置listener。 
n) 启动manager。Manager是用来管理session的。对于服务器来说,每个请求传递过来的时候,会在request里面加上一个叫做sessionId的属性,服务器就通过这个sessionId来知道这个请求到底是属于哪一个session的。 
o) 启动context的后台主线程。 
p) 配置filter。 
q) 启动带有<load-on-startup>的Servlet。如<load-on-startup>1</load-on-startup>,启动的顺序从1开始按照数字从小到大,1, 2, 3 ……,最后才是0。 
默认情况下,至少会启动如下3个的Servlet: 
org.apache.catalina.servlets.DefaultServlet 负责处理静态资源的Servlet,例如图片、html、css、js等等。 
org.apache.catalina.servlets.InvokerServlet负责处理没有做Servlet Mapping的那些Servlet。 
org.apache.jasper.servlet.JspServlet负责处理JSP文件。 
r) 标识context已经启动完毕,如果在启动的时候发生错误,则stop server。 
s) 注册JMX。registerJMX(); 
t) 关闭所有JAR,以免在启动的时候打开的文件数量总是很高。 

如果文字看不下来的话,可以看看下面的流程图,如果你坚持看完了上面一大段话的话,呃,也可以再看看下面的图。 



 到这里tomcat就算启动完毕了,我们可以看到它的启动过程是一环套一环的过程,先是父容器,然后是子容器,一层层往下递进。 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: