SpringMVC源码(六)- doDispatch - getHandler获取HandlerExecutionChain(调用方法和拦截器链)

目录

getHandler

1、匹配HandlerMethod

1)、获取真实的请求路径

2)、匹配需要调用的具体方法封装的HandlerMethod

createWithResolvedBean(没看懂)

2、匹配连接器链,组装返回HandlerExecutionChain对象


    为了方便理解,还是继续SpringMVC源码(四)- 常用HandlerMapping和HandlerAdaptor组合和使用方式#3、RequestMappingHandlerMapping和RequestMappingHandlerAdapter中的demo,当前的请求URL为:

http://127.0.0.1:9999/annotationController


getHandler

    在上一篇准备好HandlerMapping列表后,继续DispatcherServlet的doDispatch方法 -> getHandler方法,根据当前HttpServletRequest(其实当前为其子类型RequestFacade)适配具体要调用的Controller方法和需要执行的拦截器链,如下:

@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列表,如果匹配到就进行返回。当前只分析常用的@RequestMapping注解方式,则会调用RequestMappingHandlerMappinggetHandler方法:

@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 匹配需要调用的方法对象(HandlerMethod)
    Object handler = getHandlerInternal(request);
    // 如果没有获取到,赋值默认的操作器
    if (handler == null) {
        handler = getDefaultHandler();
    }
    if (handler == null) {
        return null;
    }
    // 如果被执行的调用类型为String,则认为是BeanFactory中Bean的名称,获取返回
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = obtainApplicationContext().getBean(handlerName);
    }
    // 获取当前请求的拦截器(HandlerInterceptor)执行链
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    // 对cors跨域方式的处理
    if (hasCorsConfigurationSource(handler)) {
        CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
        CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
        config = (config != null ? config.combine(handlerConfig) : handlerConfig);
        executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }
    return executionChain;
}

    1、匹配需要调用的方法对象(HandlerMethod)

    2、如果没有获取到,赋值默认的操作器(默认为null)

    3、如果被执行的调用类型为String,则认为是BeanFactory中Bean的名称,获取返回

    4、获取当前请求的拦截器(HandlerInterceptor)执行链,并组装HandlerExecutionChain(HandlerMethod和HandlerInterceptor调用链)

    5、对cors跨域方式的处理

 

1、匹配HandlerMethod

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    try {
        return super.getHandlerInternal(request);
    } finally {
        ProducesRequestCondition.clearMediaTypesAttribute(request);
    }
}

    主要逻辑还是在父类AbstractHandlerMethodMapping中完成的,只是finally中需要清除Request的MediaType。

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    request.setAttribute(LOOKUP_PATH, lookupPath);
    this.mappingRegistry.acquireReadLock();
    try {
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    } finally {
        this.mappingRegistry.releaseReadLock();
    }
}

在mappingRegistry的读锁条件下,主要做了两个事:

    1、根据HttpServletRequest获取需要调用的情况的路径(比如当前为/annotationController),并设置为请求的属性

    2、根据路径,获取对应的HandlerMethod(可以参看上一篇博客理解该类型)

 

1)、获取真实的请求路径

    具体的获取请求路径委派给UrlPathHelper进行处理,如下:

public String getLookupPathForRequest(HttpServletRequest request) {
    // Always use full path within current servlet context?
    if (this.alwaysUseFullPath) {
        return getPathWithinApplication(request);
    }
    // Else, use path within current servlet mapping if applicable
    String rest = getPathWithinServletMapping(request);
    if (!"".equals(rest)) {
        return rest;
    } else {
        return getPathWithinApplication(request);
    }
}

    由于父类中需要处理其他类型的匹配,所以会有Servlet等。@RequestMapping类型则会调用getPathWithinApplication方法:

public String getPathWithinApplication(HttpServletRequest request) {
    String contextPath = getContextPath(request);
    String requestUri = getRequestUri(request);
    String path = getRemainingPath(requestUri, contextPath, true);
    if (path != null) {
        // Normal case: URI contains context path.
        return (StringUtils.hasText(path) ? path : "/");
    } else {
        return requestUri;
    }
}

     先获取请求(HttpServletRequest)中的上下文的名称(项目名称,也可以是Spring Boot中的server.context-path属性),比如当前没有设置则为空字符串。请求时,还可能根据项目的编码,即Tomcat的uriEncoding进行处理。

    再获取请求路径,需要考虑各种请求,具体不细究了。

    再获取真实的可能需要匹配的路径。

比如当前的项目路径为空字符串,请求路径/annotationController,那么在请求路径上去除项目路径就是真实的匹配路径

 

2)、匹配需要调用的具体方法封装的HandlerMethod

@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<>();
    // 从注册的urlLookup中获取(key为@RequestMapping类和方法字符串相加 value为RequestMappingInfo)
    List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
    // 之前就注册了,一般都不为null
    if (directPathMatches != null) {
        // 将获取到的RequestMappingInfo封装为Match,添加到matches中
        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));
        // 排序
        matches.sort(comparator);
        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();
                String uri = request.getRequestURI();
                throw new IllegalStateException("省略");
            }
        }
        // 将HandlerMethod添加到request属性中
        request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
        // 将lookupPath添加到request属性中
        handleMatch(bestMatch.mapping, lookupPath, request);
        // 返回匹配的HandlerMethod
        return bestMatch.handlerMethod;
    } else {
        return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
    }
}

    主要是从之前注册的urlLookup中获取(key为@RequestMapping类和方法字符串相加 value为RequestMappingInfo)获取到RequestMappingInfo对象。

    再从mappingLookup(RequestMappingInfo与HandlerMethod的关系)中获取HandlerMapping

    还将request中添加了两个属性,返回获取到的HandlerMethod对象。

 

createWithResolvedBean

public HandlerMethod createWithResolvedBean() {
    Object handler = this.bean;
    if (this.bean instanceof String) {
        String beanName = (String) this.bean;
        handler = this.beanFactory.getBean(beanName);
    }
    return new HandlerMethod(this, handler);
}

    根据bean名称,从工厂获取对应的Bean,再new一个对象。没看懂是为什么。

 

2、匹配连接器链,组装返回HandlerExecutionChain对象

protected HandlerExecutionChain getHandlerExecutionChain(Object handler,
    HttpServletRequest request) {
    // 创建HandlerExecutionChain对象
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
            (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

    // 
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
    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;
}

    正常状况下当前的handler为HandlerMethod类型,那么会new HandlerExecutionChain(handler)。后面就是对RequestMappingHandlerMapping的回调方法setApplicationContext初始化的adaptedInterceptors属性进行遍历,如果没有MappedInterceptor类型则直接进行加入。

    MappedInterceptor为final类型,不能继承,而我们自定义的拦截器会直接实现MappedInterceptor。正常调用情况下是没有MappedInterceptor类型的,不知道在上面情况下使用。又再一次获取lookupPath(比如当前为/annotationController)。个人优化理解:1、如果考虑优化则可以在上面传入该值而不是再获取一次,毕竟每次请求都会调用(只是这样每一层更清晰);2、正常情况下没有MappedInterceptor类型的拦截器,为什么每次要调用获取lookupPath,有点浪费性能。

 

 

 

©️2020 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值