Spring 源码解析 -- SpringWeb请求映射解析


简介

基于上篇请求路径初步探索,了解到了一个请求到具体处理方法的大致路径,本篇就继续探索,看下路径是如何匹配到处理方法的

概览

基于上篇: Spring Web 请求初探

我们大致定位到了请求路径到处理方法的关键代码在类中:DispatcherServlet.java

具体的代码如下:

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

mappedHandler 的获取就是关键了,下面我们就具体看下如何获取请求的mappedHandler的

源码解析

获取 mappedHandler

在类:DispatcherServlet.java 中,我们定位到 mappedHandler 获取的关键代码

// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
    noHandlerFound(processedRequest, response);
    return;
}

通过上面的代码,我们可以看到是通过请求进行获取,我们还注意到了为null的情况,如下代码,大意就是经典的404

/**
* No handler found -> set appropriate HTTP response status.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception if preparing the response failed
*/
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (pageNotFoundLogger.isWarnEnabled()) {
        pageNotFoundLogger.warn("No mapping for " + request.getMethod() + " " + getRequestUri(request));
    }
    if (this.throwExceptionIfNoHandlerFound) {
        throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
                new ServletServerHttpRequest(request).getHeaders());
    }
    else {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
    }
}

我们进入 getHandler 函数,看看其具体处理逻辑,代码如下:

/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
    for (HandlerMapping mapping : this.handlerMappings) {
        HandlerExecutionChain handler = mapping.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    }
    return null;
}

从上面代码大意可以看出:循环遍历一个关于请求匹配的Map,如果某个Handler匹配成功则返回

这个Map是如何初始化、具体包含哪些内容?

HandlerExecutionChain 具体是什么?

这部分疑问后面再看,感觉是个大工程,当前并不影响本篇的解析

请求与Mapper匹配

接着跟下去,到类:AbstractHandlerMethodMapping.java

我们来到了一个比较关键的处理,我们可以看到这里有相关的匹配逻辑,里面有很多的处理,有些目前都不知道有啥作用,后面查查资料后咱们再回过头来看看

    @Override
    @Nullable
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = getHandlerInternal(request);

        // 什么情况下会匹配到默认的Handler?
        if (handler == null) {
            handler = getDefaultHandler();
        }
        if (handler == null) {
            return null;
        }

        // 什么情况下获取到的Bean是一个String?
        // 需要从 obtainApplicationContext 中获取
        // Bean name or resolved handler?
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = obtainApplicationContext().getBean(handlerName);
        }

        // 什么情况下会用到缓存?
        // Ensure presence of cached lookupPath for interceptors and others
        if (!ServletRequestPathUtils.hasCachedPath(request)) {
            initLookupPath(request);
        }

        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

        if (logger.isTraceEnabled()) {
            logger.trace("Mapped to " + handler);
        }
        else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
            logger.debug("Mapped to " + executionChain.getHandler());
        }

        if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
            CorsConfiguration config = getCorsConfiguration(handler, request);
            if (getCorsConfigurationSource() != null) {
                CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
                config = (globalConfig != null ? globalConfig.combine(config) : config);
            }
            if (config != null) {
                config.validateAllowCredentials();
            }
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }

        return executionChain;
    }

其中的 getHandlerExecutionChain 还挺可疑,感觉也是关键代码,给设置了一堆的我们熟悉的 Interceptors,这里做个标记,后面再探索下

    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

        for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
            if (interceptor instanceof MappedInterceptor) {
                MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                if (mappedInterceptor.matches(request)) {
                    chain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            }
            else {
                chain.addInterceptor(interceptor);
            }
        }
        return chain;
    }

Handler匹配

下面就跟着断点来到了:AbstractHandlerMapping.java

看到下面的关键代码:在查找匹配的时候上了一个锁,进入具体的查找逻辑

@Override
@Nullable
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 这里一些处理得到我们的请求路径:”/"
    String lookupPath = initLookupPath(request);
    this.mappingRegistry.acquireReadLock();
    try {
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
        this.mappingRegistry.releaseReadLock();
    }
}

我们进入类:AbstractHandlerMethodMapping.java 中,查看具体的处理逻辑

我们可以看到这里进行了一系列的匹配,如果为空还能取默认值,这个默认值是个啥?

并且还有多个匹配上的还不会报错的情况,瞬间感觉Web这块还是有很多门道啊,但这些细节后面我们再看看,继续看下匹配的逻辑

    @Nullable
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<>();
        // 在这里获取到了相关的Handler,就是一个简单的Map取值(如后面的代码),那个Map如何初始化的得后面看看了
        List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
        if (directPathMatches != null) {
            addMatchingMappings(directPathMatches, matches, request);
        }

        // 如果没有匹配上,这个默认值又是如何处理的呢?
        if (matches.isEmpty()) {
            addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
        }

        // 这里看到一些非常有趣的处理
        // 如果同时匹配上了两个,好像不是一定报错?
        if (!matches.isEmpty()) {
            Match bestMatch = matches.get(0);
            if (matches.size() > 1) {
                Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
                matches.sort(comparator);
                bestMatch = matches.get(0);
                if (logger.isTraceEnabled()) {
                    logger.trace(matches.size() + " matching mappings: " + matches);
                }
                // 这个的返回是什么意思?
                if (CorsUtils.isPreFlightRequest(request)) {
                    for (Match match : matches) {
                        if (match.hasCorsConfig()) {
                            return PREFLIGHT_AMBIGUOUS_MATCH;
                        }
                    }
                }
                else {
                    Match secondBestMatch = matches.get(1);
                    if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                        Method m1 = bestMatch.getHandlerMethod().getMethod();
                        Method m2 = secondBestMatch.getHandlerMethod().getMethod();
                        String uri = request.getRequestURI();
                        throw new IllegalStateException(
                                "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
                    }
                }
            }
            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
            handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.getHandlerMethod();
        }
        else {
            return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
        }
    }

一个简单的Map取值(如后面的代码),那个Map如何初始化的得后面看看了

class MappingRegistry {

    private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();

    @Nullable
    public List<T> getMappingsByDirectPath(String urlPath) {
        return this.pathLookup.get(urlPath);
    }
}

到这里就匹配成功并返回了,但通过调试看到不是我们熟悉的类和方法

继续看看这行的具体处理逻辑:return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);

看看类 HandlerMethod 的一些方法和构造函数:

下面函数我们看到了属性的获取Bean

    public HandlerMethod createWithResolvedBean() {
        Object handler = this.bean;
        if (this.bean instanceof String) {
            Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
            String beanName = (String) this.bean;
            handler = this.beanFactory.getBean(beanName);
        }
        return new HandlerMethod(this, handler);
    }

在其构造函数中,直接将Bean和Method进行了初始化,这个如何初始化的我们后面再看

    public HandlerMethod(Object bean, Method method) {
        Assert.notNull(bean, "Bean is required");
        Assert.notNull(method, "Method is required");
        this.bean = bean;
        this.beanFactory = null;
        this.beanType = ClassUtils.getUserClass(bean);
        this.method = method;
        this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
        this.parameters = initMethodParameters();
        evaluateResponseStatus();
        this.description = initDescription(this.beanType, this.method);
    }

总结

本篇文章探索了下具体的请求如何匹配到处理方法的相关处理函数,了解到了核心逻辑就是遍历一个: handlerMappings

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
    for (HandlerMapping mapping : this.handlerMappings) {
        HandlerExecutionChain handler = mapping.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    }
    return null;
}

后面的具体匹配逻辑也有很多值得探索的细节,但接下来更重要的是弄清楚这个: handlerMappings ,是如何初始化和加载的

下面的文章我们会继续探索