5. DispatcherServlet:Spring MVC的核心 -- getHandler()解析

上文中提到在DispatcherServlet.doDispatch()方法中,通过getHandler()方法获取HandlerExecutionChain对象(处理器执行链)

那么接下来就讲一讲这个方法:

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Iterator var2 = this.handlerMappings.iterator();

        HandlerExecutionChain handler;
        do {
            if (!var2.hasNext()) {
                return null;
            }

            HandlerMapping hm = (HandlerMapping)var2.next();
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'");
            }

            handler = hm.getHandler(request);
        } while(handler == null);

        return handler;
    }

getHandler()涉及一个设计模式——拦截过滤器模式,它会从List<HandlerMapping> handlerMappings中遍历查找对应的HandlerMapping对象,并由HandlerMapping对象创建HandlerExecutionChain对象。List<HandlerMapping> handlerMappings是初始化时在initStrategies()方法中完成的(在上一篇DispatcherServlet的初始化过程 提到过)

HandlerMapping的getHandler()方法是一个接口

实现类为AbstractHandlerMapping.class

    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        // 根据请求查找对应的handler
        Object handler = this.getHandlerInternal(request);
        if (handler == null) {
            // 没有找到,则会使用getDefaultHandler()方法获取默认的处理器
            handler = this.getDefaultHandler();
        }

        if (handler == null) {
            return null;
        } else {
            if (handler instanceof String) {
                String handlerName = (String)handler;
                handler = this.getApplicationContext().getBean(handlerName);
            }

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

            return executionChain;
        }
    }

this.getHandlerInternal(request);是一个抽象方法,实现类为:AbstractUrlHandlerMapping.class

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
        // 获取请求路径
        String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);
        // 查找handler
        Object handler = this.lookupHandler(lookupPath, request);
        if (handler == null) {
            Object rawHandler = null;
            if ("/".equals(lookupPath)) {
                rawHandler = this.getRootHandler();
            }

            if (rawHandler == null) {
                rawHandler = this.getDefaultHandler();
            }

            if (rawHandler != null) {
                if (rawHandler instanceof String) {
                    String handlerName = (String)rawHandler;
                    rawHandler = this.getApplicationContext().getBean(handlerName);
                }

                this.validateHandler(rawHandler, request);
                handler = this.buildPathExposingHandler(rawHandler, lookupPath, lookupPath, (Map)null);
            }
        }

        if (handler != null && this.logger.isDebugEnabled()) {
            this.logger.debug("Mapping [" + lookupPath + "] to " + handler);
        } else if (handler == null && this.logger.isTraceEnabled()) {
            this.logger.trace("No handler mapping found for [" + lookupPath + "]");
        }

        return handler;
    }

进入lookupHandler()可以发现,是通过请求路径字符串在handlerMap中查找到handler

       private final Map<String, Object> handlerMap = new LinkedHashMap();


       Object handler = this.handlerMap.get(urlPath);

 但是当使用RESTful风格的请求地址时,比如请求这个方法时的url为 http://localhost:8080/spring-learn/hello/hls

在handlerMap中将会找不到以/hello/hls为键名的handler:

 此时将进行其他方式的匹配:

    protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
        Object handler = this.handlerMap.get(urlPath);
        if (handler != null) {
            if (handler instanceof String) {
                String handlerName = (String)handler;
                handler = this.getApplicationContext().getBean(handlerName);
            }

            this.validateHandler(handler, request);
            return this.buildPathExposingHandler(handler, urlPath, urlPath, (Map)null);
        } else {
            List<String> matchingPatterns = new ArrayList();
            Iterator var5 = this.handlerMap.keySet().iterator();

            while(var5.hasNext()) {
                String registeredPattern = (String)var5.next();
                if (this.getPathMatcher().match(registeredPattern, urlPath)) {
                    matchingPatterns.add(registeredPattern);
                } else if (this.useTrailingSlashMatch() && !registeredPattern.endsWith("/") && this.getPathMatcher().match(registeredPattern + "/", urlPath)) {
                    matchingPatterns.add(registeredPattern + "/");
                }
            }

            String bestPatternMatch = null;
            Comparator<String> patternComparator = this.getPathMatcher().getPatternComparator(urlPath);
            if (!matchingPatterns.isEmpty()) {
                Collections.sort(matchingPatterns, patternComparator);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
                }

                bestPatternMatch = (String)matchingPatterns.get(0);
            }

            if (bestPatternMatch != null) {
                handler = this.handlerMap.get(bestPatternMatch);
                if (handler == null) {
                    Assert.isTrue(bestPatternMatch.endsWith("/"));
                    handler = this.handlerMap.get(bestPatternMatch.substring(0, bestPatternMatch.length() - 1));
                }

                String pathWithinMapping;
                if (handler instanceof String) {
                    pathWithinMapping = (String)handler;
                    handler = this.getApplicationContext().getBean(pathWithinMapping);
                }

                this.validateHandler(handler, request);
                pathWithinMapping = this.getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);
                Map<String, String> uriTemplateVariables = new LinkedHashMap();
                Iterator var9 = matchingPatterns.iterator();

                while(var9.hasNext()) {
                    String matchingPattern = (String)var9.next();
                    if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
                        Map<String, String> vars = this.getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
                        Map<String, String> decodedVars = this.getUrlPathHelper().decodePathVariables(request, vars);
                        uriTemplateVariables.putAll(decodedVars);
                    }
                }

                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
                }

                return this.buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
            } else {
                return null;
            }
        }
    }

回到AbstractHandlerMapping.class的getHandler()方法中, getHandlerExecutionChain()方法匹配拦截器然后把上边找到handler整合为一个 HandlerExecutionChain对象

    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        HandlerExecutionChain chain = handler instanceof HandlerExecutionChain ? (HandlerExecutionChain)handler : new HandlerExecutionChain(handler);
        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
        Iterator var5 = this.adaptedInterceptors.iterator();

        while(var5.hasNext()) {
            HandlerInterceptor interceptor = (HandlerInterceptor)var5.next();
            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()方法实现可以看到,首先会判断根据请求获取的处理器是不是HandlerExecutionChain对象。如果是,则直接使用;如果不是,则通过请求处理器Handler创建一个HandlerExecutionChain对象,然后将多个拦截器对象保存到HandlerExecutionChain对象中的List<HandlerInterceptor> interceptorList属性中。因此HandlerExecutionChain封装了请求的处理程序(即Controller中的处理方法)和相关的拦截器

返回到DispatcherServlet类的doDispatch()方法,执行完getHandler()方法以后,得到的HandlerExecutionChain对象如果为空,则会执行noHandlerFound()方法,执行完后直接返回,流程结束。noHandlerFound()方法的代码如下:

    protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (pageNotFoundLogger.isWarnEnabled()) {
            pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + getRequestUri(request) + "] in DispatcherServlet with name '" + this.getServletName() + "'");
        }

        if (this.throwExceptionIfNoHandlerFound) {
            throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request), (new ServletServerHttpRequest(request)).getHeaders());
        } else {
            // 返回404
            response.sendError(404);
        }
    }

如果不为空则继续执行下面的代码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
<select id="getToBePaidByDataStatus" parameterType="String" resultMap="DigitalBaohanResult"> select count(*) from DIGITAL_BAOHAN where DATA_STATUS = #{dataStatus} </select> 报错 java.lang.IllegalStateException: Ambiguous handler methods mapped for '/digitalBaohan/getToBePaidByDataStatus': {public com.ruoyi.common.core.web.domain.AjaxResult com.baohan.controller.DigitalBaohanController.getToBePaidByDataStatus(java.lang.Long), public com.ruoyi.common.core.web.domain.AjaxResult com.baohan.controller.DigitalBaohanController.getInfo(java.lang.Long)} at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:432) at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:383) at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.getHandlerInternal(RequestMappingInfoHandlerMapping.java:125) at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.getHandlerInternal(RequestMappingInfoHandlerMapping.java:67) at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:498) at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1264) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1046) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
06-09
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值