SpringMVC源码解析之Servlet
SpringMVC源码解析之GenericServlet和HttpServlet
SpringMVC源码解析之DispatcherServlet:生命周期init()和destroy()
SpringMVC源码解析之DispatcherServlet:请求处理
SpringMVC源码解析之RequestMappingHandlerMapping:getHandler
getHandler是HandlerMapping接口中的唯一方法,用于根据请求找到匹配的处理器。因此getHandler也是包括RequestMappingHandlerMapping在内的所有实现类中最核心的一个方法,其方法调用链如下:
//AbstractHandlerMapping#getHandler
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//获取handler的具体逻辑,留给子类实现
Object handler = getHandlerInternal(request);
//null handler,采用默认的handler,即属性defaultHandler
if (handler == null) {
handler = getDefaultHandler();
}
//还是null handler,直接返回null
if (handler == null) {
return null;
}
//handler是beanName, 从容器里获取对应的bean
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//根据handler和request获取处理器链HandlerExecutionChain
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
//CORS请求的处理
if (CorsUtils.isCorsRequest(request)) {
//全局配置
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
//handler的单独配置
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
//handler的所有配置,全局配置+单独配置
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
//根据cors配置更新HandlerExecutionChain
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
(1)调用getHandlerInternal获取匹配的处理器
(2)如果获取失败,获取默认的处理器。如果没有设置默认处理器,直接返回null。
(3)如果获取到的处理器是字符串,将其作为beanName从容器中获取对应的对象作为处理器。
(4)调用getHandlerExecutionChain封装成HandlerExecutionChain
(5)针对CORS请求做特殊处理
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
//MappedInterceptor根据url判断是否匹配
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
//其它类型的Interceptor对所有请求都有效
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
从请求中获取路径找到匹配的拦截器Interceptor集合,与Handler一起封装成HandlerExecutionChain。
如上一篇博客中对onApplicationContext方法的介绍中提到的一样,SpringMVC中配置的所有拦截器最终封装成HandlerInterceptor保存在adaptedInterceptors中,其中MappedInterceptor类型的拦截器只针对特点的url有效,其它类型的拦截器对所有请求都有效。
//AbstractHandlerMethodMapping#getHandlerInternal
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//请求的路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
this.mappingRegistry.acquireReadLock();
try {
//找到匹配的处理器HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
logger.debug("Returning handler method [" + handlerMethod + "]");
}
else {
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
主要功能是在读写锁加锁的情况下调用lookupHandlerMethod找到匹配的处理器
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List matches = new ArrayList<>();
//根据url获取对应的匹配条件集
List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
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()) {
//getMappingComparator抽奖方法,用于匹配条件T的比较
Comparator comparator = new MatchComparator(getMappingComparator(request));
//排序
matches.sort(comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
}
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
//CORS的预请求,返回new HandlerMethod(new EmptyHandler(), ClassUtils.getMethod(EmptyHandler.class, "handle"));
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
//确保最优的匹配条件T是唯一的,最优的匹配条件必须大于其它所有匹配条件,不能相同
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
//找到匹配合适的匹配条件后的处理,默认加入请求的属性Attributes中
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
//没有找到合适的匹配条件T,handleNoMatch为默认方案
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
(1)调用addMatchingMappings查找合适的匹配条件并加入到matches
首先根据url在对应的匹配集directPathMatches内进行查找,如果查找不到则进行全局查找。
(2)找到优先级最高的匹配条件bestMatch。
要求bestMatch的优先级必须大于matches剩余的所有的匹配条件,如果存在相同的,则抛出错误。
(3)调用handleMatch进行匹配成功后的处理
(4)调用handleNoMatch进行匹配失败后的处理
//根据请求request从匹配集mappings筛选出适用的匹配条件
private void addMatchingMappings(Collection mappings, List matches, HttpServletRequest request) {
for (T mapping : mappings) {
//抽象方法,判断并返回适用的匹配条件
T match = getMatchingMapping(mapping, request);
if (match != null) {
matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
}
}
}
根据getMatchingMapping进行筛选,符合的加入集合matches
protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) {
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
}
默认的handleMatch为在请求中添加属性进行标志
protected HandlerMethod handleNoMatch(Set mappings, String lookupPath, HttpServletRequest request)
throws Exception {
return null;
}
默认的没有找到处理器时不作处理,返回null。
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
return info.getMatchingCondition(request);
}
检查匹配条件info与请求是否匹配,不匹配返回null,匹配返回匹配的RequestMappingInfo 对象,具体逻辑委派RequestMappingInfo 的getMatchingCondition处理。
protected Comparator getMappingComparator(final HttpServletRequest request) {
return (info1, info2) -> info1.compareTo(info2, request);
}
对于RequestMappingInfo大小的比较委派给RequestMappingInfo的compareTo方法处理。
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
//父类AbstractHandlerMethodMapping中设置lookupPath为请求属性
super.handleMatch(info, lookupPath, request);
String bestPattern;
Map uriVariables;
Map decodedUriVariables;
Set patterns = info.getPatternsCondition().getPatterns();
if (patterns.isEmpty()) {
bestPattern = lookupPath;
uriVariables = Collections.emptyMap();
decodedUriVariables = Collections.emptyMap();
}
else {
bestPattern = patterns.iterator().next();
uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
}
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);
if (isMatrixVariableContentAvailable()) {
Map> matrixVars = extractMatrixVariables(request, uriVariables);
request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
}
if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
Set mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
}
}
除了lookupPath外,还将更多的属性设置到了请求中,方便后面的使用,与HandlerMapping本身并没有作用。
protected HandlerMethod handleNoMatch(Set infos, String lookupPath,
HttpServletRequest request) throws ServletException {
//记录所有url匹配的匹配条件RequestMappingInfo集
PartialMatchHelper helper = new PartialMatchHelper(infos, request);
//没有url匹配的RequestMappingInfo
if (helper.isEmpty()) {
return null;
}
//判断url匹配的RequestMappingInfo集中最终匹配失败的原因
//1. Methods不匹配(只有有一个匹配则返回false)
if (helper.hasMethodsMismatch()) {
Set methods = helper.getAllowedMethods();
//OPTIONS请求HttpOptionsHandler做处理器,返回携带了所有允许的Methods的请求头
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
HttpOptionsHandler handler = new HttpOptionsHandler(methods);
return new HandlerMethod(handler, HTTP_OPTIONS_HANDLE_METHOD);
}
//非OPTIONS请求抛出Methods不支持的异常
throw new HttpRequestMethodNotSupportedException(request.getMethod(), methods);
}
//consumes不匹配,抛出MediaType不支持的异常
if (helper.hasConsumesMismatch()) {
Set mediaTypes = helper.getConsumableMediaTypes();
MediaType contentType = null;
if (StringUtils.hasLength(request.getContentType())) {
try {
contentType = MediaType.parseMediaType(request.getContentType());
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
}
throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<>(mediaTypes));
}
//produces不匹配,抛出MediaType不支持的异常
if (helper.hasProducesMismatch()) {
Set mediaTypes = helper.getProducibleMediaTypes();
throw new HttpMediaTypeNotAcceptableException(new ArrayList<>(mediaTypes));
}
//params不匹配,抛出参数不匹配的异常
if (helper.hasParamsMismatch()) {
List conditions = helper.getParamConditions();
throw new UnsatisfiedServletRequestParameterException(conditions, request.getParameterMap());
}
return null;
}
细化了找不到匹配的匹配条件的原因,url匹配但methods(OPTIONS除外)/produces/consumes/params不匹配抛出对应异常,其它的返回null。
针对RequestMappingInfo集进行迭代,查找url匹配的请求。如果有url匹配的匹配条件,根据其匹配失败的原因(如methods不匹配,produces/consumes不匹配,params不匹配)抛出对应的异常。
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
//methodsCondition与请求的methods进行判断,返回匹配的方法,下面的getMatchingCondition方法的同理
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
if (methods == null || params == null || headers == null || consumes == null || 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;
}
return new RequestMappingInfo(this.name, patterns,
methods, params, headers, consumes, produces, custom.getCondition());
}
将RequestMappingInfo的methods等属性与请求中的属性进行匹配,只有有一个匹配失败则认为不匹配,返回false。
RequestMappingInfo的methods等属性可能是一个列表,有多个值,但是最终匹配的只有一个,因此匹配成功需要新建一个RequestMappingInfo返回。
public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
int result;
// Automatic vs explicit HTTP HEAD mapping
if (HttpMethod.HEAD.matches(request.getMethod())) {
result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
if (result != 0) {
return result;
}
}
result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
if (result != 0) {
return result;
}
result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
if (result != 0) {
return result;
}
result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
if (result != 0) {
return result;
}
result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
if (result != 0) {
return result;
}
result = this.producesCondition.compareTo(other.getProducesCondition(), request);
if (result != 0) {
return result;
}
// Implicit (no method) vs explicit HTTP method mappings
result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
if (result != 0) {
return result;
}
result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
if (result != 0) {
return result;
}
return 0;
}
按照METHODS(只针对HEAD请求),patterns,params,headers,consumes,produces,METHODS(HEAD以外的请求),自定义条件的顺序进行比较确定优先级