zoukankan      html  css  js  c++  java
  • Tomcat源码分析(五)容器处理连接之servlet的映射

    本系列转载自 http://blog.csdn.net/haitao111313/article/category/1179996 

    本文所要解决的问题:一个http请求过来,容器是怎么知道选择哪个具体servlet?

         我们知道,一个Context容器表示一个web应用,一个Wrapper容器表示一个servlet,所以上面的问题可以转换为怎么由Context容器选择servlet,答案是映射器。映射器是实现了Mapper接口的类,作用就是根据请求连接(主要是协议和路径)来选择下一个容器,可以看做是一个哈希表,根据关键字段来选择具体的值,Mapper接口的定义为:

    1. public interface Mapper {  
    2.     public Container getContainer();//返回与该映射器相关联的容器  
    3.     public void setContainer(Container container);  
    4.     public String getProtocol();//返回与该映射器处理的协议  
    5.     public void setProtocol(String protocol);  
    6.     public Container map(Request request, boolean update); //映射函数,实现该函数  
    7. }  

    Tomcat源码分析(四)--容器处理链接之责任链模式中已经知道,请求连接到达StandardContext容器的invoke方法,最终会到达StandardContextValue阀的invoke方法里面,在这个invoke方法中有一句这样的代码

    1. wrapper = (Wrapper) context.map(request, true);  
    这句代码表示容器会调用map方法来映射请求到具体的wrapper上,意思就是说,根据连接请求request来选择wrapper。上面的map会调用父类ContainerBase的map方法来找到具体的映射器,至于这个映射器和容器是怎么关联上的,具体请参考 Tomcat源码分析(三)--连接器是如何与容器关联的?这篇文章,大致原理是一样的。StandardContext容器有一个标准的映射器实现类StandardContextMapper,所以最终会调用到映射器StandardContextMapper的map方法,这个方法是选择servlet的关键(省略了一些代码):

    1. public Container map(Request request, boolean update) {  
    2.  // Identify the context-relative URI to be mapped  
    3.        String contextPath =  
    4.            ((HttpServletRequest) request.getRequest()).getContextPath();  
    5.        String requestURI = ((HttpRequest) request).getDecodedRequestURI();  
    6.        String relativeURI = requestURI.substring(contextPath.length());  
    7.        // Apply the standard request URI mapping rules from the specification  
    8.        Wrapper wrapper = null;  
    9.        String servletPath = relativeURI;  
    10.        String pathInfo = null;  
    11.        String name = null;  
    12.   
    13.        // Rule 1 -- Exact Match  
    14.        if (wrapper == null) {  
    15.            if (debug >= 2)  
    16.                context.log("  Trying exact match");  
    17.            if (!(relativeURI.equals("/")))  
    18.                name = context.findServletMapping(relativeURI);  
    19.            if (name != null)  
    20.                wrapper = (Wrapper) context.findChild(name);  
    21.            if (wrapper != null) {  
    22.                servletPath = relativeURI;  
    23.                pathInfo = null;  
    24.            }  
    25.        }  
    26.   
    27.        // Rule 2 -- Prefix Match  
    28.        if (wrapper == null) {  
    29.            if (debug >= 2)  
    30.                context.log("  Trying prefix match");  
    31.            servletPath = relativeURI;  
    32.            while (true) {  
    33.                name = context.findServletMapping(servletPath + "/*");  
    34.                if (name != null)  
    35.                    wrapper = (Wrapper) context.findChild(name);  
    36.                if (wrapper != null) {  
    37.                    pathInfo = relativeURI.substring(servletPath.length());  
    38.                    if (pathInfo.length() == 0)  
    39.                        pathInfo = null;  
    40.                    break;  
    41.                }  
    42.                int slash = servletPath.lastIndexOf('/');  
    43.                if (slash < 0)  
    44.                    break;  
    45.                servletPath = servletPath.substring(0, slash);  
    46.            }  
    47.        }  
    48.   
    49.        // Rule 3 -- Extension Match  
    50.        if (wrapper == null) {  
    51.            if (debug >= 2)  
    52.                context.log("  Trying extension match");  
    53.            int slash = relativeURI.lastIndexOf('/');  
    54.            if (slash >= 0) {  
    55.                String last = relativeURI.substring(slash);  
    56.                int period = last.lastIndexOf('.');  
    57.                if (period >= 0) {  
    58.                    String pattern = "*" + last.substring(period);  
    59.                    name = context.findServletMapping(pattern);  
    60.                    if (name != null)  
    61.                        wrapper = (Wrapper) context.findChild(name);  
    62.                    if (wrapper != null) {  
    63.                        servletPath = relativeURI;  
    64.                        pathInfo = null;  
    65.                    }  
    66.                }  
    67.            }  
    68.        }  
    69.   
    70.        // Rule 4 -- Default Match  
    71.        if (wrapper == null) {  
    72.            if (debug >= 2)  
    73.                context.log("  Trying default match");  
    74.            name = context.findServletMapping("/");  
    75.            if (name != null)  
    76.                wrapper = (Wrapper) context.findChild(name);  
    77.            if (wrapper != null) {  
    78.                servletPath = relativeURI;  
    79.                pathInfo = null;  
    80.            }  
    81.        }  

    代码很长,但是很容易看懂,就是分4中匹配模式(完全匹配,前缀匹配,扩展匹配,默认匹配)来选择wrapper,关键代码就是name = context.findServletMapping和wrapper = (Wrapper) context.findChild(name);这里面context都是StandardContext。context.findServletMapping是根据匹配模式来找到servlet名字,context.findChild是根据servlet名字找到具体的wrapper。findServletMapping方法很简单,就是在一个HashMap里面得到servlet名字,代码如下,其中servletMappings是一个HashMap:

    1. public String findServletMapping(String pattern) {  
    2.     synchronized (servletMappings) {  
    3.         return ((String) servletMappings.get(pattern));  
    4.     }  
    5. }  

    findChild方法跟findServletMapping方法一样,也是在一个HashMap找wrapper容器。至此,已经能够回答一开始的问题了,StandardContext容器根据映射器来选择wrapper。当然,容器Engine和Host也是根据映射器来选择它们的下一级容器的。
  • 相关阅读:
    有关 PHP 和 MySQL 时区的一点总结
    PHP CLI模式下的多进程应用
    Linux编程之:五个常见PHP数据库问题
    用php定制404错误页面 并发信通知管理员
    配置PHP站点安全综合教程
    新手必看的PHP学习入门的一些基础知识
    彻底杜绝PHP的session cookie错误
    专家预言:PHP将比Java更受开发人员欢迎
    PHP企业级应用之WebService续篇
    清除 数据库 日志 以 Db_Test 为例
  • 原文地址:https://www.cnblogs.com/chenying99/p/2798440.html
Copyright © 2011-2022 走看看