SpringMVC框架的Handler查找

对于springmvc框架的基本执行逻辑包含以下几步

  1. 查找能够处理http请求的Handler(通过HandlerMapping来查找)
  2. 查询能处理Handler的适配器HandlerAdapter
  3. 执行HandlerAdapter得到ModelAndView
  4. 根据ModelAndView得到对应的View
  5. 执行View的渲染方法进行客户端的响应

那么本章节主要讲解其查找Handler的过程。

在DispatcherServlet初始化的时候会通过以下代码来初始化HandlerMappings

public class DispatcherServlet extends FrameworkServlet {
    /**
     * Initialize the HandlerMappings used by this class.
     * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
     * we default to BeanNameUrlHandlerMapping.
     */
    private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;

        //判断是否查找所有的HandlerMappings,默认为true
        if (this.detectAllHandlerMappings) {
            // 从ApplicationContext(包括祖先祖先ApplicationContext)中查找所有HandlerMapping类型的Bean
            Map<String, HandlerMapping> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<>(matchingBeans.values());
                // 对查找到的HandlerMapping进行排序
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        }
        else {
            try {
                //直接从当前的ApplicationContext获取名为handlerMapping,类型为HandlerMapping的bean
                HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerMapping later.
            }
        }

        // 如果通过上面步骤没找到HandlerMapping则使用DispatcherServlet.properties中默认配置的HandlerMapping
        if (this.handlerMappings == null) {
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
            if (logger.isTraceEnabled()) {
                logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                        "': using default strategies from DispatcherServlet.properties");
            }
        }

        for (HandlerMapping mapping : this.handlerMappings) {
            if (mapping.usesPathPatterns()) {
                this.parseRequestPath = true;
                break;
            }
        }
    }
}

在spring-webmvc包中的DispatcherServlet.properties中默认配置了以下的HandlerMapping

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
    org.springframework.web.servlet.function.support.RouterFunctionMapping

spring-webmvc框架在处理请求时通过以下方法来查找Handler,当然,实际是返回的一个包含了Handler的HandlerExecutionChain

    /**
     * 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;
    }

从上可见,HandlerMapping的具体实现与Handler的查找息息相关,在讲解HandlerMapping的具体实现前,我们先聊聊会被具体实现继承的抽象类AbstractHandlerMapping

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

    @Nullable
    private PathPatternParser patternParser;

    private UrlPathHelper urlPathHelper = new UrlPathHelper();

    private PathMatcher pathMatcher = new AntPathMatcher();

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

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

    @Nullable
    private CorsConfigurationSource corsConfigurationSource;

    private CorsProcessor corsProcessor = new DefaultCorsProcessor();

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

    @Nullable
    private String beanName;

    /**
     * Initializes the interceptors.
     * @see #extendInterceptors(java.util.List)
     * @see #initInterceptors()
     */
    @Override
    protected void initApplicationContext() throws BeansException {
        extendInterceptors(this.interceptors);
        detectMappedInterceptors(this.adaptedInterceptors);
        initInterceptors();
    }

    /**
     * Look up a handler for the given request, falling back to the default
     * handler if no specific one is found.
     * @param request current HTTP request
     * @return the corresponding handler instance, or the default handler
     * @see #getHandlerInternal
     */
    @Override
    @Nullable
    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 = obtainApplicationContext().getBean(handlerName);
        }

        //生成Handler的调用链(封装了该Handler所需的拦截器)
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

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

        //hasCorsConfigurationSource:如果实际的handler是CorsConfigurationSource类型,
        //或者corsConfigurationSource不为空,这返回true
        if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
            //获取该Handler的跨域配置
            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 添加CorsInterceptor拦截器
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }

        return executionChain;
    }

    //供具体的子类来实现Handler的查找
    @Nullable
    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));

        //遍历所有的HandlerInterceptor,如果是实现类MappedInterceptor 则判断是否匹配当前的请求,
        //如果匹配则将持有的HandlerInterceptor实例添加到拦截器链中;如果不是MappedInterceptor ,
        //则直接加入到拦截器链中
        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;
    }

    protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
            HandlerExecutionChain chain, @Nullable CorsConfiguration config) {

        if (CorsUtils.isPreFlightRequest(request)) {
            HandlerInterceptor[] interceptors = chain.getInterceptors();
            return new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
        }
        else {
            chain.addInterceptor(0, new CorsInterceptor(config));
            return chain;
        }
    }
}

AbstractHandlerMapping中initApplicationContext方法的目的是为了初始化Handler的拦截器(HandlerInterceptor),其会得到ApplicationContext中所有MappedInterceptor类型的Bean(MappedInterceptor是HandlerInterceptor接口的实现类)。MappedInterceptor实例内部持有HandlerInterceptor类型的实例,通过MappedInterceptor的matches方法可以判断某请求是否需要使用该拦截器。

其中的getHandlerInternal方法供子类去实现,不同的子类其查找的策略会不一样,其得到的Handler的类型也不同。

1,BeanNameUrlHandlerMapping

BeanNameUrlHandlerMapping将URL映射到名称以斜杠(“/”)开头的bean。该种查找方式现在用的应该很少了,所以这里就简单的说说。

BeanNameUrlHandlerMapping

在BeanNameUrlHandlerMapping初始化的过程中首先会获取所有名称以斜杠(“/”)开头的bean,当请求到来时再与请求的路径进行匹配,当匹配成功则该bean就是对应的Handler。由于该种查找方式用的不多,这里就不再多缀诉了。

2,RequestMappingHandlerMapping

该对象是目前使用最多的,所以我们先来看看其初始化的过程。其初始化的过程从RequestMappingHandlerMapping的initApplicationContext方法开始

  1. 首先执行继承自AbstractHandlerMapping的initApplicationContext方法。
    该方法的目的是为了初始化Handler的拦截器(HandlerInterceptor),其会得到ApplicationContext中所有MappedInterceptor类型的Bean(MappedInterceptor是HandlerInterceptor接口的实现类)。MappedInterceptor实例内部持有HandlerInterceptor类型的实例,通过MappedInterceptor的matches方法可以判断某请求是否需要使用该拦截器。

2.执行RequestMappingHandlerMapping的afterPropertiesSet方法

  1. 执行父类AbstractHandlerMethodMapping的afterPropertiesSet方法

  2. 执行AbstractHandlerMethodMapping的initHandlerMethods方法
    initHandlerMethods的整体目的是扫描ApplicationContext中的bean,检测并注册handler方法
    -> 首先从ApplicationContext中所以Object类型Bean的名称,然后过滤掉“scopedTarget.”开头的
    -> 遍历所有的名称并从ApplicationContext中获取Bean的Class类型
    -> 判断得到的Class是否为Handler(判断是否有Controller和RequestMapping注解)
    -> 解析Class,得到包含RequestMapping注解的方法
    -> 将方法封装到HandlerMethod,并将其注册到MappingRegistry中

当请求到来时,将会从MappingRegistry中查找最匹配的Handler,此时的Handler本身是个Method。

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    /**
     * Look up a handler method for the given request.
     */
    @Override
    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();
        }
    }

    /**
     * Look up the best-matching handler method for the current request.
     * If multiple matches are found, the best match is selected.
     * @param lookupPath mapping lookup path within the current servlet mapping
     * @param request the current request
     * @return the best-matching handler method, or {@code null} if no match
     * @see #handleMatch(Object, String, HttpServletRequest)
     * @see #handleNoMatch(Set, String, HttpServletRequest)
     */
    @Nullable
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<>();
        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)) {
                    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();
                    String uri = request.getRequestURI();
                    throw new IllegalStateException(
                            "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
                }
            }
            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
            handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
        }
        else {
            return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
        }
    }
}
RequestMappingHandlerMapping
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容