#1.映射机制 在探究 SpringMVC HandlerMapping 原理之前先来了解 SpringMVC 的映射机制,知其然才能知其所以然;
解读 org.springframework.web.bind.annotation.RequestMapping
- name ,作用 : 根据名称做请求映射;
- value ,作用 :(是 path 属性的别名)根据路径做请求映射;
- path , 作用 :(是 value 属性的别名)同样也是根据请求路径做映射;
- method , 作用 :根据请求是那种类型的 HTTP 方法(GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE)做映射;
- params ,
- headers ,作用 : 根据指定的请求头做映射;
- consumes , 作用 : 限定消息的消费方(客户端——>发送请求——>服务器)
,此时消息的消费方是服务器,所指定接受的媒体类型;
@RequestMapping(
path = "/consumes",
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE
)
public String consumesHandler(Map<String,Object> model) {
model.put("handler",this.getClass().getName());
model.put("path","/consumes");
model.put("consumes",MediaType.APPLICATION_FORM_URLENCODED_VALUE);
return "show";
}
produces 限定媒体类型;
- produces , 作用限定客户端所能接收的媒体类型,由客户端告诉服务器自己能接受那种媒体类型的响应。
要想达到这个目的需要使用 Accept 请求头,比如 : Accept = application/json
@RequestMapping(
path = "/produces",
produces = MediaType.APPLICATION_JSON_VALUE
)
public String producesHandler(Map<String,Object> model) {
model.put("handler",this.getClass().getName());
model.put("path","/produces");
model.put("produces",MediaType.APPLICATION_JSON_VALUE);
return "show";
}
- 常见媒体类型:
text/html : HTML格式 ;text/plain :纯文本格式 ;text/xml :XML格式
image/gif :gif图片格式 ; image/jpeg :jpg图片格式 ; image/png:png图片格式
application/x-www-form-urlencoded : <form encType=””>中默认的encType,
form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)。
multipart/form-data : 当你需要在表单中进行文件上传时,就需要使用该格式;
application/xhtml+xml :XHTML格式 application/xml : XML数据格式
application/atom+xml :Atom XML聚合格式 application/json : JSON数据格式
application/pdf :pdf格式 application/msword : Word文档格式
application/octet-stream : 二进制流数据(如常见的文件下载)。
至此通过对 @RequestMapping 注解的分析,我们已经大致的了解了SpringMVC的对映射的支持,
和它映射的策略有哪些,接下来要做的就是探究 SpringMVC 究竟是在诸多的 handler (Controller 中被
@RequestMapping 注解的方法) 中准确定位到正确的符合预期的那一个handler。
2. HandlerMapping 概况分析
- <1> HandlerMapping 的作用 : 源码中 HandlerMapping 接口的注释: Interface to be implemented by objects that define a mapping
between requests and handler objects. 已经很明确的表示出 : HandlerMapping 是请求和处理请求这两
者之前的映射(也就是二者之间的一个桥梁) 。
- <2> HandlerMapping 类层级结构:
3. 源码探究请求映射过程
- <1> 为当前 request 查找合适的处理器 :
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
// 位于 DispatcherServlet
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 遍历所有的 HandlerMapping 实例,调用其 getHandler 当其中任意一个的返回值不
// 为 null 则认为找到了与请求相匹配的处理器链条,立即返回该处理器链条
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
// 上一步调用了 hm.getHandler(request); hm 为 RequestMappingHandlerMapping
// 位于 AbstractHandlerMapping
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 获取处理器
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
//
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
// 获取处理器执行链
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
// 获取处理器
// 位于 AbstractHandlerMethodMapping
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 获取到请求URI , 例如 : /mapping/path
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
this.mappingRegistry.acquireReadLock();
try {
// 获取处理器方法实例,其中封装了对应处理器的相关信息
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();
}
}
// 获取到最匹配的一个 HandlerMethod
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
// SpringMVC 初始化 controller 时会将 @RequestMapping 中定义的路径全部加载统一管理,
// 确切的说是保存在 org.springframework.util.MultiValueMap 中 ,路径作为 key , value 是 LinkedList
// LinkedList 中存放了 RequestMappingInfo ,其中记录了 @RequestMapping 中支持的相关属性信息
// private final String name;
// private final PatternsRequestCondition patternsCondition;
// private final RequestMethodsRequestCondition methodsCondition;
// private final ParamsRequestCondition paramsCondition;
// private final HeadersRequestCondition headersCondition;
// private final ConsumesRequestCondition consumesCondition;
// private final ProducesRequestCondition producesCondition;
// private final RequestConditionHolder customConditionHolder;
// 保存在org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry 中
// 从 MappingRegistry 中获取对应路径的所有直接匹配项,这里仅仅是对路径做了匹配
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
// 由获取到的直接匹配路径构造
// org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.Match 实例
// Match 中封装了请求路径和 HandlerMethod , HandlerMethod 存储在 MappingRegistry 中
// 请求 URI 和 支持的 HTTP 方法,以及@RequestMapping 中定义的其他属性字符串 作为 key ,
// value 为一个 List ,该 List 中存放的是 HandlerMethod
// 值为 List 体现了 SpringMVC 的优良设计,比如灵活的映射策略和对 RESTful 很好的支持
// 此时就是决定请求和处理器能否最终找到了彼此的时刻了
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));
Collections.sort(matches, comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
lookupPath + "] : " + matches);
}
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();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
// 位于 AbstractHandlerMethodMapping
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
// 根据当前 request 和 mapping : {[/mapping/headers],headers=[YOURNAME]}
// 去进行匹配一个 RequestMappingInfo 实例,和 @RequestMapping 中属性对应的信息都以字符串的形式在 mapping
// 中表示
T match = getMatchingMapping(mapping, request);
if (match != null) {
matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
}
}
}
// 根据 @RequestMapping 中的属性设置和去和当前请求匹配
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
return info.getMatchingCondition(request);
}
// 位于 RequestMappingInfo 中
// 对当前 request 中和 @RequestMapping 注解中属性相对应的信息进行匹配
// @RequestMapping 中每一个属性都有一个对应的 RequestCondition 对象
// 最后返回一个 RequestMappingInfo 实例
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
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());
}
- RequestCondition 类结构
- 如果上述过程都正常执行完成,最终会返回最佳匹配的 HandlerMethod , 如果没有找到一个
HandlerMethod 那么会返回 null , 此时 SpringMVC 会去获取 DefaultHandler 。到现在为止 HandlerMapping 的功能大致分析完了。由于现在主流的Spring官方推荐的使用方式都是 Spring
3.1 之后的 @RequestMapping , RequestMappingHandlerMapping ,所以本文主要针对该方式 的请求映射做了分析,当然SpringMVC也支持其他的请求映射策略,分别对应不同的 HandlerMapping
接口的子类,不过既然我们已经掌握了最优的方案又为什么要去研究那些相较之下并不是最优的
方案呢:),当然了或许每个人都有不同的见解,这才是世界变得缤纷多彩的原因