Tomcat Wrapper组件

Tomcat Wrapper组件

一些基本概念
1、ServletContext:
作用 :表示一个web应用的上下文;可以想象成一个Web应用程序的共享数据区域,该区域保存该Web应用程序的共享数据;
生命周期: 每个Web应用程序都对应一个ServletContext,保存在Context中,在Context初始化时创建,Context撤销时销毁;
2、servlet-mapping:
作用: 按url将请求匹配到servlet;
匹配过程: 1) url按最长context path匹配当前Host下的Context;
                   2) 余下的将用于匹配该Context下的Servlet;
匹配规则,顺序如下:
1) 精确匹配:如/user/login;必须以/开头,不能以/*结尾;
2) 通配符匹配:如/customers/*,以最长串优先依次匹配;
3) 扩展名匹配:如/user/register.jsp;任何以*开头,以jsp结尾的mapping都会匹配;
4) 默认匹配:上述3种匹配失败后,将匹配到该mapping上;
3、Filter:
作用: Filter使用户可以改变一个request和修改一个response,它不是一个servlet,也不能产生response,它能够在一个request到达servlet之前预处理request,也可以在response离开servlet时处理response。
类图: 如下
Tomcat Wrapper组件
其中:
1) FilterDef用于保存从web.xml解析出来的filter定义,包含filterName, filterClass, init-param等;
2) FilterMap用于保存从web.xml解析出来的filter-mapping配置;
3) 当初始化完所有的FilterDef以及FilterMap后,容器会初始化ApplicationFilterConfig,用于Filter内部访问Tomcat容器,如Filter的init-param, ServletContext等;
4) 解析出来的FilterDef、FilterMap以及ApplicationFilterConfig均保存在StandardContext中;
4、Listener:
作用: 通过在web.xml中配置<listener>元素,可以在适当的时候收到Tomcat容器的事件通知,从而可以做出相应的响应;
类型:
1) 事件类型: 当某事件发生时触发;
a) ServletContextAttributeListener:当ServletContext的属性发生变化(新增、修改、删除)时触发;
b) ServletRequestAttributeListener:当request的属性发生变化(新增、修改、删除)时响应;
c) ServletRequestListener:当请求在经Filter链处理前后时响应;
d) HttpSessionAttributeListener:当session中的属性发生变化(新增、修改、删除)时响应;
2) 生命周期类型: 在生命周期中的某个点触发;
a) ServletContextListener:在ServletContext初始化完成和销毁时触发;
b) HttpSessionListener:在session创建和销毁时触发;

重要的数据结构
1、Mapper:
保存Tomcat容器中所有Host, Context, Wrapper及servlet-mapping的映射关系;
2、MappingData:
当前请求经过Mapper后,即决定将该请求交给哪个Host, 哪个Context, 哪个Wrapper来处理;这些数据组成MappingData;

创建ServletContext
1、ServletContext类图
Tomcat Wrapper组件
这里采用了门面模式,ApplicationContext仅供tomcat内部使用,在Servlet中取得的servletContext实际上是ApplicationContextFacade的实例,防止外部修改tomcat容器中重要的数据结构;
2、实例化ServletContext
实例化ServletContext是通过StandardContext.getServletContext()方法来完成的:
Java代码 复制代码  收藏代码
  1. public ServletContext getServletContext() {   
  2.     if (context == null) {   
  3.         context = new ApplicationContext(getBasePath(), this);   
  4.         if (altDDName != null)   
  5.             context.setAttribute(Globals.ALT_DD_ATTR,altDDName);   
  6.     }   
  7.     return (context.getFacade());   
  8. }  
    public ServletContext getServletContext() {
        if (context == null) {
            context = new ApplicationContext(getBasePath(), this);
            if (altDDName != null)
                context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
        }
        return (context.getFacade());
    }

解析部署描述符web.xml
1、初始化Wrapper
当StandardContext初始化时,会解析web.xml文件(参考WebRuleSet);当解析到web-app/servlet标签时会调用StandardContext.createWrapper()方法,从而构造Wrapper组件,并将其作为子节点添加到当前Context下;
初始化Wrapper后,会设置与该servlet相关的配置,如servlet-name, servlet-class, init-param, jsp-file, load-on-startup等;这些配置值都是刚才构建出来的Wrapper组件的属性;
2、初始化servlet-mapping
由于servlet-mapping是针对Context的,处理servlet-mapping的工作由Context来完成,主要工作如下:
1) 根据servert-name找到该Context的子节点Wrapper,然后将该mapping添加到Wrapper中;
2) 将该Wrapper添加到当前Context的Mapper中,供ServletContext使用;
3、初始化filter及filter-mapping
1) 解析filter:调用StandardContext.addFilterDef(FilterDef filterDef),如下:
Java代码 复制代码  收藏代码
  1. public void addFilterDef(FilterDef filterDef) {   
  2.     synchronized (filterDefs) {   
  3.         filterDefs.put(filterDef.getFilterName(), filterDef);   
  4.     }   
  5.     fireContainerEvent("addFilterDef", filterDef);   
  6. }  
    public void addFilterDef(FilterDef filterDef) {
        synchronized (filterDefs) {
            filterDefs.put(filterDef.getFilterName(), filterDef);
        }
        fireContainerEvent("addFilterDef", filterDef);
    }
即:将filterDef以filterName为key保存在Map中;
2) 解析filter-mapping:调用StandardContext.addFilterMap(FilterMap filterMap),如下:
Java代码 复制代码  收藏代码
  1. public void addFilterMap(FilterMap filterMap) {   
  2.     // 先会做一些校验   
  3.     synchronized (filterMaps) {   
  4.         FilterMap results[] =new FilterMap[filterMaps.length + 1];   
  5.         System.arraycopy(filterMaps, 0, results, 0, filterMaps.length);   
  6.         results[filterMaps.length] = filterMap;   
  7.         filterMaps = results;   
  8.     }   
  9.     fireContainerEvent("addFilterMap", filterMap);   
  10. }  
    public void addFilterMap(FilterMap filterMap) {
        // 先会做一些校验
        synchronized (filterMaps) {
            FilterMap results[] =new FilterMap[filterMaps.length + 1];
            System.arraycopy(filterMaps, 0, results, 0, filterMaps.length);
            results[filterMaps.length] = filterMap;
            filterMaps = results;
        }
        fireContainerEvent("addFilterMap", filterMap);
    }
3) 初始化ApplicationFilterConfig:当StandardContext启动时,会通过filterStart()方法来初始化ApplicationFilterConfig:将1)中保存的每个filterDef和当前StandardContext包装成一个ApplicationFilterConfig,供后面的创建filter链时使用;
4、初始化listener
当解析到web.xml中的<listener>元素时,会调用Standard.addApplicationListener(String listener)将listener的class信息保存起来,然后在StandardContext启动时,通过listenerStart()方法来初始化listener:实例化先前保存起来的listener,并按事件/生命周期分类保存其实例;
此时StandardContext初始化也快完成了,因此会触发ServletContextListener.contextInitialized()方法;

Servlet的loadOnStartup
在配置Servlet时,可以指定其loadOnStartup属性,该属性值决定了Servlet在容器启动后是否加载及加载顺序,默认为-1,表示不加载;
StandardContext在加载完所有Servlet配置后,会检查当前Context下的所有Wrapper的loadOnStartup属性值,当值大于0时,通过TreeMap按loadOnStartup从小到大排序后,分别调用每个Wrapper.load()方法来完成Servlet的初始化:
1) 根据servletClass是否是Tomcat内部servlet,决定是用webappClassloader还是用serverClassloader来装载servletClass;
2) 实例化servletClass;
3) 如果servlet是单线程(即一个servlet实例同时只能处理一个请求),则初始化一个servlet pool,用于存放空闲的servlet实例;后面在处理请求时分配servlet则会用到该servlet pool;

Mapper相关
1、先看看Mapper相关的类图
Tomcat Wrapper组件
请不要把上图中的Host, Context, Wrapper和Tomcat中的Host组件、Context组件、Wrapper组件相混淆了,它们仅仅代表这三个组件的映射关系,真正所对应的组件实例引用存放在MapperElement抽象类中的object属性上;
2、初始化Mapper结构
我们再来回顾一下Tomcat容器的初始化过程:
1) 解析server.xml(由Catalina类来完成),同时调用StandardServer.start()方法来初始化Server组件;
2) Server组件初始化时,调用StandardService.start()方法来初始化Service组件;
3) 在StandardService.start()中,先完成所有内嵌组件的初始化(Engine, Host, Context, Wrapper等),然后调用Connect.start()方法来初始化Connector组件;
4) 由于在Connector初始化时,所有的内嵌组件都已初始化完毕,此时便可以完成Mapper的构造了;
初始化Mapper主要是由MapperListener来完成(在Connector.start()中调用MapperListener.init()方法):
1) 注册Engine:找到defaultHost,并设置Mapper的defaultHostName属性;
2) 注册所有的Host:针对每个Host组件,都构造一个Mapper$Host实例,name属性为该Host组件的name,object属性即该Host组件;另外,针对Host组件的别名,也会产生新的Mapper$Host实例,不过其object和Mapper$contextList的引用相同;
3) 注册所有的Context:针对每个Context组件,根据其hostName,将其加到对应Mapper$Host实例的Mapper$contextList中去;
4) 注册所有的Wrapper:和注册Context类似,将每个Wrapper组件根据其servlet-mapping保存到Mapper$Context的相应属性上;
3、MappingData的构造
当请求到达后,在交给Engine处理前,会构造MappingData(见CoyoteAdapter.postParseRequest()方法):
1) 根据请求的host属性,从Mapper中找到相应的Host组件及Mapper$Context数组;此时如果没有指定host属性的Host组件,则取defaultHostName所对应的Host组件;
2) 根据请求的uri,找到Context组件;
3) 根据请求的uri,按servlet-mapping规则,依次查找,直接找到对应的Wrapper组件;此时如果找不到,则依次查找welcomeFile所对应Wrapper组件;
4) 如果最后还是没有找到,则返回defaultWrapper组件;
5) 将匹配到的Host, Context, Wrapper保存在request中;

Wrapper组件对请求的处理
当MappingData构造完成后,CoyoteAdapter便将请求交给Engine、Host、Context、Wrapper组件进行处理;在Context组件中,StandardContextValve直接从request中取出Wrapper,然后交给它处理;下面看看Wrapper组件是如何处理的?
1、StandardWrapperValve
Wrapper组件对请求的处理,是通过StandardWrapperValve来完成的:
1) 从Wrapper中分配一个servlet实例;
2) 根据当前servlet和requestPath将匹配到的filter加到filter链中;
3) 调用filter chain处理请求和响应;当所有filter执行完,最后才执行servlet.service()方法;

你可能感兴趣的:(tomcat,jsp,xml,Web,servlet)