首先从应用层面上谈谈,Servlet是运行在服务器端的java应用程序,由servlet容器对其进行管理,当用户对容器发送Http请求时,通过web.xml配置文件servlet容器将找到并通知相应的servlet对象进行处理,从而完成与用户的交互。
短短几句话,看起来简单,但是可以这么说:“整个java web应用基本上都是基于sevlet技术的。”所以觉得有必要深入抠一下servlet的细节。
要深入servlet的话,首先有必要先把servlet容器搞一搞,servlet与servlet容器是相互依赖,但同时他们也是独立发展,这也是出于解耦的考虑。它们之间通过标准化接口来协同工作。
Servlet容器种类很多,这里仅仅谈谈常用的tomcat容器。Tomcat容器分为好几层,有container,engine,host,servlet容器,context,wrapper 如下图:
每一个context对应一个web应用,一般的是把一个servlet封装成wrapper放到容器里面运行。也就是说context容器是才是直接管理servlet的容器。当添加一个新的web应用时,tomcat会创建一个StandardContext容器。并且给这个容器配置一些参数,想url,path等。最重要的是contextconfig这个配置,因为之后整个web应用的解析都依靠这个配置。
Ok,那下面就稍微深入一点看看servlet。
. servlet初始化过程
首先web.xml作为整个web应用的入口,通过前面提到的contextconfig解析web.xml文件,从而初始化整个web应用。Config将web.xml中的配置内容解析为各个属性保存到webxml对象中,其中包括filter,listener,servlet等,并将这些放到context容器中,其中servlet是被解析问wrapper作为子容器放到context,而其他的直接解析放到context的。主要是因为servlet有自己独立的开发标准,不需要强耦合到容器中。
解析工作之后,就是实例化了,tomcat在启动时,会自动实例两个servlet即defaultservelt和jspservlet。其他的servlet是通过wrapper根据配置文件实例化的。
通过wrapper中的相应方法调用servlet的init方法初始化servlet,如果有jsp文件的话,则初始化的就是jspservlet,同时就会模拟一次简单请求,请求调用这个jsp文件从而编译为.class文件,并初始化这个class。
.servlet自身内部结构
Servlet顶层类关联图
(Tips: 这些接口的源代码都在javax.servlet包中,但是我找的时候没有找到,原来是因为使用的是jdk1.5标准版也就是j2se,而servlet是j2ee中使用的,所以可以下载j2ee开发包,或者是在tomcat的commod\lib下存在jsp和servlet的jar包。)
从上图可以看出servlet是基于这几个类的,request和response为交易对象,servletconfig是在init时有容器传过来的,主要负责配置方面的工作。Servletcontext主要是负责一些运行环境方面的属性像路径和url等。
这里主要谈谈request和response对象。已经我们常用的httpservlet和httpresponse对象的关系。
请看下图:
他们之间的转化过程:
从图中可以看出http线程处理socket发送到Tomcat容器,容器启动时首先先创建一个request和response对象,接着传给servlet应用是façade对象,目的也是为了封装容器request和response对象的细节。
.servlet如何工作?
用户从浏览器向服务器发起一个请求,通常会包含如下信息:http://hostname: port /contextpath/servletpath,hostname 和 port 是用来与服务器建立 TCP 连接,而后面的 URL 才是用来选择服务器中那个子容器服务用户的请求。那服务器是如何根据这个 URL 来达到正确的 Servlet 容器中的呢?
Tomcat7.0 中这件事很容易解决,因为这种映射工作有专门一个类来完成的,这个就是 mapper,这个类保存了 Tomcat 的 Container 容器中的所有子容器的信息,当 Request 类在进入 Container 容器之前,mapper 将会根据这次请求的 hostnane 和 contextpath 将 host 和 context 容器设置到 Request 的 mappingData 属性中。所以当 Request 进入 Container 容器之前,它要访问那个子容器这时就已经确定了。
Mapper中如何会有所有的这些容器完整关系,这里应用了一个设计模式叫做监听者模式,mapperlistener被加到每个子容器中,这样只要任何一个容器变化,监听器将会被通知。则相应的mapper属性会被修改。
Mapping之后中间会执行filter,listener等,之后就是调用service方法了。
一般的我们定义的servlet会继承httpservlet或者GenericServlet类直接选择覆盖相应方法,servlet就可以帮助完成相应工作了。
但是,现在大多数的应用都不直接用servlet来操作了,而是使用一些更有效的mvc框架,而这些框架的基本原理也都是将所有请求映射到一个servlet中,然后运行service方法,也就是mvc框架的入口。
之后就是从servlet容器中移除了。调用destroy方法就ok了。
(挺晚了,先这些吧。上面提到了监听者模式,明天发一篇关于该模式的和session和cookie的博客。)