小工具知识网

当前位置: 首页 > 行业知识 > Spring RequestMappingHandlerMapping详解

行业知识

Spring RequestMappingHandlerMapping详解

2026-11-01 23:46:48 来源:互联网转载

文章目录

  • 前言
  • 一、AbstractHandlerMethodMapping
    • 1.1 mappingRegistry
    • 1.2 MappingRegistry 注册表
    • 1.3 getHandlerInternal
    • 1.4 lookupHandlerMethod
    • 二、RequestMappingInfoHandlerMapping
    • 三、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 等注解信息。

      public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMapping implements InitializingBean {/** * 是否只扫描可访问的 HandlerMethod 们 */private boolean detectHandlerMethodsInAncestorContexts = false;/** * Mapping 命名策略 */@Nullableprivate HandlerMethodMappingNamingStrategy namingStrategy;/**     * Mapping 注册表     */private final MappingRegistry mappingRegistry = new MappingRegistry();}

      1.1 mappingRegistry

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

      @FunctionalInterfacepublic interface HandlerMethodMappingNamingStrategy {/** * 根据 HandlerMethod 获取名称,就是为对应的 Mappring 对象生成一个名称,便于获取 */String getName(HandlerMethod handlerMethod, T mapping);}public class RequestMappingInfoHandlerMethodMappingNamingStrategy implements HandlerMethodMappingNamingStrategy {/** Separator between the type and method-level parts of a HandlerMethod mapping name. */public static final String SEPARATOR = "#";@Overridepublic String getName(HandlerMethod handlerMethod, RequestMappingInfo mapping) {// 情况一,mapping 名字非空,则使用 mapping 的名字if (mapping.getName() != null) {return mapping.getName();}// 情况二,使用类名大写 + "#" + 方法名StringBuilder sb = new StringBuilder();String simpleTypeName = handlerMethod.getBeanType().getSimpleName();for (int i = 0; i  

      情况一,如果 Mapping 已经配置名字,则直接返回。例如,@RequestMapping(name = “login”, value = “user/login”) 注解的方法,它对应的 Mapping 的名字就是 login

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

      1.2 MappingRegistry 注册表

      class MappingRegistry {    /**     * 注册表     *     * Key: Mapping     * Value:{@link MappingRegistration}(Mapping + HandlerMethod)     */    private final Map registry = new HashMap();    /**     * 注册表2     *     * Key:Mapping     * Value:{@link HandlerMethod}     */    private final Map mappingLookup = new LinkedHashMap();    /**     * 直接 URL 的映射     *     * Key:直接 URL(就是固定***的路径,而非多个)     * Value:Mapping 数组     */    private final MultiValueMap urlLookup = new LinkedMultiValueMap();    /**     * Mapping 的名字与 HandlerMethod 的映射     *     * Key:Mapping 的名字     * Value:HandlerMethod 数组     */    private final Map nameLookup = new ConcurrentHashMap();    private final Map corsLookup = new ConcurrentHashMap();    /**     * 读写锁     */    private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();        public void register(T mapping, Object handler, Method method) {    //  获得写锁    this.readWriteLock.writeLock().lock();    try {        //  创建 HandlerMethod 对象        HandlerMethod handlerMethod = createHandlerMethod(handler, method);        //  校验当前 mapping 是否存在对应的 HandlerMethod 对象,如果已存在但不是当前的 handlerMethod 对象则抛出异常        assertUniqueMethodMapping(handlerMethod, mapping);        //  将 mapping 与 handlerMethod 的映射关系保存至 this.mappingLookup        this.mappingLookup.put(mapping, handlerMethod);        //  获得 mapping 对应的普通 URL 数组        List directUrls = getDirectUrls(mapping);        //  将 url 和 mapping 的映射关系保存至 this.urlLookup        for (String url : directUrls) {            this.urlLookup.add(url, mapping);        }        //  初始化 nameLookup        String name = null;        if (getNamingStrategy() != null) {            //  获得 Mapping 的名字            name = getNamingStrategy().getName(handlerMethod, mapping);            //  将 mapping 的名字与 HandlerMethod 的映射关系保存至 this.nameLookup            addMappingName(name, handlerMethod);        }        //  初始化 CorsConfiguration 配置对象        CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);        if (corsConfig != null) {            this.corsLookup.put(handlerMethod, corsConfig);        }        //  创建 MappingRegistration 对象        // 并与 mapping 映射添加到 registry 注册表中        this.registry.put(mapping, new MappingRegistration(mapping, handlerMethod, directUrls, name));    }    finally {        //  释放写锁        this.readWriteLock.writeLock().unlock();    }}}

      1.3 getHandlerInternal

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

      @Overrideprotected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {    //  获得请求的路径    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);    //  获得读锁    this.mappingRegistry.acquireReadLock();    try {        //  获得 HandlerMethod 对象        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);        //  进一步,获得一个新的 HandlerMethod 对象        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);    }    finally {        //  释放读锁        this.mappingRegistry.releaseReadLock();    }}

      1.4 lookupHandlerMethod

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

      @Nullableprotected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {    //  Match 数组,存储匹配上当前请求的结果(Mapping + HandlerMethod)    List matches = new ArrayList();    //  优先,基于直接 URL (就是固定***的路径,而非多个)的 Mapping 们,进行匹配    List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);    if (directPathMatches != null) {        addMatchingMappings(directPathMatches, matches, request);    }    //  其次,扫描注册表的 Mapping 们,进行匹配    if (matches.isEmpty()) {        // No choice but to go through all mappings...        addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);    }    //  如果匹配到,则获取最佳匹配的 Match 结果的 `HandlerMethod`属性    if (!matches.isEmpty()) {        //  创建 MatchComparator 对象,排序 matches 结果,排序器        Comparator comparator = new MatchComparator(getMappingComparator(request));        matches.sort(comparator);        //  获得首个 Match 对象,也就是最匹配的        Match bestMatch = matches.get(0);        //  处理存在多个 Match 对象的情况!!        if (matches.size() > 1) {            if (logger.isTraceEnabled()) {                logger.trace(matches.size() + " matching mappings: " + matches);            }            if (CorsUtils.isPreFlightRequest(request)) {                return PREFLIGHT_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();                throw new IllegalStateException(                        "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");            }        }        request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);        //  处理首个 Match 对象        handleMatch(bestMatch.mapping, lookupPath, request);        //  返回首个 Match 对象的 handlerMethod 属性        return bestMatch.handlerMethod;    }    //  如果匹配不到,则处理不匹配的情况    else {        return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);    }}

      二、RequestMappingInfoHandlerMapping

      当 Spring MVC 收到一个 HTTP 请求时,会通过 RequestMappingInfoHandlerMapping 来查找与请求匹配的 HandlerMethod,然后执行该方法来处理请求。

      RequestMappingInfo

      org.springframework.web.servlet.mvc.method.RequestMappingInfo,实现 RequestCondition 接口,每个方法的定义的请求信息,也就是 @RequestMapping 等注解的信息

      public final class RequestMappingInfo implements RequestCondition {    @Nullable    private final String name;    //请求路径条件(url合并,筛选url,择优url)    private final PatternsRequestCondition patternsCondition;    //请求方法条件    private final RequestMethodsRequestCondition methodsCondition;    //请求参数条件    private final ParamsRequestCondition paramsCondition;    //请求头条件    private final HeadersRequestCondition headersCondition;    //请求content-type条件    private final ConsumesRequestCondition consumesCondition;    //请求accept条件    private final ProducesRequestCondition producesCondition;    //自定义请求条件    private final RequestConditionHolder customConditionHolder;}

      getMatchingCondition

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

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

      三、RequestMappingHandlerMapping

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

      类的属性定义

      public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappingimplements MatchableHandlerMapping, EmbeddedValueResolverAware {private boolean useSuffixPatternMatch = true;private boolean useRegisteredSuffixPatternMatch = false;private boolean useTrailingSlashMatch = true;private Map condition = (element instanceof Class ?                                      getCustomTypeCondition((Class) element) : getCustomMethodCondition((Method) element));    //  基于 @RequestMapping 注解,创建 RequestMappingInfo 对象    return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);}protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping, @Nullable RequestCondition 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 处理器对象后,就可以通过反射调用相应的方法了。

上一篇:单位邮箱服务器怎么看?

下一篇:揭秘参数值的奥秘,一篇文章深度解析其核心要点!