【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
上一篇:SpringMVC源码分析-DispatcherServlet-init方法分析
DispatcherServlet的init已经将所需要的各种Resolver准备好,可以说是万事俱备只欠东风了,下面就看看它是如何接收请求,并将请求映射到Controller上的方法,然后将返回值格式化为字符串或者使用视图解析器完成解析的
时序图
概要说明
前半部分画了从Tomcat最后一个阀门(Valve)如何一步步调用到DispatcherServlet的doService方法。
后半部分画了SpringMVC的处理流程,如下:
在DispatcherServlet中调用getHandler()得到符合条件的HandlerMapping(如:RequestMappingRequestHandler-请求参数解析、绑定。。。在这里面还会降将该请求适合的Interceptors与handler一起封装为一个HandlerExecutionChain),接着根据得到的HandlerMapping调用getHandlerAdpater()得到符合条件的HandlerAdapter(如:RequestMappingHandlerAdapter-调用Controller中的方法,返回值格式化,确定ModleAndView),在调用Controller之前执行拦截器的preHandle(),调用再接着调用HandlerAdapter的handle方法,在调用Contoller之后执行拦截器的postHandle()方法,最后调用processDispatchResult()根据MV执行转发
源码分析
DispatcherServlet-doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);//文件上传,转换request为mutipart request
multipartRequestParsed = (processedRequest != request);
// 决定当前请求使用的最优Handler,也就是说根据请求路径去找到Controller中的某一个方法
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 根据mappedHanler中的HandlerMethod决定当前请求要使用的HandlerAdpater,比如RequestMappingHandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
//getLastModified该方法都是返回-1,也不知道具体是干什么用的。感觉像是判断Mapping是否改变了,也许和热加载有关系吧
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//Invoke前处理--百度一大把SpringMVC请求流程中的讲到的东西
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 真正invoke Handler对应的方法或者其他对象,会完成参数绑定、方法调用、返回参数格式化等操作
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//Invoke后处理
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
/**
* 处理由前面选择的处理器(mappedHandler)和处理器(HandlerAdapter)调用的结果:就是一个正常或者一个异常的MV
* 然后再调用triggerAfterCompletion
* 下面的两个异常逻辑也需要调用triggerAfterCompletion
*
*
*/
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//异常-触发“完成后”处理
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
//异常-触发“完成后”处理
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
获取Handler(HandlerExecutionChain)
DispatcherServlet.getHandle()
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
/**
* 循环所有的HandlerMappings,找到合适的HandlerMethod或者其他类型的Handler
* 将找到的Handler与拦截器一起封装一个HandlerExecutionChain
* 下面看看RequestMappingHandlerMapping的getHandler方法
*/
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
RequestMappingHandlerMapping.getHandler(),最终会调用到它父类AbstractHandlerMethodMapping的getHandlerInternal()
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock();
try {
//从HandlerMappings当中找到一个最优匹配的Handler(MethodHandler)
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
上面的代码再调用AbstractHandlerMethodMapping的lookupHandlerMethod方法
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
/**
* mappingRegistry当中有一个urlLookup的Map,里面存放的是所有以目录形式访问的路径对应的RequestMappingInfo
* 比如:/testweb/user/query
* /testweb/user/save
* 但是/testweb/user/query/{userName}这种就不行了,通过这个拆分可以让更多场景迅速找到唯一的RequestMappingInfo,
* 减少循环遍历所有的RequestMappingInfos,然后再Compare的耗时
*/
List<T> 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()) {
//MatchComparator定义了排序的规则,具体规则就不赘述了,百度一下或者看看RequestMappingInfo的CompareTo方法
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
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);
}
}
获取HandlerAdpter
DispathcerServlet.getHandlerAdapter()
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
/**
* 循环所有的handlerAdpaters,调用它们的supports方法找到匹配的HandlerAdapter
* 以RequestMappingHandlerAdapter为例看看它的supports方法
*/
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
上面的supports方法实际上是RequestMappingHandlerAdapter父类AbstractHandlerMethodAdapter的supports
public final boolean supports(Object handler) {
//supportsInternal()里面居然就是一个返回一个确定的true,可以想到,自己可以扩展一个Adapter修改这个方法,更改MVC的默认匹配规则
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
可以很清楚看到这个Adapter就是处理HandlerMethod这类型的Handler
执行拦截的preHandle
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
/**
* 循环执行拦截器的preHandle方法,如果返回值为false,则还要倒序执行拦截的afterCompletion方法
*/
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
这里面有一个倒序执行拦截器的afterCompletion方法要注意
调用Adapter的handle
在这里重点看底层被调用的
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
/**
* 方法里面首先解析参数,选择对的HandlerMethodArgumentResolver,并完成参数值的绑定,比如:
* RequestParamMethodArgumentResolver负责解析,完成参数绑定。没有注解该Resolver同样适用
* 由PathVariableMethodArgumentResolver负责解析,完成参数绑定
* 由RequestBodyMethodArgumentResolver负责解析,完成参数绑定
*/
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
。。。。省略部分代码。。。。。
try {
/**
* 根据返回类型以及方法的注解判断才用那个HandlerMethodReturnValueHandler来处理返回值
*
*
*
*
*
*/
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
returnValueHandlers.handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
//确定应该使用的HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
//对invoke返回的结果进行处理,比如进行json格式化
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
看看debug时handler是谁

再看看对返回结果转换的核心代码,可以看到默认是有前面4个Converter的,最后一个Converter是我通过PostProcessor强行添加进去的

最终由MappingJacksonHttpMessageConverter完成对User对象的Json格式化转换,并将转换结果写到了Response当中
执行拦截器的postHandle
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
/**
* 循环执行拦截器的postHandle方法
*/
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
执行跳转
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
//异常MV处理
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
//在这里可以使用自定义的ExceptionHandlerResolver对异常MV进行处理
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
//渲染Handler返回的MV,渲染没有深入去看,因为现在流行前后端分离,几乎都是返回的JSON串回去
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
。。。省略部分代码。。。
if (mappedHandler != null) {
// 最后执行afterCompletion方法
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
最后只有执行完Tomcat的CoyoteAdapter.service中的如下两行代码浏览器才会收到返回值
request.finishRequest(); response.finishResponse();
总结
DispatcherServlet接收请求的处理流程涉及到几个非常重要的扩展点,自定义HandlerMapping、自定义HandlerAdapter,通常SpringMVC是不建议开发者去修改最底层的内容的,但是SpringMVC允许且建议开发者可以修改默认的HandlerMapping与HandlerAdpater当中属性中的值,比如给Adapter添加argumentResolvers,initBinderArgumentResolvers,returnValueHandlers,MessageConverters等等
除了上面的内容,当然还有经常用到的一点就是Interceptor在HandlerAdapter.handle()之前的preHandle与之后postHandle,以及afterCompletion。需要注意preHandle的返回值为false时会倒序调用拦截器中的afterCompletion方法
SpringMVC源码复杂度和Spring比起来,前者还是要轻松一些,总的来说在Tomcat启动的时候调用DispatcherServlet.init方法通过Spring容器完成了所有默认组件与配置类的实例化与参数的加载,开发者可以通过Spring提供的BeanPostProcessor修改这些默认类的实例化过程,修改它们的属性值和行为,然后在完成Dispatcher.init之后通过servlet规范调用到DispatcherServlet的service方法,通过之前已经实例化的组件与配置类实例共同作用完成了从请求到Controller的执行与返回浏览器的过程
接下来去开启SpringBoot的源码分析
来源:oschina
链接:https://my.oschina.net/u/3049601/blog/3145081