【Tomcat9源码分析】Mapper路由映射器的设计

转载请注明出处:http://blog.csdn.net/linxdcn/article/details/73863126


1 概述

如果你对Tomcat的整个框架、组件、请求流程不熟悉,建议你先阅读以下3篇Tomcat概述性的文章,再来看本篇文章:

【Tomcat9源码分析】组件与框架概述
【Tomcat9源码分析】生命周期、启动、停止概述
【Tomcat9源码分析】请求过程概述

在Tomcat中,当一个请求到达时,该请求最终由哪个Servlet来处理呢?这个任务是由Mapper路由映射器完成的。Mapper是由Service管理。


2 存储结构

Mapper所用到内部类可用下面这张图概括:



2.1 MapElement

protected abstract static class MapElement<T> {

    public final String name;   // 名字

    public final T object;      // 对应的对象,如host, context, wrapper

}

MapElement 是下面所有类的基类。

2.2 MappedHost

protected static final class MappedHost extends MapElement<Host> {

    // host包含的context列表,即MappedContext数组的包装
    public volatile ContextList contextList;
}

2.3 MappedContext

protected static final class MappedContext extends MapElement<Void> {

    // 一个Context可能会对应许多个不同的版本的context,一般情况下是1个
    public volatile ContextVersion[] versions;

}

其中ContextVersion包含了Context下的所有Servlet,有多种映射方式,如精确的map,通配符的map,扩展名的map,如下:

protected static final class ContextVersion extends MapElement<Context> {

    // 对wrapper的精确的map
    public MappedWrapper[] exactWrappers = new MappedWrapper[0];

    // 基于通配符的map
    public MappedWrapper[] wildcardWrappers = new MappedWrapper[0];

    // 基于扩展名的map
    public MappedWrapper[] extensionWrappers = new MappedWrapper[0];
}

2.4 MappedWrapper

protected static class MappedWrapper extends MapElement<Wrapper> {
    public final boolean jspWildCard;
    public final boolean resourceOnly;
}

3 Mapper类设计

简单地说,Mapper中以数组的形式保存了host, context, wrapper, 且他们在数组中有序的,Mapper可以通过请求的url,通过二分法查找定位到wrapper。

public final class Mapper {

    // host数组,host里面又包括了context和wrapper数组
    volatile MappedHost[] hosts = new MappedHost[0];

    // 下面三个是添加host, context, wrapper的函数,都是同步的
    // 而且保证添加后是有序的
    public synchronized void addHost(String name, String[] aliases, Host host) { }

    public void addContextVersion(String hostName, Host host, String path,
            String version, Context context, String[] welcomeResources,
            WebResourceRoot resources, Collection<WrapperMappingInfo> wrappers) {

    }

    protected void addWrapper(ContextVersion context, String path,
            Wrapper wrapper, boolean jspWildCard, boolean resourceOnly) {

    }

    // 根据name,查找一个MapElement(host, context, 或者wrapper)
    private static final <T> int find(MapElement<T>[] map, CharChunk name,
            int start, int end) {

        // ...省略

        // 核心是二分法
        while (true) {
            i = (b + a) / 2;
            int result = compare(name, start, end, map[i].name);
            if (result == 1) {
                a = i;
            } else if (result == 0) {
                return i;
            } else {
                b = i;
            }
            if ((b - a) == 1) {
                int result2 = compare(name, start, end, map[b].name);
                if (result2 < 0) {
                    return a;
                } else {
                    return b;
                }
            }
        }
    }
}

4 实现过程

下面介绍在一次http请求中,哪个环节调用了Mapper作路由映射,已经路由映射的过程。

【Tomcat9源码分析】请求过程概述已经介绍了http请求经过http11Processor解析之后,会调用CoyoteAdapterservice()函数转发给Container,我们来详细看一下这一步。

public class CoyoteAdapter implements Adapter {

    @Override
    public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
            throws Exception {      
        // ...省略
        connector.getService().getMapper().map(serverName, decodedURI,
                version, request.getMappingData());
    }   
}

CoyoteAdapter会获取Connector中的Service中的Mapper,然后调用map()方法。

public final class Mapper {

    public void map(MessageBytes host, MessageBytes uri, String version,
            MappingData mappingData) throws IOException {
        // ...省略
        // 1 调用私有方法internalMap,传入host, uri, version, 结果将会保存在mappingData中
        internalMap(host.getCharChunk(), uri.getCharChunk(), version,
                mappingData);
    }

    private final void internalMap(CharChunk host, CharChunk uri,
            String version, MappingData mappingData) throws IOException {

        // ...省略

        // 2 查找映射的host
        MappedHost[] hosts = this.hosts;
        // 跟mapper的find方法一样,采用二分法查找
        MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
        mappingData.host = mappedHost.object;

        // 3 查找映射的context
        // ...省略
        contextVersion = exactFind(contextVersions, version);


        // 4 查找映射的wrapper
        if (!contextVersion.isPaused()) {
            internalMapWrapper(contextVersion, uri, mappingData);
        }
    }
}
  1. 调用私有方法internalMap,传入host, uri, version, 结果将会保存在request的mappingData中
  2. 查找映射的host
  3. 查找映射的context
  4. 查找映射的wrapper

转载请注明出处:http://blog.csdn.net/linxdcn/article/details/73863126

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页