行业知识
Spring RequestMappingHandlerMapping详解
文章目录
- 前言
- 一、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 处理器对象后,就可以通过反射调用相应的方法了。
上一篇:单位邮箱服务器怎么看?