Tomcat源码分析(五)--容器处理连接之servlet的映射

    本文所要解决的问题:一个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
        Wrapper wrapper = null;
        String servletPath = relativeURI;
        String pathInfo = null;
        String name = null;

        // Rule 1 -- Exact Match
        if (wrapper == null) {
            if (debug >= 2)
                context.log("  Trying exact match");
            if (!(relativeURI.equals("/")))
                name = context.findServletMapping(relativeURI);
            if (name != null)
                wrapper = (Wrapper) context.findChild(name);
            if (wrapper != null) {
                servletPath = relativeURI;
                pathInfo = null;
            }
        }

        // Rule 2 -- Prefix Match
        if (wrapper == null) {
            if (debug >= 2)
                context.log("  Trying prefix match");
            servletPath = relativeURI;
            while (true) {
                name = context.findServletMapping(servletPath + "/*");
                if (name != null)
                    wrapper = (Wrapper) context.findChild(name);
                if (wrapper != null) {
                    pathInfo = relativeURI.substring(servletPath.length());
                    if (pathInfo.length() == 0)
                        pathInfo = null;
                    break;
                }
                int slash = servletPath.lastIndexOf('/');
                if (slash < 0)
                    break;
                servletPath = servletPath.substring(0, slash);
            }
        }

        // Rule 3 -- Extension Match
        if (wrapper == null) {
            if (debug >= 2)
                context.log("  Trying extension match");
            int slash = relativeURI.lastIndexOf('/');
            if (slash >= 0) {
                String last = relativeURI.substring(slash);
                int period = last.lastIndexOf('.');
                if (period >= 0) {
                    String pattern = "*" + last.substring(period);
                    name = context.findServletMapping(pattern);
                    if (name != null)
                        wrapper = (Wrapper) context.findChild(name);
                    if (wrapper != null) {
                        servletPath = relativeURI;
                        pathInfo = null;
                    }
                }
            }
        }

        // Rule 4 -- Default Match
        if (wrapper == null) {
            if (debug >= 2)
                context.log("  Trying default match");
            name = context.findServletMapping("/");
            if (name != null)
                wrapper = (Wrapper) context.findChild(name);
            if (wrapper != null) {
                servletPath = relativeURI;
                pathInfo = 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也是根据映射器来选择它们的下一级容器的。
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值