目录
2)、匹配需要调用的具体方法封装的HandlerMethod
2、匹配连接器链,组装返回HandlerExecutionChain对象
为了方便理解,还是继续SpringMVC源码(四)- 常用HandlerMapping和HandlerAdaptor组合和使用方式#3、RequestMappingHandlerMapping和RequestMappingHandlerAdapter中的demo,当前的请求URL为:
http://127.0.0.1:9999/annotationController
getHandler
在上一篇准备好HandlerMapping列表后,继续DispatcherServlet的doDispatch方法 -> getHandler方法,根据当前HttpServletRequest(其实当前为其子类型RequestFacade)适配具体要调用的Controller方法和需要执行的拦截器链,如下:
@Nullable
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列表,如果匹配到就进行返回。当前只分析常用的@RequestMapping注解方式,则会调用RequestMappingHandlerMapping的getHandler方法:
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 匹配需要调用的方法对象(HandlerMethod)
Object handler = getHandlerInternal(request);
// 如果没有获取到,赋值默认的操作器
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// 如果被执行的调用类型为String,则认为是BeanFactory中Bean的名称,获取返回
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 获取当前请求的拦截器(HandlerInterceptor)执行链
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
// 对cors跨域方式的处理
if (hasCorsConfigurationSource(handler)) {
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;
}
1、匹配需要调用的方法对象(HandlerMethod)
2、如果没有获取到,赋值默认的操作器(默认为null)
3、如果被执行的调用类型为String,则认为是BeanFactory中Bean的名称,获取返回
4、获取当前请求的拦截器(HandlerInterceptor)执行链,并组装HandlerExecutionChain(HandlerMethod和HandlerInterceptor调用链)
5、对cors跨域方式的处理
1、匹配HandlerMethod
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
try {
return super.getHandlerInternal(request);
} finally {
ProducesRequestCondition.clearMediaTypesAttribute(request);
}
}
主要逻辑还是在父类AbstractHandlerMethodMapping中完成的,只是finally中需要清除Request的MediaType。
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
} finally {
this.mappingRegistry.releaseReadLock();
}
}
在mappingRegistry的读锁条件下,主要做了两个事:
1、根据HttpServletRequest获取需要调用的情况的路径(比如当前为/annotationController),并设置为请求的属性
2、根据路径,获取对应的HandlerMethod(可以参看上一篇博客理解该类型)
1)、获取真实的请求路径
具体的获取请求路径委派给UrlPathHelper进行处理,如下:
public String getLookupPathForRequest(HttpServletRequest request) {
// Always use full path within current servlet context?
if (this.alwaysUseFullPath) {
return getPathWithinApplication(request);
}
// Else, use path within current servlet mapping if applicable
String rest = getPathWithinServletMapping(request);
if (!"".equals(rest)) {
return rest;
} else {
return getPathWithinApplication(request);
}
}
由于父类中需要处理其他类型的匹配,所以会有Servlet等。@RequestMapping类型则会调用getPathWithinApplication方法:
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;
}
}
先获取请求(HttpServletRequest)中的上下文的名称(项目名称,也可以是Spring Boot中的server.context-path属性),比如当前没有设置则为空字符串。请求时,还可能根据项目的编码,即Tomcat的uriEncoding进行处理。
再获取请求路径,需要考虑各种请求,具体不细究了。
再获取真实的可能需要匹配的路径。
比如当前的项目路径为空字符串,请求路径为/annotationController,那么在请求路径上去除项目路径就是真实的匹配路径。
2)、匹配需要调用的具体方法封装的HandlerMethod
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 从注册的urlLookup中获取(key为@RequestMapping类和方法字符串相加 value为RequestMappingInfo)
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
// 之前就注册了,一般都不为null
if (directPathMatches != null) {
// 将获取到的RequestMappingInfo封装为Match,添加到matches中
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()) {
// 获取比较器
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
// 排序
matches.sort(comparator);
Match bestMatch = matches.get(0);
// 大于一个则需要比较是否有限级一样,一样需要抛异常(一般到这里只有一个)
if (matches.size() > 1) {
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("省略");
}
}
// 将HandlerMethod添加到request属性中
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
// 将lookupPath添加到request属性中
handleMatch(bestMatch.mapping, lookupPath, request);
// 返回匹配的HandlerMethod
return bestMatch.handlerMethod;
} else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
主要是从之前注册的urlLookup中获取(key为@RequestMapping类和方法字符串相加 value为RequestMappingInfo)获取到RequestMappingInfo对象。
再从mappingLookup(RequestMappingInfo与HandlerMethod的关系)中获取HandlerMapping
还将request中添加了两个属性,返回获取到的HandlerMethod对象。
createWithResolvedBean
public HandlerMethod createWithResolvedBean() {
Object handler = this.bean;
if (this.bean instanceof String) {
String beanName = (String) this.bean;
handler = this.beanFactory.getBean(beanName);
}
return new HandlerMethod(this, handler);
}
根据bean名称,从工厂获取对应的Bean,再new一个对象。没看懂是为什么。
2、匹配连接器链,组装返回HandlerExecutionChain对象
protected HandlerExecutionChain getHandlerExecutionChain(Object handler,
HttpServletRequest request) {
// 创建HandlerExecutionChain对象
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
//
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
正常状况下当前的handler为HandlerMethod类型,那么会new HandlerExecutionChain(handler)。后面就是对RequestMappingHandlerMapping的回调方法setApplicationContext初始化的adaptedInterceptors属性进行遍历,如果没有MappedInterceptor类型则直接进行加入。
MappedInterceptor为final类型,不能继承,而我们自定义的拦截器会直接实现MappedInterceptor。正常调用情况下是没有MappedInterceptor类型的,不知道在上面情况下使用。又再一次获取lookupPath(比如当前为/annotationController)。个人优化理解:1、如果考虑优化则可以在上面传入该值而不是再获取一次,毕竟每次请求都会调用(只是这样每一层更清晰);2、正常情况下没有MappedInterceptor类型的拦截器,为什么每次要调用获取lookupPath,有点浪费性能。