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也是根据映射器来选择它们的下一级容器的。
原文地址:https://www.cnblogs.com/chenying99/p/2798440.html