上一篇写到文件上传请求的原理。接下来就是获取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 是它的父类):
RequestMappingHandlerMapping
就是用来处理标注了xxxMapping
注解的。基于注解来映射请求到处理器方法
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();
}
}
流程分析:
getLookupPathForRequest()
:使用路径帮助器拿到请求中的请求路径lookupHandlerMethod()
:通过请求路径拿到HandlerMethod
(核心)
根据当前请求得到路径getLookupPathForRequest()
就是调用底层api得到请求路径:request.getContextPath()
、request.getRequestURI()
public String getLookupPathForRequest(HttpServletRequest request) {
// 得到路径
String pathWithinApp = getPathWithinApplication(request);
// Always use full path within current servlet context?
if (this.alwaysUseFullPath) {
return pathWithinApp;
}
// Else, use path within current servlet mapping if applicable
String rest = getPathWithinServletMapping(request, pathWithinApp);
if (StringUtils.hasLength(rest)) {
return rest;
}
else {
return pathWithinApp;
}
}
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;
}
}
根据请求路径找到对应处理器-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(比如RESTful风格就会在这一步处理)
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()
// ======AbstractHandlerMappin.class========
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;
}
// ============MappedInterceptor.class============
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
封装到HandlerExecutionChain
中 -
利用路径帮助器拿到当前请求路径
-
遍历所有的拦截器并判断当前拦截器是否拦截当前请求,拦截的话就添加到我们的
HandlerExecutionChain
对象中,然后返回
HandlerExecutionChain
对象:
这两个拦截器是默认的拦截器,不用管
里面就3个东西:处理器、拦截器集合、当前执行到第几个拦截器的索引
到这一步也就拿到了HandlerExecutionChain
对象,最后还会有跨域的处理,就把这个对象返回出去了。
跨域放在下一篇说。
总结
HandlerMethod
目标方法。我们通过 @xxxMapping
修饰的Controller
中接口就是目标方法。
会通过 RequestMappingHanderMapping
处理器适配器找到它。
会通过 RequestMappingHandlerAdapter
处理器适配器执行。
MappingRegistry
MappingRegistry
:映射的注册中心
路径会映射为RequestMappingInfo
对象,RequestMappingInfo
又和HandlerMethod
所映射
所以流程就是先根据路径找到对应的RequestMappingInfo
,再根据RequestMappingInfo
找到对应的HandlerMethod
(目标方法)
HandlerMapping
HandlerMapping
:处理器映射
public interface HandlerMapping {
String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";
String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath";
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
// 返回此请求的 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
注解:
RESTful风格原理
RESTful风格原理
如果定义了多个路径相同的方法但请求方式不同,它是怎么找到对应方法的?
那也就是说getMappingsByUrl()
会根据路径找到多个RequestMappingInfo
对象。比如这样:
最终会在getMatchingMapping()
找到匹配的目标方法,核心就是根据不同的请求方式找不同的方法
利用RequestMappingInfo
对象里的RequestMethodsRequestCondition
对象做区分的
源码:org.springframework.web.servlet.mvc.method.RequestMappingInfo#getMatchingCondition