上一篇写到文件上传请求的原理。接下来就是获取Handler的原理了
mappedHandler = getHandler(processedRequest)
:返回HandlerExecutionChain
对象(包含目标方法、拦截器链)
getHandler()
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
调用其getHandler()
来拿到HandlerExecutionChain
对象
5个HandlerMapping
HandlerMapping
的作用就是根据请求找到对应的Handler
我们都是在controller接口中然后声明方法来做请求处理的,所以会由这个类处理 RequestMappingHandlerMapping(AbstractHandlerMapping 是它的父类):
private Object defaultHandler; public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 根据当前请求拿到对应的 Handler Object handler = getHandlerInternal(request); // 没拿到就给一个默认的 Handler if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } // 根据拿到的 Handler 和 当前请求得到一个 HandlerExecutionChain HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (logger.isTraceEnabled()) { logger.trace("Mapped to " + handler); } else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) { logger.debug("Mapped to " + executionChain.getHandler()); } // 处理跨域 if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { 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; }
获取处理器对象-getHandlerInternal()
拿到目标方法
最终会来到AbstractHandlerMethodMapping#getHandlerInternal
// 路径帮助器 private UrlPathHelper urlPathHelper = new UrlPathHelper(); String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath"; protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 1、使用路径帮助器拿到请求中的请求路径 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); request.setAttribute(LOOKUP_PATH, lookupPath); this.mappingRegistry.acquireReadLock(); try { // 2、通过请求路径拿到 HandlerMethod (核心) HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
根据请求路径找到对应处理器-lookupHandlerMethod()
AbstractHandlerMethodMapping#lookupHandlerMethod
// RequestMappingInfo 集合 k:路径 v:RequestMappingInfo private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>(); // HandlerMethod 集合 k:RequestMappingInfo v:HandlerMethod private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>(); protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { // 根据路径匹配到的集合 List<Match> matches = new ArrayList<>(); // 1,根据路径找到对应的 RequestMappingInfo 集合 List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { // 2,匹配到的集合 这里面有 HandlerMethod 对象 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()) { // 3,从匹配到的集合中拿到第一个 Match bestMatch = matches.get(0); if (matches.size() > 1) { Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); matches.sort(comparator); bestMatch = matches.get(0); if (logger.isTraceEnabled()) { logger.trace(matches.size() + " matching mappings: " + matches); } 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( "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}"); } } request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod); handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.handlerMethod; } else { return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } }
流程分析:
getMappingsByUrl()
:去MappingRegistry
(注册中心)中根据当前路径找到对应的RequestMappingInfo
对象addMatchingMappings()
:再根据找到的RequestMappingInfo
去MappingRegistry
(注册中心)找HandlerMethod
然后封装为Match
对象,添加到matches
集合中- 如果匹配到了多个,会根据规则拿到最佳匹配的
1、getMappingsByUrl()
去MappingRegistry
(注册中心)中根据当前路径找到对应的RequestMappingInfo
对象
// RequestMappingInfo 集合 k:路径 v:RequestMappingInfo private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>(); public List<T> getMappingsByUrl(String urlPath) { return this.urlLookup.get(urlPath); }
注意: PathVariable
类型是不会加到这个集合里的,具体逻辑在这里,有兴趣可以去看这两个方法: org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register()、org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getDirectUrls()
注意:这里key是路径,value是RequestMappingInfo
对象
2、addMatchingMappings()
再根据找到的RequestMappingInfo
去MappingRegistry
(注册中心)找HandlerMethod
然后封装为Match
对象,添加到matches
集合中
// HandlerMethod 集合 k:RequestMappingInfo v:HandlerMethod private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>(); private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) { for (T mapping : mappings) { // 封装为 RequestMappingInfo T match = getMatchingMapping(mapping, request); if (match != null) { // 找到的集合,又会封装为 Match 对象 matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping))); } } } // 封装 RequestMappingInfo return new RequestMappingInfo(this.name, pathPatterns, patterns, methods, params, headers, consumes, produces, custom, this.options);
注意:这里的key是RequestMappingInfo
对象,value是HandlerMethod
Match
对象:
3、得到最佳匹配
最后如果找到了多个,会根据规则拿到最佳匹配的HandlerMethod
对象返回给我们
HandlerMethod对象:
目标方法: HandlerMethod
对象
获取处理器执行链对象-getHandlerExecutionChain()
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { // 1、封装 HandlerExecutionChain 对象 HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); // 2、利用路径帮助器拿到当前路径 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH); // 3、遍历所有的拦截器 for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; // 3、判断当前拦截器是否拦截此请求 if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { // 添加到目标对象中 chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }
流程:
- 先把
HandlerMethod
封装到HandlerExecutionChain
中 - 利用路径帮助器拿到当前请求路径
- 遍历所有的拦截器并判断当前拦截器是否拦截当前请求,拦截的话就添加到我们的
HandlerExecutionChain
对象中,然后返回
这两个拦截器是默认的拦截器,不用管
里面就3个东西:处理器、拦截器集合、当前执行到第几个拦截器的索引
到这一步也就拿到了HandlerExecutionChain
对象,最后还会有跨域的处理,就把这个对象返回出去了。
跨域放在下一篇说。
public boolean matches(String lookupPath, PathMatcher pathMatcher) { PathMatcher pathMatcherToUse = (this.pathMatcher != null ? this.pathMatcher : pathMatcher); // 当前路径是否在排除路径的列表中 if (!ObjectUtils.isEmpty(this.excludePatterns)) { for (String pattern : this.excludePatterns) { if (pathMatcherToUse.match(pattern, lookupPath)) { return false; } } } if (ObjectUtils.isEmpty(this.includePatterns)) { return true; } // 当前路径是否在要拦截器的路径列表中 for (String pattern : this.includePatterns) { if (pathMatcherToUse.match(pattern, lookupPath)) { return true; } } return false; }
总结
HandlerMethod
目标方法。我们通过 @xxxMapping
修饰的Controller
中接口就是目标方法。
会通过 RequestMappingHanderMapping
处理器适配器找到它。
会通过 RequestMappingHandlerAdapter
处理器适配器执行。
MappingRegistry
MappingRegistry
:映射的注册中心
路径会映射为RequestMappingInfo
对象,RequestMappingInfo
又和HandlerMethod
所映射
所以流程就是先根据路径找到对应的RequestMappingInfo
,再根据RequestMappingInfo
找到对应的HandlerMethod
(目标方法)
HandlerMapping
HandlerMapping
:处理器映射
public interface HandlerMapping { // 返回此请求的 HandlerExecutionChain 对象(Handler、Interceptor) @Nullable HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }
1、RequestMappingHandlerMapping
就是用来处理标注了@xxxMapping
注解的。基于注解来映射请求到处理器方法(最常用)
2、BeanNameUrlHandlerMapping
:根据请求的URL路径与控制器Bean的名称进行匹配。如果URL路径与某个控制器的Bean名称相匹配,则该控制器将被选中来处理请求。
3、SimpleUrlHandlerMapping
:通过直接配置URL路径与处理器的映射关系来处理请求。开发者可以在配置文件中指定哪些URL应该由哪些控制器处理,提供了比BeanNameUrlHandlerMapping
更灵活的映射方式
4、WelcomePageHandlerMapping
:用来处理欢迎页。比如访问 /
就会跳转到index.html
5、RouterFunctionMapping
:函数式编程风格的接口
@xxxMapping
注解:
5个HandlerMapping
RESTful风格原理
RESTful风格原理
如果定义了多个路径相同的方法但请求方式不同,它是怎么找到对应方法的?
那也就是说getMappingsByUrl()
会根据路径找到多个RequestMappingInfo
对象。比如这样:
最终会在getMatchingMapping()
找到匹配的方法,核心就是根据不同的请求方式找不同的方法
利用RequestMappingInfo
对象里的RequestMethodsRequestCondition
对象做区分的
源码:org.springframework.web.servlet.mvc.method.RequestMappingInfo#getMatchingCondition