tomcat源码解析(四)--请求过程之路径的匹配

时间:2022-01-29 06:26:58

好了接着上篇分析,tomcat分析完请求头之后,是怎么把请求发送给对应的servlet的呢?
看到org.apache.coyote.http11.Http11Processor类的service方法中,有这么一句:

getAdapter().service(request, response);

这里的request会把浏览器的发过来的信息,解析之后放在这里.
跟进,service方法,部分代码如下:

  @Override
    public void service(org.apache.coyote.Request req,
                        org.apache.coyote.Response res)
        throws Exception {
        Request request = (Request) req.getNote(ADAPTER_NOTES);
        Response response = (Response) res.getNote(ADAPTER_NOTES);
        ......
        // 这部分把org.apache.coyote.Request和org.apache.coyote.Response
        // 包装成平时我们写servlet需要的request 和response 
        if (request == null) {
            request = connector.createRequest();
            request.setCoyoteRequest(req);
            response = connector.createResponse();
            response.setCoyoteResponse(res);

            request.setResponse(response);
            response.setRequest(request);
            req.setNote(ADAPTER_NOTES, request); 
            res.setNote(ADAPTER_NOTES, response);

            req.getParameters().setQueryStringEncoding
                (connector.getURIEncoding());

        }       
        ......
        req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
        postParseSuccess = postParseRequest(req, request, res, response);// 在这里匹配路径

        if (postParseSuccess) {
            ......
            connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);// 这里传递请求
        }
        ...... 
    }

看到postParseRequest,会在该方法里面索引到我们之前放在mapper里面的host的信息

postParseSuccess = postParseRequest(req, request, res, response);

postParseRequest的部分代码如下:

   protected boolean postParseRequest(org.apache.coyote.Request req, Request request,
            org.apache.coyote.Response res, Response response) throws IOException, ServletException {

        ......
        MessageBytes serverName; // 获取服务器的名字,比如localhost
        ......

        String version = null;
        Context versionContext = null;
        boolean mapRequired = true;

        while (mapRequired) { 
            connector.getService().getMapper().map(serverName, decodedURI,
                    version, request.getMappingData()); //在这里匹配路径---

            // 找不到context,就是我们发布的应用
            if (request.getContext() == null) { 
                res.setStatus(404); 
                res.setMessage("Not found");
                    ......
                return false;
            }
            ......
        }
        ......

        return false;
        }
        ......
        return true;
    }

根据之前的分析知道,生成的host容器会存放在service类的mapper属性里面,所以,getMapper就会获取到service里面的mapper的.
看到connector.getService().getMapper().map的方法,该方法传入的参数分别为:
serverName就是之前host的名字,如

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

Host的标签name,就是localhost. decodedURI请求路径如:/index,version版本号,
request.getMappingData(),还有的就是mappingData用来储存host和host下所有的webapp的类.等下索引到的host信息就放在这个属性下了.Mapper类的map方法里面会调用internalMap方法,部分代码如下:

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

        // 通过主机名,如localhost找到host容器
        MappedHost[] hosts = this.hosts;
        MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
        if (mappedHost == null) {
            if (defaultHostName == null) {
                return;
            }
            mappedHost = exactFind(hosts, defaultHostName);
            if (mappedHost == null) {
                return;
            }
        }
        mappingData.host = mappedHost.object;

        ContextList contextList = mappedHost.contextList;
        MappedContext[] contexts = contextList.contexts;
        int pos = find(contexts, uri);
        ......
        int lastSlash = -1;
        int uriEnd = uri.getEnd();
        int length = -1;
        boolean found = false;
        MappedContext context = null;
        ......
        while (pos >= 0) {
            context = contexts[pos];
            if (uri.startsWith(context.name)) { // 在这里匹配webapp的
                length = context.name.length();
                if (uri.getLength() == length) {
                    found = true;
                    break;
                } else if (uri.startsWithIgnoreCase("/", length)) {
                    found = true;
                    break;
                }
            }
            if (lastSlash == -1) {
                lastSlash = nthSlash(uri, contextList.nesting + 1);
            } else {
                lastSlash = lastSlash(uri);
            }
            uri.setEnd(lastSlash);
            pos = find(contexts, uri);
        }
        ......
        // 在这里匹配filter和servlet的
        if (!contextVersion.isPaused()) {
            internalMapWrapper(contextVersion, uri, mappingData);
        }

    }

该方法主要以下工作:
1,通过servername在mapper里面找到host容器.
2,然后通过请求路径在找到的host里面找到context容器.
3,接着通过请求路径,知道wrapper,也就保存了servelet的包装类.
好了匹配host,context,和wrapper的结束之后,继续看到,org.apache.coyote.http11.Http11Processor类的service方法中的

 connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

这个invoke方法会在一次调用放在pipeline类里面的invoke方法,在这里我们只需要看到org.apache.catalina.core.StandardWrapperValve里面的invoke方法即可,部分代码如下:

  @Override
    public final void invoke(Request request, Response response)
        throws IOException, ServletException {
        ......
        StandardWrapper wrapper = (StandardWrapper) getContainer();
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();
        ......  

        if (!unavailable) {
            servlet = wrapper.allocate(); // 在这里创建servlet的实例,有的话就重用,没有的话就加载
        }

        ......
        ApplicationFilterChain filterChain =
                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

        if ((servlet != null) && (filterChain != null)) {
            if (context.getSwallowOutput()) {

                    ......
                    filterChain.doFilter(request.getRequest(),
                        response.getResponse());
                   ......
                } else {

                    ......
                }
            }
        ......

            if (servlet != null) {
                wrapper.deallocate(servlet);
            }
        ......
    }

该方法主要做了以下几个工作:
1,索引到servlet
2,创建过滤器链,也就是filterChain

因为之前在分析web.xml的时候就分析过servlet的创建了,在这里就不再分析.现在主要分析的是filterChain的创建,
在分析web.xml的时候知道filter的信息是放在FilterDef里面的,而FilterDef又保存在standardContext里面,代码如下:

     for (FilterDef filter : webxml.getFilters().values()) {
            ......
            context.addFilterDef(filter); // 添加过滤器
        }
        for (FilterMap filterMap : webxml.getFilterMappings()) {
            context.addFilterMap(filterMap);
        }

看到

ApplicationFilterChain filterChain =
                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

createFilterChain的部分代码如下:

    public static ApplicationFilterChain createFilterChain(ServletRequest request,
            Wrapper wrapper, Servlet servlet) {

       ......

        ApplicationFilterChain filterChain = null;
        if (request instanceof Request) {
            Request req = (Request) request;
            if (Globals.IS_SECURITY_ENABLED) {

                filterChain = new ApplicationFilterChain();
            } else {
                filterChain = (ApplicationFilterChain) req.getFilterChain();
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
            filterChain = new ApplicationFilterChain();
        }

        filterChain.setServlet(servlet);
        ......

        StandardContext context = (StandardContext) wrapper.getParent();
        FilterMap filterMaps[] = context.findFilterMaps();
        ......
        String servletName = wrapper.getName();

        // 根据路径匹配的过滤器
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMaps[i], requestPath))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {

                continue;
            }
            filterChain.addFilter(filterConfig);
        }

        // 根据servlet匹配的过滤器
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMaps[i], servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                continue;
            }
            filterChain.addFilter(filterConfig);
        }
        return filterChain;
    }

该方法主要是创建filterChain,还有选择匹配路径的过滤器.
接着看回invoke方法中有:

filterChain.doFilter(request.getRequest(),
                        response.getResponse());

看到这里相信大家都很熟悉了,就不用详细讲了.

分析到这里tomcat的整个请求过程基本分析完了.后面有时间的话会详细分析,tomcat是如何解析客户端发过来的信息的,比如:如何处理get和post发送过来的数据,以及如何处理servlet3.0支持的异步发送信息的方.

在分析源码的过程中,难免有错漏和不足之处,望各位海涵吧.如果大家在发现有错漏之处,可以给我留言.