Spring MVC请求处理(二) - HandlerMappi

2019-03-23  本文已影响0人  buzzerrookie

DispatcherServlet的doDispatch方法利用getHandler获取与请求匹配的HandlerExecutionChain,getHandler方法代码如下,可以看到该方法从已有的HandlerMapping中返回第一个匹配该请求的HandlerExecutionChain,若没有匹配则返回null。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    for (HandlerMapping hm : this.handlerMappings) {
        if (logger.isTraceEnabled()) {
            logger.trace(
                    "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
        }
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    return null;
}

这里的HandlerMapping和HandlerExecutionChain是什么呢?

HandlerMapping接口

HandlerMapping接口定义了请求到处理器对象的映射,处理器会被包装成处理器执行链HandlerExecutionChain。HandlerMapping接口的类层次结构如下图所示:


HandlerMapping接口的类层次结构.png

HandlerMapping接口的代码如下所示,除了常量定义外只有一个接口方法。

public interface HandlerMapping {

    String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";

    String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";

    String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";

    String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";

    String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";

    String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

getHandler方法返回与请求匹配的执行链,若没有匹配则返回null,其Javadoc如下所述:

Return a handler and any interceptors for this request. The choice may be made on request URL, session state, or any factor the implementing class chooses.
The returned HandlerExecutionChain contains a handler Object, rather than even a tag interface, so that handlers are not constrained in any way. For example, a HandlerAdapter could be written to allow another framework's handler objects to be used.
Returns null if no match was found. This is not an error. The DispatcherServlet will query all registered HandlerMapping beans to find a match, and only decide there is an error if none can find a handler.

下面以常用的RequestMappingHandlerMapping为例分析请求与处理器的匹配过程。

AbstractHandlerMapping类

AbstractHandlerMapping类是实现了HandlerMapping接口的抽象类。

成员变量

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
    private Object defaultHandler;

    private UrlPathHelper urlPathHelper = new UrlPathHelper();

    private PathMatcher pathMatcher = new AntPathMatcher();

    private final List<Object> interceptors = new ArrayList<Object>();

    private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();

    private int order = Ordered.LOWEST_PRECEDENCE;  // default: same as non-Ordered

    // 省略一些代码
    
    public void setOrder(int order) {
        this.order = order;
    }

    @Override
    public int getOrder() {
        return this.order;
    }
}

初始化

AbstractHandlerMapping类继承了WebApplicationObjectSupport类,重写了initApplicationContext方法以自定义初始化过程。

@Override
protected void initApplicationContext() throws BeansException {
    extendInterceptors(this.interceptors);
    detectMappedInterceptors(this.adaptedInterceptors);
    initInterceptors();
}

protected void extendInterceptors(List<Object> interceptors) {
}

protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
    mappedInterceptors.addAll(
            BeanFactoryUtils.beansOfTypeIncludingAncestors(
                    getApplicationContext(), MappedInterceptor.class, true, false).values());
}

初始化的过程做了以下三件事:

protected void initInterceptors() {
    if (!this.interceptors.isEmpty()) {
        for (int i = 0; i < this.interceptors.size(); i++) {
            Object interceptor = this.interceptors.get(i);
            if (interceptor == null) {
                throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
            }
            this.adaptedInterceptors.add(adaptInterceptor(interceptor));
        }
    }
}

protected HandlerInterceptor adaptInterceptor(Object interceptor) {
    if (interceptor instanceof HandlerInterceptor) {
        return (HandlerInterceptor) interceptor;
    }
    else if (interceptor instanceof WebRequestInterceptor) {
        return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
    }
    else {
        throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
    }
}

setter方法

AbstractHandlerMapping类的部分setter方法如下所示,除了setUrlPathHelper和setPathMatcher设置了成员变量外,其他都用来设置urlPathHelper成员变量的属性。

public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
    this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
    this.globalCorsConfigSource.setAlwaysUseFullPath(alwaysUseFullPath);
}

public void setUrlDecode(boolean urlDecode) {
    this.urlPathHelper.setUrlDecode(urlDecode);
    this.globalCorsConfigSource.setUrlDecode(urlDecode);
}

public void setRemoveSemicolonContent(boolean removeSemicolonContent) {
    this.urlPathHelper.setRemoveSemicolonContent(removeSemicolonContent);
    this.globalCorsConfigSource.setRemoveSemicolonContent(removeSemicolonContent);
}

public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
    Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
    this.urlPathHelper = urlPathHelper;
    this.globalCorsConfigSource.setUrlPathHelper(urlPathHelper);
}

public void setPathMatcher(PathMatcher pathMatcher) {
    Assert.notNull(pathMatcher, "PathMatcher must not be null");
    this.pathMatcher = pathMatcher;
    this.globalCorsConfigSource.setPathMatcher(pathMatcher);
}

getHandler接口方法

AbstractHandlerMapping类利用模板方法模式实现了HandlerMapping的getHandler接口方法,子类需要重写getHandlerInternal方法以实现功能,代码如下所示。

@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    Object handler = getHandlerInternal(request);
    if (handler == null) {
        handler = getDefaultHandler();
    }
    if (handler == null) {
        return null;
    }
    // Bean name or resolved handler?
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = getApplicationContext().getBean(handlerName);
    }

    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    if (CorsUtils.isCorsRequest(request)) {
        CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
        CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
        CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
        executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }
    return executionChain;
}

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

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

getHandlerExecutionChain函数的包装过程如下:

  1. 利用处理器对象生成一个HandlerExecutionChain对象,HandlerExecutionChain的getHandler返回的即是该处理器对象,这会用于doDispatch的后续过程;
  2. 利用UrlPathHelper获得该请求的查找路径;
  3. 为HandlerExecutionChain添加拦截器,添加时需要区分拦截器的类型,这是因为MappedInterceptor只会应用于其路径模式与请求URL匹配的请求。对所有拦截器,若不是MappedInterceptor类型那么直接添加到HandlerExecutionChain中;否则判断查找路径是否与MappedInterceptor的路径模式相匹配,匹配才添加到HandlerExecutionChain中。

AbstractHandlerMethodMapping类

AbstractHandlerMethodMapping类是抽象泛型类,它继承AbstractHandlerMapping类并实现了InitializingBean接口,类名暗示它返回的处理器是HandlerMethod类型,泛型参数T表示一种映射,这个映射含有将处理器匹配到请求所需的条件。请注意,如不加说明,本文中的映射均指这种映射,而不是数据结构的Map。

成员变量

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    private boolean detectHandlerMethodsInAncestorContexts = false;

    private HandlerMethodMappingNamingStrategy<T> namingStrategy;

    private final MappingRegistry mappingRegistry = new MappingRegistry();

    public void setDetectHandlerMethodsInAncestorContexts(boolean detectHandlerMethodsInAncestorContexts) {
        this.detectHandlerMethodsInAncestorContexts = detectHandlerMethodsInAncestorContexts;
    }

    public void setHandlerMethodMappingNamingStrategy(HandlerMethodMappingNamingStrategy<T> namingStrategy) {
        this.namingStrategy = namingStrategy;
    }

    public HandlerMethodMappingNamingStrategy<T> getNamingStrategy() {
        return this.namingStrategy;
    }

    // 省略一些代码
}

初始化

AbstractHandlerMethodMapping类既继承了AbstractHandlerMapping类又实现了InitializingBean接口,因此重写了afterPropertiesSet方法。

@Override
public void afterPropertiesSet() {
    initHandlerMethods();
}

上文提到AbstractHandlerMapping类的initApplicationContext方法也用于初始化过程,这里需要注意这些回调方法的执行顺序:

因此AbstractHandlerMethodMapping初始化时会先初始化所有的拦截器,然后调用initHandlerMethods发现所有HandlerMethod,代码如下:

protected void initHandlerMethods() {
    if (logger.isDebugEnabled()) {
        logger.debug("Looking for request mappings in application context: " + getApplicationContext());
    }
    String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
            BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
            getApplicationContext().getBeanNamesForType(Object.class));

    for (String beanName : beanNames) {
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            Class<?> beanType = null;
            try {
                beanType = getApplicationContext().getType(beanName);
            }
            catch (Throwable ex) {
                // An unresolvable bean type, probably from a lazy bean - let's ignore it.
                if (logger.isDebugEnabled()) {
                    logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                }
            }
            if (beanType != null && isHandler(beanType)) {
                detectHandlerMethods(beanName);
            }
        }
    }
    handlerMethodsInitialized(getHandlerMethods());
}

protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) {
}

protected abstract boolean isHandler(Class<?> beanType);

下面重点看一下detectHandlerMethods方法:

protected void detectHandlerMethods(final Object handler) {
    Class<?> handlerType = (handler instanceof String ?
            getApplicationContext().getType((String) handler) : handler.getClass());
    final Class<?> userType = ClassUtils.getUserClass(handlerType);

    Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
            new MethodIntrospector.MetadataLookup<T>() {
                @Override
                public T inspect(Method method) {
                    try {
                        return getMappingForMethod(method, userType);
                    }
                    catch (Throwable ex) {
                        throw new IllegalStateException("Invalid mapping on handler class [" +
                                userType.getName() + "]: " + method, ex);
                    }
                }
            });

    if (logger.isDebugEnabled()) {
        logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
    }
    for (Map.Entry<Method, T> entry : methods.entrySet()) {
        Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
        T mapping = entry.getValue();
        registerHandlerMethod(handler, invocableMethod, mapping);
    }
}

protected abstract T getMappingForMethod(Method method, Class<?> handlerType);

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    this.mappingRegistry.register(mapping, handler, method);
}

MappingRegistration内部类

MappingRegistration类是AbstractHandlerMethodMapping的私有静态内部类,有构造函数和getter方法,没有setter方法,表示映射的注册(见下文分析)信息。

private static class MappingRegistration<T> {
    private final T mapping;

    private final HandlerMethod handlerMethod;

    private final List<String> directUrls;

    private final String mappingName;

    public MappingRegistration(T mapping, HandlerMethod handlerMethod, List<String> directUrls, String mappingName) {
        Assert.notNull(mapping, "Mapping must not be null");
        Assert.notNull(handlerMethod, "HandlerMethod must not be null");
        this.mapping = mapping;
        this.handlerMethod = handlerMethod;
        this.directUrls = (directUrls != null ? directUrls : Collections.<String>emptyList());
        this.mappingName = mappingName;
    }

    public T getMapping() {
        return this.mapping;
    }

    public HandlerMethod getHandlerMethod() {
        return this.handlerMethod;
    }

    public List<String> getDirectUrls() {
        return this.directUrls;
    }

    public String getMappingName() {
        return this.mappingName;
    }
}

MappingRegistry内部类

MappingRegistry类是AbstractHandlerMethodMapping的内部类,保存了与映射有关的全部信息。

1. 成员变量
class MappingRegistry {
    private final Map<T, MappingRegistration<T>> registry = new HashMap<T, MappingRegistration<T>>();

    private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>();

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

    private final Map<String, List<HandlerMethod>> nameLookup =
            new ConcurrentHashMap<String, List<HandlerMethod>>();

    private final Map<HandlerMethod, CorsConfiguration> corsLookup =
            new ConcurrentHashMap<HandlerMethod, CorsConfiguration>();

    private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public Map<T, HandlerMethod> getMappings() {
        return this.mappingLookup;
    }

    public List<T> getMappingsByUrl(String urlPath) {
        return this.urlLookup.get(urlPath);
    }

    public List<HandlerMethod> getHandlerMethodsByMappingName(String mappingName) {
        return this.nameLookup.get(mappingName);
    }

    public CorsConfiguration getCorsConfiguration(HandlerMethod handlerMethod) {
        HandlerMethod original = handlerMethod.getResolvedFromHandlerMethod();
        return this.corsLookup.get(original != null ? original : handlerMethod);
    }

    public void acquireReadLock() {
        this.readWriteLock.readLock().lock();
    }

    public void releaseReadLock() {
        this.readWriteLock.readLock().unlock();
    }

    // 省略一些代码
}

成员变量的多数虽然都是Map,但用途不同:

2. 注册映射

将处理器和Java原始方法通过register和其他辅助方法注册映射,代码如下所示。

public void register(T mapping, Object handler, Method method) {
    this.readWriteLock.writeLock().lock();
    try {
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        assertUniqueMethodMapping(handlerMethod, mapping);

        if (logger.isInfoEnabled()) {
            logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
        }
        this.mappingLookup.put(mapping, handlerMethod);

        List<String> directUrls = getDirectUrls(mapping);
        for (String url : directUrls) {
            this.urlLookup.add(url, mapping);
        }

        String name = null;
        if (getNamingStrategy() != null) {
            name = getNamingStrategy().getName(handlerMethod, mapping);
            addMappingName(name, handlerMethod);
        }

        CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
        if (corsConfig != null) {
            this.corsLookup.put(handlerMethod, corsConfig);
        }

        this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
    }
    finally {
        this.readWriteLock.writeLock().unlock();
    }
}

经过上述分析,HandlerMethod的概念逐渐变得清晰。简单地说,它是对Java方法的包装,但不是所有的Java方法都能被包装成HandlerMethod去处理请求,只有符合某些条件的才是,这个条件的判断就在getMappingForMethod抽象方法中。

重写getHandlerInternal方法

AbstractHandlerMethodMapping类继承了AbstractHandlerMapping类,重写的getHandlerInternal方法如下,由lookupHandlerMethod方法完成查找匹配HandlerMethod的任务。

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    if (logger.isDebugEnabled()) {
        logger.debug("Looking up handler method for path " + lookupPath);
    }
    this.mappingRegistry.acquireReadLock();
    try {
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        if (logger.isDebugEnabled()) {
            if (handlerMethod != null) {
                logger.debug("Returning handler method [" + handlerMethod + "]");
            }
            else {
                logger.debug("Did not find handler method for [" + lookupPath + "]");
            }
        }
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
        this.mappingRegistry.releaseReadLock();
    }
}

查找匹配HandlerMethod的代码如下。

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<Match>();
    List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
    if (directPathMatches != null) {
        addMatchingMappings(directPathMatches, matches, request);
    }
    if (matches.isEmpty()) {
        // No choice but to go through all mappings...
        addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
    }

    if (!matches.isEmpty()) {
        Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
        Collections.sort(matches, comparator);
        if (logger.isTraceEnabled()) {
            logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
                    lookupPath + "] : " + matches);
        }
        Match bestMatch = matches.get(0);
        if (matches.size() > 1) {
            if (CorsUtils.isPreFlightRequest(request)) {
                return PREFLIGHT_AMBIGUOUS_MATCH;
            }
            Match secondBestMatch = matches.get(1);
            if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                Method m1 = bestMatch.handlerMethod.getMethod();
                Method m2 = secondBestMatch.handlerMethod.getMethod();
                throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
                        request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
            }
        }
        handleMatch(bestMatch.mapping, lookupPath, request);
        return bestMatch.handlerMethod;
    }
    else {
        return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
    }
}

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
    for (T mapping : mappings) {
        T match = getMatchingMapping(mapping, request);
        if (match != null) {
            matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
        }
    }
}

/**
 * Check if a mapping matches the current request and return a (potentially
 * new) mapping with conditions relevant to the current request.
 * @param mapping the mapping to get a match for
 * @param request the current HTTP servlet request
 * @return the match, or {@code null} if the mapping doesn't match
 */
protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);

抽象方法getMatchingMapping检查映射是否与请求相匹配,若匹配则返回一个与当前请求有关的映射,否则返回null。addMatchingMappings方法遍历mappings表示的映射集合,将与请求相匹配的映射包装成Match添加到匹配集matches。从上述代码可以总结出查找匹配HandlerMethod的过程:

  1. MappingRegistry的getMappingsByUrl方法代码如下,是从URL到映射的多值Map中取值,这个Map保存的是注册映射时直接URL路径(即不需要模式匹配)与映射的关联。
    public List<T> getMappingsByUrl(String urlPath) {
        return this.urlLookup.get(urlPath);
    }
    
    从mappingRegistry获取请求路径对应的映射(不需要模式匹配),这时只看URL路径而不会去管HTTP请求方法等条件,接着将与请求相匹配的映射加入匹配集;
  2. 若匹配集为空则表示没有能与请求路径直接匹配的映射(不需要模式匹配),此时需要遍历mappingRegistry中的所有映射进行模式匹配;
  3. 若能匹配请求路径,那么对这些匹配排序,排序方式由子类决定。若匹配数量有两个以上且最佳匹配和次最佳匹配相同则报错;
    protected abstract Comparator<T> getMappingComparator(HttpServletRequest request);
    
  4. handleMatch相当于回调函数,表示有匹配时的执行动作;
    protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) {
        request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
    }
    
  5. handleNoMatch也相当于回调函数,表示无匹配时的执行动作;
    protected HandlerMethod handleNoMatch(Set<T> mappings, String lookupPath, HttpServletRequest request)
            throws Exception {
        return null;
    }
    

RequestMappingInfoHandlerMapping类和RequestMappingHandlerMapping类的分析请看下文。

上一篇 下一篇

猜你喜欢

热点阅读