0


Spring RequestMappingHandlerMapping详解

文章目录


前言

RequestMappingHandlerMapping

是Spring MVC中的一个请求映射处理器,它负责将HTTP请求映射到特定的

@RequestMapping

注解的方法上。允许你使用简单的注解(如

@GetMapping

@PostMapping

@RequestMapping

等)来定义请求路径和HTTP方法。
工作机制

  • 当Spring MVC的
    DispatcherServlet
    
    接收到一个HTTP请求时,它会查找一个合适的
    HandlerMapping
    
    来处理这个请求。
  • RequestMappingHandlerMapping
    
    会检查它的映射注册表,该注册表包含了所有使用
    @RequestMapping
    
    注解的方法的映射信息。
  • 如果找到了一个匹配的映射,那么
    RequestMappingHandlerMapping
    
    会返回一个
    HandlerExecutionChain
    
    ,它包含了要执行的处理器(通常是
    HandlerMethod
    
    ,代表一个
    @Controller
    
    中的方法)和任何与之关联的拦截器。

与其他组件的关系

*

RequestMappingHandlerMapping

HandlerAdapter

(如

RequestMappingHandlerAdapter

)紧密合作。一旦

RequestMappingHandlerMapping

找到了一个匹配的处理器,它会将这个处理器传递给

HandlerAdapter

,后者负责调用处理器并执行相应的逻辑。

*

RequestMappingHandlerMapping

还可能与

HandlerInterceptor

一起使用,以在请求处理过程中添加额外的逻辑(如日志记录、身份验证等)。

在这里插入图片描述
由类图可知,RequestMappingHandlerMapping的父类AbstractHandlerMethodMapping实现了InitializingBean接口,RequestMappingHandlerMapping和AbstractHandlerMethodMapping均实现了afterPropertiesSet() 方法,该方法会在bean属性完成初始化后被调用。

一、AbstractHandlerMethodMapping

AbstractHandlerMethodMapping,实现 InitializingBean 接口,继承 AbstractHandlerMapping 抽象类,以 Method 方法 作为 Handler 处理器 的 HandlerMapping 抽象类,提供 Mapping 的初始化、注册等通用的骨架方法。

那么具体是什么呢?AbstractHandlerMethodMapping 定义为了 泛型,交给子类做决定。例如,子类 RequestMappingInfoHandlerMapping 使用 RequestMappingInfo 类作为 泛型,也就是我们在上面注解模块看到的 @RequestMapping 等注解信息。

publicabstractclassAbstractHandlerMethodMapping<T>extendsAbstractHandlerMappingimplementsInitializingBean{/**
     * 是否只扫描可访问的 HandlerMethod 们
     */privateboolean detectHandlerMethodsInAncestorContexts =false;/**
     * Mapping 命名策略
     */@NullableprivateHandlerMethodMappingNamingStrategy<T> namingStrategy;/**
     * Mapping 注册表
     */privatefinalMappingRegistry mappingRegistry =newMappingRegistry();}

1.1 mappingRegistry

HandlerMethodMappingNamingStrategy 接口,HandlerMethod 的 Mapping 的名字生成策略接口。

@FunctionalInterfacepublicinterfaceHandlerMethodMappingNamingStrategy<T>{/**
     * 根据 HandlerMethod 获取名称,就是为对应的 Mappring 对象生成一个名称,便于获取
     */StringgetName(HandlerMethod handlerMethod,T mapping);}publicclassRequestMappingInfoHandlerMethodMappingNamingStrategyimplementsHandlerMethodMappingNamingStrategy<RequestMappingInfo>{/** Separator between the type and method-level parts of a HandlerMethod mapping name. */publicstaticfinalStringSEPARATOR="#";@OverridepublicStringgetName(HandlerMethod handlerMethod,RequestMappingInfo mapping){// 情况一,mapping 名字非空,则使用 mapping 的名字if(mapping.getName()!=null){return mapping.getName();}// 情况二,使用类名大写 + "#" + 方法名StringBuilder sb =newStringBuilder();String simpleTypeName = handlerMethod.getBeanType().getSimpleName();for(int i =0; i < simpleTypeName.length(); i++){if(Character.isUpperCase(simpleTypeName.charAt(i))){
                sb.append(simpleTypeName.charAt(i));}}
        sb.append(SEPARATOR).append(handlerMethod.getMethod().getName());return sb.toString();}}

情况一,如果 Mapping 已经配置名字,则直接返回。例如,@RequestMapping(name = “login”, value = “user/login”) 注解的方法,它对应的 Mapping 的名字就是 login
情况二,如果 Mapping 未配置名字,则使用使用类名大写 + “#” + 方法名。例如,@RequestMapping(value = “user/login”) 注解的方法,假设它所在的类为 UserController ,对应的方法名为 login ,则它对应的 Mapping 的名字就是 USERCONTROLLER#login

1.2 MappingRegistry 注册表

classMappingRegistry{/**
     * 注册表
     *
     * Key: Mapping
     * Value:{@link MappingRegistration}(Mapping + HandlerMethod)
     */privatefinalMap<T,MappingRegistration<T>> registry =newHashMap<>();/**
     * 注册表2
     *
     * Key:Mapping
     * Value:{@link HandlerMethod}
     */privatefinalMap<T,HandlerMethod> mappingLookup =newLinkedHashMap<>();/**
     * 直接 URL 的映射
     *
     * Key:直接 URL(就是固定死的路径,而非多个)
     * Value:Mapping 数组
     */privatefinalMultiValueMap<String,T> urlLookup =newLinkedMultiValueMap<>();/**
     * Mapping 的名字与 HandlerMethod 的映射
     *
     * Key:Mapping 的名字
     * Value:HandlerMethod 数组
     */privatefinalMap<String,List<HandlerMethod>> nameLookup =newConcurrentHashMap<>();privatefinalMap<HandlerMethod,CorsConfiguration> corsLookup =newConcurrentHashMap<>();/**
     * 读写锁
     */privatefinalReentrantReadWriteLock readWriteLock =newReentrantReadWriteLock();publicvoidregister(T mapping,Object handler,Method method){// <1> 获得写锁this.readWriteLock.writeLock().lock();try{// <2.1> 创建 HandlerMethod 对象HandlerMethod handlerMethod =createHandlerMethod(handler, method);// <2.2> 校验当前 mapping 是否存在对应的 HandlerMethod 对象,如果已存在但不是当前的 handlerMethod 对象则抛出异常assertUniqueMethodMapping(handlerMethod, mapping);// <2.3> 将 mapping 与 handlerMethod 的映射关系保存至 this.mappingLookupthis.mappingLookup.put(mapping, handlerMethod);// <3.1> 获得 mapping 对应的普通 URL 数组List<String> directUrls =getDirectUrls(mapping);// <3.2> 将 url 和 mapping 的映射关系保存至 this.urlLookupfor(String url : directUrls){this.urlLookup.add(url, mapping);}// <4> 初始化 nameLookupString name =null;if(getNamingStrategy()!=null){// <4.1> 获得 Mapping 的名字
                name =getNamingStrategy().getName(handlerMethod, mapping);// <4.2> 将 mapping 的名字与 HandlerMethod 的映射关系保存至 this.nameLookupaddMappingName(name, handlerMethod);}// <5> 初始化 CorsConfiguration 配置对象CorsConfiguration corsConfig =initCorsConfiguration(handler, method, mapping);if(corsConfig !=null){this.corsLookup.put(handlerMethod, corsConfig);}// <6> 创建 MappingRegistration 对象// 并与 mapping 映射添加到 registry 注册表中this.registry.put(mapping,newMappingRegistration<>(mapping, handlerMethod, directUrls, name));}finally{// <7> 释放写锁this.readWriteLock.writeLock().unlock();}}}

1.3 getHandlerInternal

通过 AbstractHandlerMapping 的 getHandler(HttpServletRequest request) 方法获取 HandlerExecutionChain 处理器执行链时,需要调用 getHandlerInternal 抽象方法获取处理器,这个方法由子类去实现,就到这里了。

@OverrideprotectedHandlerMethodgetHandlerInternal(HttpServletRequest request)throwsException{// <1> 获得请求的路径String lookupPath =getUrlPathHelper().getLookupPathForRequest(request);// <2> 获得读锁this.mappingRegistry.acquireReadLock();try{// <3> 获得 HandlerMethod 对象HandlerMethod handlerMethod =lookupHandlerMethod(lookupPath, request);// <4> 进一步,获得一个新的 HandlerMethod 对象return(handlerMethod !=null? handlerMethod.createWithResolvedBean():null);}finally{// <5> 释放读锁this.mappingRegistry.releaseReadLock();}}

1.4 lookupHandlerMethod

获得请求对应的 HandlerMethod 处理器对象

@NullableprotectedHandlerMethodlookupHandlerMethod(String lookupPath,HttpServletRequest request)throwsException{// <1> Match 数组,存储匹配上当前请求的结果(Mapping + HandlerMethod)List<Match> matches =newArrayList<>();// <1.1> 优先,基于直接 URL (就是固定死的路径,而非多个)的 Mapping 们,进行匹配List<T> directPathMatches =this.mappingRegistry.getMappingsByUrl(lookupPath);if(directPathMatches !=null){addMatchingMappings(directPathMatches, matches, request);}// <1.2> 其次,扫描注册表的 Mapping 们,进行匹配if(matches.isEmpty()){// No choice but to go through all mappings...addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);}// <2> 如果匹配到,则获取最佳匹配的 Match 结果的 `HandlerMethod`属性if(!matches.isEmpty()){// <2.1> 创建 MatchComparator 对象,排序 matches 结果,排序器Comparator<Match> comparator =newMatchComparator(getMappingComparator(request));
        matches.sort(comparator);// <2.2> 获得首个 Match 对象,也就是最匹配的Match bestMatch = matches.get(0);// <2.3> 处理存在多个 Match 对象的情况!!if(matches.size()>1){if(logger.isTraceEnabled()){
                logger.trace(matches.size()+" matching mappings: "+ matches);}if(CorsUtils.isPreFlightRequest(request)){returnPREFLIGHT_AMBIGUOUS_MATCH;}// 比较 bestMatch 和 secondBestMatch ,如果相等,说明有问题,抛出 IllegalStateException 异常// 因为,两个优先级一样高,说明无法判断谁更优先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();thrownewIllegalStateException("Ambiguous handler methods mapped for '"+ uri +"': {"+ m1 +", "+ m2 +"}");}}
        request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);// <2.4> 处理首个 Match 对象handleMatch(bestMatch.mapping, lookupPath, request);// <2.5> 返回首个 Match 对象的 handlerMethod 属性return bestMatch.handlerMethod;}// <3> 如果匹配不到,则处理不匹配的情况else{returnhandleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);}}

二、RequestMappingInfoHandlerMapping

当 Spring MVC 收到一个 HTTP 请求时,会通过 RequestMappingInfoHandlerMapping 来查找与请求匹配的 HandlerMethod,然后执行该方法来处理请求。
RequestMappingInfo
org.springframework.web.servlet.mvc.method.RequestMappingInfo,实现 RequestCondition 接口,每个方法的定义的请求信息,也就是 @RequestMapping 等注解的信息

publicfinalclassRequestMappingInfoimplementsRequestCondition<RequestMappingInfo>{@NullableprivatefinalString name;//请求路径条件(url合并,筛选url,择优url)privatefinalPatternsRequestCondition patternsCondition;//请求方法条件privatefinalRequestMethodsRequestCondition methodsCondition;//请求参数条件privatefinalParamsRequestCondition paramsCondition;//请求头条件privatefinalHeadersRequestCondition headersCondition;//请求content-type条件privatefinalConsumesRequestCondition consumesCondition;//请求accept条件privatefinalProducesRequestCondition producesCondition;//自定义请求条件privatefinalRequestConditionHolder customConditionHolder;}

getMatchingCondition
getMatchingCondition(HttpServletRequest request)方法,从当前 RequestMappingInfo 获得匹配的条件。如果匹配,则基于其匹配的条件,创建新的 RequestMappingInfo 对象,如果不匹配,则返回 null ,代码如下:

@Override@NullablepublicRequestMappingInfogetMatchingCondition(HttpServletRequest request){// 匹配 methodsCondition、paramsCondition、headersCondition、consumesCondition、producesCondition// 如果任一为空,则返回 null ,表示匹配失败RequestMethodsRequestCondition methods =this.methodsCondition.getMatchingCondition(request);if(methods ==null){returnnull;}ParamsRequestCondition params =this.paramsCondition.getMatchingCondition(request);if(params ==null){returnnull;}HeadersRequestCondition headers =this.headersCondition.getMatchingCondition(request);if(headers ==null){returnnull;}ConsumesRequestCondition consumes =this.consumesCondition.getMatchingCondition(request);if(consumes ==null){returnnull;}ProducesRequestCondition produces =this.producesCondition.getMatchingCondition(request);if(produces ==null){returnnull;}PatternsRequestCondition patterns =this.patternsCondition.getMatchingCondition(request);if(patterns ==null){returnnull;}RequestConditionHolder custom =this.customConditionHolder.getMatchingCondition(request);if(custom ==null){returnnull;}/*
     * 创建匹配的 RequestMappingInfo 对象
     * 一个 methodsCondition 可以配置 GET、POST、DELETE 等等条件,
     * 但是实际就匹配一个请求类型,此时 methods 只代表其匹配的那个。
     */returnnewRequestMappingInfo(this.name, patterns,
            methods, params, headers, consumes, produces, custom.getCondition());}

三、RequestMappingHandlerMapping

RequestMappingHandlerMapping实现 MatchableHandlerMapping、EmbeddedValueResolverAware 接口,继承 RequestMappingInfoHandlerMapping 抽象类,基于@RequestMapping 注解来构建 RequestMappingInfo 对象。
类的属性定义

publicclassRequestMappingHandlerMappingextendsRequestMappingInfoHandlerMappingimplementsMatchableHandlerMapping,EmbeddedValueResolverAware{privateboolean useSuffixPatternMatch =true;privateboolean useRegisteredSuffixPatternMatch =false;privateboolean useTrailingSlashMatch =true;privateMap<String,Predicate<Class<?>>> pathPrefixes =newLinkedHashMap<>();privateContentNegotiationManager contentNegotiationManager =newContentNegotiationManager();@NullableprivateStringValueResolver embeddedValueResolver;//RequestMappingInfo 的构建器privateRequestMappingInfo.BuilderConfiguration config =newRequestMappingInfo.BuilderConfiguration();

afterPropertiesSet
因父类 AbstractHandlerMethodMapping 实现了 InitializingBean 接口,在 Sping 初始化该 Bean 的时候,会调用该方法,完成一些初始化工作,方法如下:

@OverridepublicvoidafterPropertiesSet(){// 构建 RequestMappingInfo.BuilderConfiguration 对象this.config =newRequestMappingInfo.BuilderConfiguration();this.config.setUrlPathHelper(getUrlPathHelper());this.config.setPathMatcher(getPathMatcher());this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);this.config.setContentNegotiationManager(getContentNegotiationManager());// 调用父类,初始化super.afterPropertiesSet();}

isHandler
是否还记得 AbstractHandlerMethodMapping 的这个抽象方法?在它的 processCandidateBean 方法中,扫描 Spring 中所有 Bean 时会调用,判断是否需要扫描这个 Bean 中的方法,有 @Controller 或者 @RequestMapping 的注解的类才需要进行扫描,方法如下:

@OverrideprotectedbooleanisHandler(Class<?> beanType){// 判断是否有 @Controller 或者 @RequestMapping 的注解return(AnnotatedElementUtils.hasAnnotation(beanType,Controller.class)||AnnotatedElementUtils.hasAnnotation(beanType,RequestMapping.class));}

getMappingForMethod
AbstractHandlerMethodMapping 的 detectHandlerMethods 方法中调用该方法,用于获取 Method 方法对应的 Mapping 对象,方法如下:

@OverrideprotectedRequestMappingInfogetMappingForMethod(Method method,Class<?> handlerType){//1、基于方法上的 @RequestMapping 注解,创建 RequestMappingInfo 对象RequestMappingInfo info =createRequestMappingInfo(method);if(info !=null){// 2、 基于类上的 @RequestMapping 注解,合并进去RequestMappingInfo typeInfo =createRequestMappingInfo(handlerType);if(typeInfo !=null){
            info = typeInfo.combine(info);}// 3、 如果有前缀,则设置到 info 中String prefix =getPathPrefix(handlerType);if(prefix !=null){
            info =RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);}}return info;}

createRequestMappingInfo
基于方法上的 @RequestMapping 注解,创建 RequestMappingInfo 对象

@NullableprivateRequestMappingInfocreateRequestMappingInfo(AnnotatedElement element){// <1> 获得 @RequestMapping 注解RequestMapping requestMapping =AnnotatedElementUtils.findMergedAnnotation(element,RequestMapping.class);// <2> 获得自定义的条件。目前都是空方法,可以无视RequestCondition<?> condition =(element instanceofClass?getCustomTypeCondition((Class<?>) element):getCustomMethodCondition((Method) element));// <3> 基于 @RequestMapping 注解,创建 RequestMappingInfo 对象return(requestMapping !=null?createRequestMappingInfo(requestMapping, condition):null);}protectedRequestMappingInfocreateRequestMappingInfo(RequestMapping requestMapping,@NullableRequestCondition<?> customCondition){// 创建 RequestMappingInfo.Builder 对象,设置对应属性RequestMappingInfo.Builder builder =RequestMappingInfo.paths(resolveEmbeddedValuesInPatterns(requestMapping.path())).methods(requestMapping.method()).params(requestMapping.params()).headers(requestMapping.headers()).consumes(requestMapping.consumes()).produces(requestMapping.produces()).mappingName(requestMapping.name());if(customCondition !=null){
        builder.customCondition(customCondition);}// 创建 RequestMappingInfo 对象return builder.options(this.config).build();}

总结

将RequestMappingHandlerMapping注入到 Spring 上下文时,会进行一些初始化工作,扫描 @Controller 或者 @RequestMapping 注解标注的 Bean 对象,会将带有 @RequestMapping 注解(包括其子注解)解析成 RequestMappingInfo 对象。接下来,会将 RequestMappingInfo、该方法对象、该方法所在类对象 往 MappingRegistry 注册表进行注册,其中会生成 HandlerMethod 处理器(方法的所有信息)对象保存起来。当处理某个请求时,HandlerMapping 找到该请求对应的 HandlerMethod 处理器对象后,就可以通过反射调用相应的方法了。

标签: spring java 后端

本文转载自: https://blog.csdn.net/gwc791224/article/details/138916346
版权归原作者 shandongwill 所有, 如有侵权,请联系我们删除。

“Spring RequestMappingHandlerMapping详解”的评论:

还没有评论