Servlet的工作原理-深入分析Java Web技术内幕

Servlet的工作原理

(1)、Servlet容器

(2)、Servlet容器的启动过程

ContextConfig类会负责整个Web应用的配置文件的解析工作。

ContextConfig的init方法将会主要完成以下工作:

◎ 创建用于解析XML配置文件的contextDigester对象。

◎ 读取默认context.xml配置文件,如果存在解析它。

◎ 读取默认Host配置文件,如果存在解析它。

◎ 读取默认Context自身的配置文件,如果存在解析它。

◎ 设置Context的DocBase。

ContextConfig的init方法完成后,Context容器就会执行startInternal方法,这个方法的启动逻辑比较复杂,主要包括如下几个部分:

◎ 创建读取资源文件的对象。

◎ 创建ClassLoader对象。

◎ 设置应用的工作目录。

◎ 启动相关的辅助类,如logger、realm、resources等。

◎ 修改启动状态,通知感兴趣的观察者(Web应用的配置)。

◎ 子容器的初始化。

◎ 获取ServletContext并设置必要的参数。

◎ 初始化“load on startup”的Servlet

 

(3)、Web应用的初始化

Web应用的初始化工作是在ContextConfig的configureStart方法中实现的,应用的初始化主要是解析web.xml文件,这个文件描述了一个Web应用的关键信息,也是一个Web应用的入口。

Tomcat首先会找globalWebXml,这个文件的搜索路径是engine的工作目录下的org/apache/catalin/startup/NO_DEFAULT_XML或conf/web.xml。接着会找hostWebXml,这个文件可能会在System.getProperty("catalina.base")/conf/${EngineName}/${HostName}/web.xml.default中,接着寻找应用的配置文件examples/WEB-INF/web.xml。web.xml文件中的各个配置项将会被解析成相应的属性保存在WebXml对象中。如果当前应用支持Servlet 3.0,解析还将完成额外9项工作,这额外的9项工作主要是Servlet 3.0新增的特性(包括jar包中的META-INF/web-fragment.xml)的解析及对annotations的支持。

接下去将会将WebXml对象中的属性设置到Context容器中,这里包括创建Servlet对象、filter、listener等,这里StandardWrapper是Tomcat容器中的一部分,它具有容器的特征,而Servlet为一个独立的Web开发标准,不应该强耦合在Tomcat中。

除了将Servlet包装成StandardWrapper并作为子容器添加到Context中,其他的所有web.xml属性都被解析到Context中,所以说Context容器才是真正运行Servlet的Servlet容器。一个Web应用对应一个Context容器,容器的配置属性由应用的web.xml指定。

 

(4)、创建Servlet实例

如果Servlet的load-on-startup配置项大于0,那么在Context容器启动的时候就会被实例化,前面提到在解析配置文件时会读取默认的globalWebXml,在conf下的web.xml文件中定义了一些默认的配置项,其中定义了两个Servlet,分别是org.apache.catalina. servlets.DefaultServlet和org.apache.jasper.servlet.JspServlet。它们的load-on-startup分别是1和3,也就是当Tomcat启动时这两个Servlet就会被启动。

创建Servlet实例的方法是从Wrapper. loadServlet开始的。loadServlet方法要完成的就是获取servletClass,然后把它交给InstanceManager去创建一个基于servletClass.class的对象。如果这个Servlet配置了jsp-file,那么这个servletClass就是conf/web.xml中定义的org.apache.jasper.servlet.JspServlet了。

初始化Servlet在StandardWrapper的initServlet方法中,这个方法很简单,就是调用Servlet的init()方法,同时把包装了StandardWrapper对象的StandardWrapperFacade作为ServletConfig传给Servlet。如果该Servlet关联的是一个JSP文件,那么前面初始化的就是JspServlet,接下去会模拟一次简单请求,请求调用这个JSP文件,以便编译这个JSP文件为类,并初始化这个类。

这样Servlet对象就初始化完成了,事实上Servlet从被web.xml解析到完成初始化,这个过程非常复杂,中间有很多过程,包括各种容器状态的转化引起的监听事件的触发、各种访问权限的控制和一些不可预料的错误发生的判断行为等。下面是Servlet初始化的时序图。

 

(5)、Servlet体系结构

Servlet规范就是基于这几个类运转的,与Servlet主动关联的是三个类,分别是ServletConfig、ServletRequest和ServletResponse。这三个类都是通过容器传递给Servlet的,其中ServletConfig在Servlet初始化时就传给Servlet了,而后两个是在请求达到时调用Servlet传递过来的。我们很清楚ServletRequest和ServletResponse在Servlet运行时的意义,但是ServletConfig和ServletContext对Servlet有何价值?仔细查看ServletConfig接口中声明的方法会发现,这些方法都是为了获取这个Servlet的一些配置属性,而这些配置属性可能在Servlet运行时被用到。ServletContext又是干什么的呢?Servlet的运行模式是一个典型的“握手型的交互式”运行模式。所谓“握手型的交互式”就是两个模块为了交换数据通常都会准备一个交易场景,这个场景一直跟随这个交易过程直到这个交易完成为止。这个交易场景的初始化是根据这次交易对象指定的参数来定制的,这些指定参数通常就是一个配置类。所以对号入座,交易场景就由ServletContext来描述,而定制的参数集合就由ServletConfig来描述。而ServletRequest和ServletResponse就是要交互的具体对象,它们通常都作为运输工具来传递交互结果。

可以看出,StandardWrapper和StandardWrapperFacade都实现了ServletConfig接口,而StandardWrapperFacade是StandardWrapper门面类。所以传给Servlet的是StandardWrapperFacade对象,这个类能够保证从StandardWrapper中拿到ServletConfig所规定的数据,而又不把ServletConfig不关心的数据暴露给Servlet。

同样ServletContext也与ServletConfig有类似的结构,Servlet中能拿到的ServletContext的实际对象也是ApplicationContextFacade对象。ApplicationContextFacade同样保证ServletContex只能从容器中拿到它该拿的数据,它们都起到对数据的封装作用,它们使用的都是门面设计模式。

(6)、Servlet如何工作

Servlet如何被调用?

用户从浏览器向服务器发起一个请求[http://hostname:port/contextpath/sevletpath],会根据hostname和port用来与服务器建立TCP连接,而URL用来选择服务器中哪个子容器服务用户的请求,Tomcat7中会建立URL与Servlet的映射。

Mapper将会根据这次请求的hostnane和contextpath将host和context容器设置到Request的mappingData属性中,如图所示。所以当Request进入Container容器之前,它要访问哪个子容器就已经确定了。

 

 

(7)、Servlet中的Listener

 

目前Servlet中提供了6中两类事件的观察者接口,分别是EventListener和LifecycleListener。

 

(8)、Filter如何工作

Web.xml中一个常用的配置项Filter,可以通过<filter>和<filter-mapping>组合来使用Filter。

在Tomcat容器中,FilterConfig和FilterChain的实现类分别是ApplicationFilterConfig和ApplicationFilterChain,而Filter的实现类由用户自定义,只要实现Filter接口中定义的三个接口就行,这三个接口与Servlet中的类似。只不过还有一个ApplicationFilterChain类,这个类可以将多个Filter串联起来,组成一个链,这个链与Jetty中的Handler链有异曲同工之妙。下面详细看一下Filter类中的三个接口方法。

◎ init(FilterConfig):初始化接口,在用户自定义的Filter初始化时被调用,它与Servlet的init方法的作用是一样的,FilterConfig与ServletConfig也类似,除了都能取到容器的环境类ServletContext对象之外,还能获取在<filter>下配置的<init-param>参数值。

◎ doFilter ( ServletRequest, ServletResponse, FilterChain):每个用户的请求进来时这个方法都会被调用,这个方法会在Servlet的service方法之前被调用。而FilterChain就代表当前的整个请求链,所以通过调用FilterChain.doFilter可以将请求继续传递下去。如果想拦截这个请求,可以不调用FilterChain.doFilter,那么这个请求就直接返回了。所以Filter是一种责任链设计模式。

◎ destroy:当Filter对象被销毁时,这个方法被调用。注意,当Web容器调用这个方法之后,容器会再调用一次doFilter方法。

Filter类的核心还是传递的FilterChain对象,这个对象保存了到最终Servlet对象的所有Filter对象,这些对象都保存在ApplicationFilterChain对象的filters数组中。FilterChain链上每执行一个Filter对象,数组的当前计数加1,直到计数等于数组的长度,当FilterChain上所有的Filter对象执行完成后,就会执行最终的Servlet。所以ApplicationFilterChain对象中会持有Servlet对象的引用。

(9)、Servlet中的url-pattern

<url-pattern>的解析规则,对Servlet和Filter是一样的,匹配的规则有三种。

◎ 精确匹配:如/foo.htm只会匹配foo.htm这个URL。

◎ 路径匹配:如/foo/*会匹配以foo为前缀的URL。

◎ 后缀匹配:如*.htm会匹配所有以.htm的URL。

 

以上图来自《深入分析Java Web技术内幕》

参考书籍:

《深入分析Java Web技术内幕》

猜你喜欢

转载自blog.csdn.net/zangdaiyang1991/article/details/85044088