Tomcat源码分析(五)--容器处理连接之servlet的映射
  fztgkkRjHIsV 2023年11月14日 25 0


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

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


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


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


wrapper = (Wrapper) context.map(request, true);

这句代码表示容器会调用map方法来映射请求到具体的wrapper上,意思就是说,根据连接请求request来选择wrapper。上面的map会调用父类ContainerBase的map方法来找到具体的映射器,至于这个映射器和容器是怎么关联上的,具体请参考  Tomcat源码分析(三)--连接器是如何与容器关联的?

这篇文章,大致原理是一样的。StandardContext容器有一个标准的映射器实现类StandardContextMapper,所以最终会调用到映射器StandardContextMapper的map方法,这个方法是选择servlet的关键(省略了一些代码):


public Container map(Request request, boolean update) {  
// Identify the context-relative URI to be mapped  
       String contextPath =  
           ((HttpServletRequest) request.getRequest()).getContextPath();  
       String requestURI = ((HttpRequest) request).getDecodedRequestURI();  
       String relativeURI = requestURI.substring(contextPath.length());  
// Apply the standard request URI mapping rules from the specification  
null;  
       String servletPath = relativeURI;  
null;  
null;  
  
// Rule 1 -- Exact Match  
if (wrapper == null) {  
if (debug >= 2)  
"  Trying exact match");  
if (!(relativeURI.equals("/")))  
               name = context.findServletMapping(relativeURI);  
if (name != null)  
               wrapper = (Wrapper) context.findChild(name);  
if (wrapper != null) {  
               servletPath = relativeURI;  
null;  
           }  
       }  
  
// Rule 2 -- Prefix Match  
if (wrapper == null) {  
if (debug >= 2)  
"  Trying prefix match");  
           servletPath = relativeURI;  
while (true) {  
"/*");  
if (name != null)  
                   wrapper = (Wrapper) context.findChild(name);  
if (wrapper != null) {  
                   pathInfo = relativeURI.substring(servletPath.length());  
if (pathInfo.length() == 0)  
null;  
break;  
               }  
int slash = servletPath.lastIndexOf('/');  
if (slash < 0)  
break;  
0, slash);  
           }  
       }  
  
// Rule 3 -- Extension Match  
if (wrapper == null) {  
if (debug >= 2)  
"  Trying extension match");  
int slash = relativeURI.lastIndexOf('/');  
if (slash >= 0) {  
               String last = relativeURI.substring(slash);  
int period = last.lastIndexOf('.');  
if (period >= 0) {  
"*" + last.substring(period);  
                   name = context.findServletMapping(pattern);  
if (name != null)  
                       wrapper = (Wrapper) context.findChild(name);  
if (wrapper != null) {  
                       servletPath = relativeURI;  
null;  
                   }  
               }  
           }  
       }  
  
// Rule 4 -- Default Match  
if (wrapper == null) {  
if (debug >= 2)  
"  Trying default match");  
"/");  
if (name != null)  
               wrapper = (Wrapper) context.findChild(name);  
if (wrapper != null) {  
               servletPath = relativeURI;  
null;  
           }  
       }



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



public String findServletMapping(String pattern) {  
synchronized (servletMappings) {  
return ((String) servletMappings.get(pattern));  
    }  
}



findChild方法跟findServletMapping方法一样,也是在一个HashMap找wrapper容器。至此,已经能够回答一开始的问题了,StandardContext容器根据映射器来选择wrapper。当然,容器Engine和Host也是根据映射器来选择它们的下一级容器的。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月14日 0

暂无评论

推荐阅读
fztgkkRjHIsV