Tomcat 系列篇十-介绍下 Tomcat 里的 Mapper 作用

Mapper 顾名思义是作一个映射作用,在 Tomcat 中会根据域名找到 host 组件,再根据 uri 可以找到对应的 context 和 wrapper 组件,但是对于当前这个环境 (Springboot) 会有一点小区别
之前说到
请求会经过 coyote 适配器进行进一步处理,
org.apache.coyote.http11.Http11Processor#service

// Process the request in the adapter
if (getErrorState().isIoAllowed()) {
    try {
        rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
        getAdapter().service(request, response);

然后到 coyoteAdapter 的 service
org.apache.catalina.connector.CoyoteAdapter#service

try {
    // Parse and set Catalina and configuration specific
    // request parameters
    postParseSuccess = postParseRequest(req, request, res, response);
    if (postParseSuccess) {
        //check valves if we support async
        request.setAsyncSupported(
                connector.getService().getContainer().getPipeline().isAsyncSupported());
        // Calling the container
        // ----------> 到这就是调用 pipeline 去处理了,我们要关注上面的 postParseRequest
        connector.getService().getContainer().getPipeline().getFirst().invoke(
                request, response);
    }

主要先看到 postParseRequest
在 postParseRequest 的代码里会调用 Mapper 的 map 方法

while (mapRequired) {
    // This will map the the latest version by default
    connector.getService().getMapper().map(serverName, decodedURI,
            version, request.getMappingData());

    // If there is no context at this point, either this is a 404
    // because no ROOT context has been deployed or the URI was invalid
    // so no context could be mapped.
    if (request.getContext() == null) {
        // Allow processing to continue.

而后面的 context 就是在 map 方法里处理塞进去的
往里看就是 org.apache.catalina.mapper.Mapper#internalMap
第一步找的是 host

// Virtual host mapping
        MappedHost[] hosts = this.hosts;
        MappedHost mappedHost = exactFindIgnoreCase(hosts, host);

而在后续代码里继续设置 context

if (contextVersion == null) {
            // Return the latest version
            // The versions array is known to contain at least one element
            contextVersion = contextVersions[versionCount - 1];
        }
        mappingData.context = contextVersion.object;

然后是 wrapper

// Wrapper mapping
if (!contextVersion.isPaused()) {
    internalMapWrapper(contextVersion, uri, mappingData);
}

在这个方法 org.apache.catalina.mapper.Mapper#internalMapWrapper

// Rule 7 -- Default servlet
if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
    if (contextVersion.defaultWrapper != null) {
        mappingData.wrapper = contextVersion.defaultWrapper.object;
        mappingData.requestPath.setChars
            (path.getBuffer(), path.getStart(), path.getLength());
        mappingData.wrapperPath.setChars
            (path.getBuffer(), path.getStart(), path.getLength());
        mappingData.matchType = MappingMatch.DEFAULT;

这里就设置了 wrapper ,因为我们是在 Springboot 中,所以只有默认的 dispatchServlet
上面主要是在请求处理过程中的查找映射过程,一开始的注册是从 MapperListener 开始的
MapperListener 继承了 LifecycleMbeanBase,也就是有了 Lifecycle 状态变化那一套

@Override
public void startInternal() throws LifecycleException {

    setState(LifecycleState.STARTING);

    Engine engine = service.getContainer();
    if (engine == null) {
        return;
    }

    findDefaultHost();

    addListeners(engine);

    Container[] conHosts = engine.findChildren();
    for (Container conHost : conHosts) {
        Host host = (Host) conHost;
        if (!LifecycleState.NEW.equals(host.getState())) {
            // Registering the host will register the context and wrappers
            registerHost(host);
        }
    }
}

在启动过程中就会去把 engine 的子容器 host 找出来进行注册,就是调用 registerHost 方法

private void registerHost(Host host) {

    String[] aliases = host.findAliases();
    mapper.addHost(host.getName(), aliases, host);

    for (Container container : host.findChildren()) {
        if (container.getState().isAvailable()) {
            registerContext((Context) container);
        }
    }

    // Default host may have changed
    findDefaultHost();

    if(log.isDebugEnabled()) {
        log.debug(sm.getString("mapperListener.registerHost",
                host.getName(), domain, service));
    }
}

在这里面会添加 host 组件,注册 context 等,注册context 里还会处理 wrapper 的添加记录

private void registerContext(Context context) {

    String contextPath = context.getPath();
    if ("/".equals(contextPath)) {
        contextPath = "";
    }
    Host host = (Host)context.getParent();

    WebResourceRoot resources = context.getResources();
    String[] welcomeFiles = context.findWelcomeFiles();
    List<WrapperMappingInfo> wrappers = new ArrayList<>();

    for (Container container : context.findChildren()) {
        prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);

        if(log.isDebugEnabled()) {
            log.debug(sm.getString("mapperListener.registerWrapper",
                    container.getName(), contextPath, service));
        }
    }

    mapper.addContextVersion(host.getName(), host, contextPath,
            context.getWebappVersion(), context, welcomeFiles, resources,
            wrappers);

    if(log.isDebugEnabled()) {
        log.debug(sm.getString("mapperListener.registerContext",
                contextPath, service));
    }
}

就是在 org.apache.catalina.mapper.Mapper#addContextVersion 方法中

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

    hostName = renameWildcardHost(hostName);

    MappedHost mappedHost  = exactFind(hosts, hostName);
    if (mappedHost == null) {
        addHost(hostName, new String[0], host);
        mappedHost = exactFind(hosts, hostName);
        if (mappedHost == null) {
            log.error(sm.getString("mapper.addContext.noHost", hostName));
            return;
        }
    }
    if (mappedHost.isAlias()) {
        log.error(sm.getString("mapper.addContext.hostIsAlias", hostName));
        return;
    }
    int slashCount = slashCount(path);
    synchronized (mappedHost) {
        ContextVersion newContextVersion = new ContextVersion(version,
                path, slashCount, context, resources, welcomeResources);
        if (wrappers != null) {
            addWrappers(newContextVersion, wrappers);
        }

大致介绍了下 Mapper 的逻辑