美文网首页
HandlerMapping简介

HandlerMapping简介

作者: Hogantry | 来源:发表于2019-03-03 16:48 被阅读0次

    1、HandlerMapping接口继承结构体系

    B0373966-7A31-4C09-912F-724CFA9243D6.png

    2、HandlerMapping接口功能

    HandlerMapping接口中只定义了一个方法

    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
    

    该方法很简单,就是根据请求的request,获取HandlerExecutionChain对象。

    3、HandlerMapping家族成员

    从HandlerMapping接口继承结构体系图中,我们可以发现HandlerMapping家族可以分为两支,一支继承AbstractUrlHandlerMapping,一支继承AbstractHandlerMethodMapping,这两支都继承自AbstractHandlerMapping。其中AbstractUrlHandlerMapping在目前大部分的项目使用中,已经很少使用到,本篇文章会稍微分析下SimpleUrlHandlerMapping子类。重点分析AbstractHandlerMethodMapping,他就是我们经常使用的@RequestMapping注解会使用到的方式。下面我们先分析这个父类,再分别分析这两个分支。

    4、AbstractHandlerMapping

    4.1、AbstractHandlerMapping概述

    AbstractHandlerMapping是HandlerMapping的抽象实现,采用模板模式设计了HandlerMapping的整体架构。其定义了getHandlerInternal方法,根据request来获取Handler,由子类来具体实现该方法。然后再根据request来获取相应的interceptors,整合从子类获取的Handler,组成HandlerExecutionChain对象返回。

    4.2、AbstractHandlerMapping初始化

    AbstractHandlerMapping继承了WebApplicationObjectSupport,初始化时会自动调用模板方法initApplicationContext,具体如下:

    @Override
    protected void initApplicationContext() throws BeansException {
        // 模板方法,暂无子类实现
        extendInterceptors(this.interceptors);
        // 从容器中获取实现了MappedInterceptor接口的对象,添加到adaptedInterceptors列表中
        detectMappedInterceptors(this.adaptedInterceptors);
         // 将interceptors(由子类添加)中的对象封装后添加到adaptedInterceptors列表中
        initInterceptors();
    }
    

    4.3 AbstractHandlerMapping的使用

    AbstractHandlerMapping的模板模式就是从如下方法开始的

    @Override
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        // 模板模式方法,具体由子类实现
        Object handler = getHandlerInternal(request);
        if (handler == null) {
            handler = getDefaultHandler();
        }
        if (handler == null) {
            return null;
        }
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = getApplicationContext().getBean(handlerName);
        }
        // 根据request从adaptedInterceptors中选择匹配的interceptors,与handler一起封装成HandlerExecutionChain对象
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        return executionChain;
    }
    

    下面具体分析两个分支AbstractUrlHandlerMapping和AbstractHandlerMethodMapping

    5、AbstractUrlHandlerMapping

    5.1 AbstractUrlHandlerMapping概述

    AbstractUrlHandlerMapping实现了父类的模板方法getHandlerInternal

    @Override
    protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        Object handler = lookupHandler(lookupPath, request);
        if (handler == null) {
            Object rawHandler = null;
            if ("/".equals(lookupPath)) {
                rawHandler = getRootHandler();
            }
            if (rawHandler == null) {
                rawHandler = getDefaultHandler();
            }
            if (rawHandler != null) {
                // Bean name or resolved handler?
                if (rawHandler instanceof String) {
                    String handlerName = (String) rawHandler;
                    rawHandler = getApplicationContext().getBean(handlerName);
                }
                validateHandler(rawHandler, request);
                handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
            }
        }
        if (handler != null && logger.isDebugEnabled()) {
            logger.debug("Mapping [" + lookupPath + "] to " + handler);
        }
        else if (handler == null && logger.isTraceEnabled()) {
            logger.trace("No handler mapping found for [" + lookupPath + "]");
        }
        return handler;
    }
    

    其中最重要的就是lookupHandler方法,具体代码就不贴了,大体思路是说一下。AbstractUrlHandlerMapping类中定义了一个Map<String, Object>类型的变量handlerMap,用来存储url与handler的对应关系,lookupHandler方法就是根据url在该变量中找到相应的handler。其中url是可以根据正则表达式来查找的,所以lookupHandler方法中还会根据url可能会查找到多个,然后根据正则的匹配关系找到最符合的handler对象返回。那handlerMap变量中的数据又是从哪来的呢?带着这个问题我们继续分析下去。我们看到AbstractUrlHandlerMapping类中提供了两个注册handler方法

    protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException 
    protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException 
    

    这两个方法就是提供给子类注册handler了的,子类通过调用这两个注册方法就可以将url与handler的对应关系存储到handlerMap变量中了。那问题又来了,子类又是如何获取url与handler的对应关系的呢?针对AbstractUrlHandlerMapping的子类,我们只分析下SimpleUrlHandlerMapping类。

    5.2 SimpleUrlHandlerMapping

    5.5.1 SimpleUrlHandlerMapping的使用

    上面也提过,目前大部分项目都是使用@RequestMapping的方式来定义Handler,SimpleUrlHandlerMapping类很少会使用到,所以这里先介绍下该类的使用,方便后续对其使用原理的讲解。

    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="simpleUrlController">simleUrl</prop>
            </props>
        </property>
    </bean>
    <bean id="simleUrl" class="com.zhao.web.SimpleUrlController" />
    

    SimpleUrlHandlerMapping的使用非常简单,如上在spring mvc的配置文件中指定好url与bean的对应关系即刻,其中该bean类需要实现Controller接口。

    5.5.2 SimpleUrlHandlerMapping原理

    如上定义的url与bean的对应关系,最终会被解析设置到SimpleUrlHandlerMapping类中的urlmap变量中。而SimpleUrlHandlerMapping类重写了父类AbstractHandlerMapping的initApplicationContext方法

    @Override
    public void initApplicationContext() throws BeansException {
        // 调用父类AbstractHandlerMapping的方法,主要功能是实现interceptor的初始化
        super.initApplicationContext();
        // 将本类中的urlmap注册到父类AbstractUrlHandlerMapping的handlerMap变量中
        registerHandlers(this.urlMap);
    }
    

    6、AbstractHandlerMethodMapping

    6.1 AbstractHandlerMethodMapping概述

    与AbstractUrlHandlerMapping一样,AbstractHandlerMethodMapping也是实现了父类的模板方法getHandlerInternal

    @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();
        }
    }
    

    代码很简单,主要的实现逻辑就是lookupHandlerMethod方法

    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 + "}");
                }
            }
            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
            handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
        }
        else {
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }
    

    其中T是来自AbstractHandlerMethodMapping类的定义,目前SpringMVC中只有RequestMappingInfo的实现。RequestMappingInfo类主要是对多种类型的请求匹配对象的封装,包括请求url、cookie、header等。

    在分析lookupHandlerMethod方法的整体思路之前,我们还得知晓AbstractHandlerMethodMapping的内部类MappingRegistry。MappingRegistry类中定义了两个比较重要的变量,Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>()MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>()。其中mappingLookup变量保存了RequestMappingInfo与HandlerMethod的一一对应关系,而urlLookup变量则保存了url与RequestMappingInfo的对应关系,需要注意的是MultiValueMap类型的变量是可以一个key对应多个value的,也就是说urlLookup变量中,一个url可能对应多个RequestMappingInfo。

    好,现在继续看lookupHandlerMethod方法,首选根据lookupPath(可以理解为url)获取到RequestMappingInfo,就是从urlLookup获取,可能返回多个对象,最后找到一个最符合的RequestMappingInfo对象,然后根据该对象再去获取到HandlerMethod对象返回。大体思路如此,具体细节不做更多分析。

    与分析AbstractUrlHandlerMapping类时一致,这里的mappingLookup和urlLookup中的数据又是何时初始化的呢?AbstractHandlerMethodMapping实现了InitializingBean接口,所以spring容器会自动调用其afterPropertiesSet方法,该方法又交给initHandlerMethods方法完成具体的初始化。

    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());
    }
    

    逻辑不是很复杂,首先从spring容器中获取所有的bean,然后进行过滤与处理。主要关注其中的两个方法,isHandler(beanType)detectHandlerMethods(beanName)。其中isHandler(beanType)方法由子类RequestMappingHandlerMapping实现,用于对bean进行过滤,判断是否包含Controller或者RequestMapping注解。

    @Override
    protected boolean isHandler(Class<?> beanType) {
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }
    

    detectHandlerMethods(beanName)方法,则根据筛选出的bean,进行进一步的处理,也就是对上述的mappingLookup和urlLookup两个变量的数据填充。

    protected void detectHandlerMethods(final Object handler) {
        Class<?> handlerType = (handler instanceof String ?
                getApplicationContext().getType((String) handler) : handler.getClass());
        // CGLib动态代理的特殊处理
        final Class<?> userType = ClassUtils.getUserClass(handlerType);
        // methods包含了bean中所有符合条件的method与相关的RequestMappingInfo键值对
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                new MethodIntrospector.MetadataLookup<T>() {
                    @Override
                    public T inspect(Method method) {
                        try {
                            // 如果method有@RequestMapping注解,则返回由注解得到的封装好的RequestMappingInfo对象,否则返回null
                            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();
        // 向mappingLookup和urlLookup两个变量中填充数据
            registerHandlerMethod(handler, invocableMethod, mapping);
        }
    }
    

    相关文章

      网友评论

          本文标题:HandlerMapping简介

          本文链接:https://www.haomeiwen.com/subject/fnqmuqtx.html