一个context对应着一个web工程
一个wrapper对应一个servlet
1.1 Servlet容器启动过程
tomcat7以后支持嵌入式功能,可直接通过构建一个tomcat对象,调用start方法启动。
添加一个web应用会调用addWebapp方法,创建一个standardContext容器,
addWebapp(Host host,String url,String path)
host:context的上层容器
url:应用的访问路径
path:应用的实际路径
Tomcat的启动逻辑是基于观察者模式设计的,所有的容器都会继承lifecycle接口,他管理容器的生命周期,所有容器的修改和状态的改变都会通知已经注册的观察者
最重要的一个配置类ContextConfig,它负责整个Web应用的配置文件的解析工作
ContextConfig的init方法主要完成以下工作:
创建解析XML配置文件的contextDigester对象
读取默认的context.xml文件,存在则解析它
读取默认的Host文件,存在则解析它
读取默认的Context自身配置文件,存在则解析它
设置Context的DocBase
ContextConfig的init完成,执行startInternal方法,主要包括:
创建资源文件对象,创建ClassLoader对象
设置对应的工作目录
启动相关辅助类
修改启动状态,通知感兴趣的观察者
子容器初始化
获取servletContext并设置必要的参数
初始化“load on startup” 的servlet
1.2 web应用初始化
Web应用初始化是在ContextConfig的configure方法实现的,主要是解析web.xml文件
Tomcat首先会找到globalWebXml,这个文件的路径是engine的工作目录下的org/apache/catalina/startup/NO_DEFAULT_XML或者conf/web.xml 。接着找hostWebXml, 这个文件在System.getProperty("catalina.base")/conf/${EngineName}/${HostName}/web.xml.default中。接着寻找应用配置文件/WEB-INF/web.xml。web.xml的各个配置项将会被解析成相应的属性保存在WebXml对象中,如果应用致辞servlet3.0,解析还将完成别的工作,比如注解的支持
servlet为什么要包装成StandardWrapper?
因为servlet是web的标准,不应强耦合在tomcat中,StandardWrapper是tomcat容器的一部分,具有容器特征
除了Servlet包装成StandardWrapper在Context容器外,其他所有的web.xml属性都被解析到Context中
1.3 创建Servlet实例
如果servlet的load-on-startup配置项大于0,那么context容器启动时就会被实例化
创建Servlet实例的方法是从Wrapper.loadSeervlet开始的,loadServlet方法要完成的就是获取servletClass,然后交给InstanceManager去创建一个机遇sevletClass.class的对象。如果配置了jsp-fle,那么这个servletClass就是JspServlet
2.1Servlet的体系结构
Servlet的规范基于ServletConfig,ServletRequest,ServletResponse这三个类运转
ServletConfig是在Servlet初始化的时候传给Servlet,而后两个值是在请求到达时调用Servlet传递过来的
StandWrapper 和 StandWrapperFacade都实现了ServletConfig的接口,StandWrapperFacade是StandWrapper的门面类,所以传给Servlet的是StandWrapperFacade对象,好处是只拿到感兴趣的数据,不会过多暴露数据
Tomcat接收请求,首先会创建org.apache.coyote.Request和Response ,这两个类是tomcat内部使用轻量级的类,只会简单解析,然后交给后续线程处理,对象很小,很容易被回收。接下来交给一个用户线程去处理这个请求,又会创建org.apache.catalina.connector.Request 和Response对象,这两个对象一直贯穿整个Servlet容器直到传给Sevelt,传递的也是门面类RequestFacade 和ResponseFacade
2.2 Servlet是如何工作的
一个请求:http://hostname:port/contextpath/servletpath
hostname 和port 是用来与服务器建立 TCP连接的 ,后面的url用来选择在服务器的哪个子容器中完成请求
如何确定哪个Servlet容器?
有一个mapper类保存Tomcat 和Container容器的所有子容器信息,进入Container之前Request会根据请求的hostname 和context 将host 和context容器设置到Request的mappingData属性中
2.3 Servlet中的Listener
基于观察者模式设计
servlet通过6种两类事件的观察者接口:
EventListeners: ServeltContextAtributeListener , ServeltRequestAtributeListener ,ServeltRequestListener, HttpSessionAtributeListener
LifecycleListeners: ServeltContextListener 和 HttpSessionListener
例如常用的ContextLoaderListener实际上就是实现ServletContextListener接口,容器加载时初始化Spring容器,ContextLoaderListener在contextInitialized方法中初始化Spring容器
public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext());
}
2.3 Filter如何工作
web.xml中配置<filter> 和 <filter-mapping> 使用filter 或者 实现filter接口
实现filter接口需要实现三个方法:
init:初始化
doFilter:请求来时调用,在servlet.service之前执行,使用责任链模式,可以通过FilterChain.doFilter将请求继续传递下去
destory:销毁时调用,注意,web容器调用这个方法以后,容器会再调用一次dofilter方法
Filter的核心是FilterChain对象,这个对象保存了最终Servlet对象的所有Filter对象,这些对象都在ApplicationFilterChain对象的filters数组,每执行一个filter,数组计数会加一,直到等于数组长度,执行完会执行Servlet,因此ApplicationFilterChain对象持有Servlet的引用
2.4 Servlet 中的url-pattern
servlet是通过mapper类映射,请求被创建时就已经匹配了
filter的url-pattern匹配在创建ApplicationFilterChain对象时进行,将所有filter的url-pattern与当前url匹配,匹配成功就保存到filters数组中
url-pattern匹配规则:
精确匹配:/foo.htm
路径匹配:/foo/*
后缀匹配:*.htm
Filter会将所有匹配成功的加入filters数组中
对于Servlet而言,多个匹配成功的话,优先级是:精确匹配优先,其次是最长匹配路径,最后是后缀匹配