阿昌教你看懂SpringMVC执行流程
一、前言
Hello呀!!!阿昌又来也 ╰(°▽°)╯!!!
SpringMVC的执行流程大家应该都挺熟悉的,但是真的去debug源码的人应该算少数,这里阿昌一直都想自己记录一下debug-SpringMVC的执行流程源码,做一下总结,今天终于有机会记录一下SpringMVC执行流程
==同样我还是建议打开源码一起debug看!!!==
1、流程图
- 执行图
更详细一点:
2、基于版本
SpringBoot
:2.4.1
3、前置的测试代码
这里debug只涉及到controller
和Interceptor拦截器
,且端口在8080
(๑•̀ㅂ•́)و✧
- TestController控制器
@RequestMapping("/test")
@RestController
public class TestController {
@GetMapping("/123")
public String test(@RequestParam String name, ServletResponse response){
System.out.println("name="+name);
System.out.println("response:"+response.isCommitted());
return "我是结果";
}
}
复制代码
-
MyInterceptor拦截器
拦截器就涉及到了3个执行方法的
执行顺序
- preHandle()
- postHandle()
- afterCompletion()
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor.preHandle");
System.out.println("response:"+response.isCommitted());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor.postHandle");
System.out.println("response:"+response.isCommitted());
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor.afterCompletion");
System.out.println("response:"+response.isCommitted());
}
}
复制代码
二、正文
这里我们启动服务,并用浏览器请求:http://localhost:8080/test/123?name=阿昌
(。・∀・)ノ゙
我们都知道对于SpringBoot中是自带Tomcat服务器的组件的,当一个请求发来,会被Tomcat处理,并转交给SpringMVC中的DispatcherServlet类
来做接下来的处理,他在SpringMVC中非常的重要,起着流程执行骨架的作用。
1、doDispatch
那首先Tomcat会经过流转调用去执行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);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
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)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
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);
}
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);
}
}
}
}
复制代码
2、getHandler
经过一些的初始化后,首先会去执行getHandler()
方法,去寻找对应可以去处理这个请求的mappedHandler
-
mappedHandler对应的类型是HandlerExecutionChain
HandlerExecutionChain
就是==拦截器链==
- getHandler()
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {//遍历预设的handlerMappings
for (HandlerMapping mapping : this.handlerMappings) {
//看看哪个能处理这次的请求
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
//找到了就返回这个handler(其实这个handler被包装了一层,HandlerExecutionChain)
return handler;
}
}
}
return null;
}
复制代码
- mapping.getHandler(request)
mapping.getHandler(request)拿到的是被包装后的handlerMapping,也就是HandlerExecutionChain
- getHandlerExecutionChain
- handlerMapping处理器
- 对应的拦截器
- 这次的请求request
封装成
HandlerExecutionChain
- getHandlerInternal
获取handlerMapping
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
///这次请求的uri为:test/123
String lookupPath = initLookupPath(request);//获取这次请求的uri
this.mappingRegistry.acquireReadLock();
try {
//根据这次请求和这个uri,判断获取对应能处理的HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
复制代码
4、小总结
执行这里就会遍历所有this.handlerMappings
,获取请求的uri和请求,拿到对应能够处理这次请求的handlerMapping
,并将拿到:↓
- handlerMapping处理器
- 对应的拦截器
- 这次的请求request
包装成一个HandlerExecutionChain
。
5、getHandlerAdapter()
上面我们获取到的对应的HandlerExecutionChain拦截器链
(处理器handlermapping+拦截器链+这次请求)。
接下来就要获取对应这个handlermapping对应的的适配器HandlerAdapter
- getHandlerAdapter
里面的逻辑也很简单,遍历所有的handlerAdapter
,看看哪个可以处理个handlerMapping,找到后返回
6、判断如果是get请求,是否被修改
7、applyPreHandle()
这里就是上面流程图的执行器链中的一个执行时机之一applyPreHandle
,他会去执行所有拦截器链中的每个拦截的applyPreHandle()方法
- 我们自定义的拦截器
MyInterceptor.preHandle
- 执行所有拦截器链中的每个拦截的
applyPreHandle()方法
- 每执行成功一个
this.interceptorIndex
就会给赋上i的值(拦截器变量的索引)
-
当某个拦截器中的
preHandle()
返回了false
,就会触发执行triggerAfterCompletion
-
triggerAfterCompletion
这里因为上面每次执行preHandle都会记录一下拦截器变量的索引
所以如果有一个preHandle执行返回了false,那么这里就会倒置的
去执行已经执行的拦截器的afterCompletion()
方法
8、handle
ha.handle(processedRequest, response, mappedHandler.getHandler());
复制代码
这里是真正执行
我们这次请求处理的controller对应的方法
==那阿昌这里就好奇了,他是如何执行,并拿到对应的结果的,这个结果封装在哪里???==
- handleInternal()
- invokeHandlerMethod()
- invokeAndHandle()
- invokeForRequest
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {
//获取这次请求的参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
//执行对应的controller方法
return doInvoke(args);
}
复制代码
- doInvoke
这里就是controller的代理
了,对应的代理设计模式
- invoke
==那上面拿到执行了controller的方法后,拿到的结果是如何处理的呢?==
- handleReturnValue
- writeWithMessageConverters
将处理响应的结果,写入响应中,这个方法很长,对应与servletHttp响应的介绍
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Object body;
Class<?> valueType;
Type targetType;
if (value instanceof CharSequence) {
body = value.toString();
valueType = String.class;
targetType = String.class;
}
else {
body = value;
valueType = getReturnValueType(body, returnType);
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
}
if (isResourceType(value, returnType)) {
outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
outputMessage.getServletResponse().getStatus() == 200) {
Resource resource = (Resource) value;
try {
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
body = HttpRange.toResourceRegions(httpRanges, resource);
valueType = body.getClass();
targetType = RESOURCE_REGION_LIST_TYPE;
}
catch (IllegalArgumentException ex) {
outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
}
}
}
MediaType selectedMediaType = null;
MediaType contentType = outputMessage.getHeaders().getContentType();
boolean isContentTypePreset = contentType != null && contentType.isConcrete();
if (isContentTypePreset) {
if (logger.isDebugEnabled()) {
logger.debug("Found 'Content-Type:" + contentType + "' in response");
}
selectedMediaType = contentType;
}
else {
HttpServletRequest request = inputMessage.getServletRequest();
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
if (body != null && producibleTypes.isEmpty()) {
throw new HttpMessageNotWritableException(
"No converter found for return value of type: " + valueType);
}
List<MediaType> mediaTypesToUse = new ArrayList<>();
for (MediaType requestedType : acceptableTypes) {
for (MediaType producibleType : producibleTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
if (mediaTypesToUse.isEmpty()) {
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}
if (logger.isDebugEnabled()) {
logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
}
return;
}
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
for (MediaType mediaType : mediaTypesToUse) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using '" + selectedMediaType + "', given " +
acceptableTypes + " and supported " + producibleTypes);
}
}
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
return;
}
}
}
if (body != null) {
Set<MediaType> producibleMediaTypes =
(Set<MediaType>) inputMessage.getServletRequest()
.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
throw new HttpMessageNotWritableException(
"No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
}
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}
复制代码
重要的部分在:
body
:controller处理的结果
outputMessage
:上面封装的webRequest
中拿到的响应输出
//将通过converter将内容body写入outputMessage
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
复制代码
- write()
t
:就是我们的结果“我是结果”
@Override
public final void write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
final HttpHeaders headers = outputMessage.getHeaders();
addDefaultHeaders(headers, t, contentType);
if (outputMessage instanceof StreamingHttpOutputMessage) {
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
streamingOutputMessage.setBody(outputStream -> writeInternal(t, new HttpOutputMessage() {
@Override
public OutputStream getBody() {
return outputStream;
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
}));
}
else {
//将t写入到outputMessage
writeInternal(t, outputMessage);
//flush刷新响应body
outputMessage.getBody().flush();
}
}
复制代码
==他是如何写入outputMessage的呢?==
- writeInternal
将t写入到outputMessage
- copy
就直接通过OutputStreamWriter
直接调用write
写入in也就是结果,最后flush
刷新
执行完写入响应结果后,他就会提交
这次响应,什么是 提交响应呢?介绍:响应提交
9、小总结
-
上面我们拿到对应的
HandlerExecutionChain拦截器链
这里包含:↓
- 处理器handlermapping
- 拦截器链
- 这次请求
-
从HandlerExecutionChain拦截器链到拿到对应的
handlerMapping
-
根据handlerMapping,去获取对应的
HandlerAdapter
的处理器对应的适配器
-
判断请求方式是否是
get
,如果是就判断是否被修改过 -
遍历所有的拦截器链,执行每个
applyPreHandle()
- 每次执行都记录执行的拦截器链遍历索引
- 如果有一个applyPreHandle返回false,就去导致执行triggerAfterCompletion,去执行
倒置
每一个已经执行applyPreHandle()方法的拦截器的afterCompletion()
方法
-
再通过适配器执行
真正处理
这次请求的controller方法handle
,返回ModelAndView
(==这里我们不是jsp,所以肯定必然没有ModelAndView对应,就肯定为null==)
10、applyPostHandle()
// Actually invoke the handler.
//真正执行controller对应的方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//判断当前请求的所选处理程序是否选择处理异步处理
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//给对应返回的ModelAndView对象赋予默认名
applyDefaultViewName(processedRequest, mv);
//倒置去执行每个拦截器链中每个拦截的applyPostHandle()方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
复制代码
倒置
去执行每个拦截器链中每个拦截的applyPostHandle()
方法
11、processDispatchResult()
在processDispatchResult()
方法中,会做一些收尾的工作
这里会判断是否需要去渲染视图ModelAndView
,因为我们这里是通过json响应返回,必然是没有modelAndView对象
去执行triggerAfterCompletion()
,这里上面已经出现过,也就是去执行已经执行过preHandle
拦截器的afterCompletion()
方法
这里会倒置执行
已经执行过preHandle()方法的拦截器的afterCompletion()
方法
12、小总结
-
执行适配器对应的handle()方法也就是对应的controller的方法后,会返回
ModelAndView
-
判断当前请求的所选处理程序
是否选择处理异步处理
-
给对应返回的ModelAndView对象赋予
默认名
-
倒置
去执行每个拦截器链中每个拦截的applyPostHandle()
方法 -
执行
processDispatchResult()
做收尾工作- 去执行
triggerAfterCompletion()
,这里上面已经出现过,也就是去执行已经执行过preHandle
拦截器的afterCompletion()
方法
- 去执行
三、总结
-
执行这里就会遍历所有
this.handlerMappings
,获取请求的uri和请求,拿到对应能够处理这次请求的handlerMapping
,并将拿到:↓- handlerMapping处理器
- 对应的拦截器
- 这次的请求request
包装成一个
HandlerExecutionChain
。 -
从HandlerExecutionChain拦截器链到拿到对应的
handlerMapping
-
根据handlerMapping,去获取对应的
HandlerAdapter
的处理器对应的适配器
-
判断请求方式是否是
get
,如果是就判断是否被修改过 -
遍历所有的拦截器链,执行每个
applyPreHandle()
- 每次执行都记录执行的拦截器链遍历索引
- 如果有一个applyPreHandle返回false,就去导致执行triggerAfterCompletion,去执行
倒置
每一个已经执行applyPreHandle()方法的拦截器的afterCompletion()
方法
-
再通过适配器执行
真正处理
这次请求的controller方法handle
,返回ModelAndView
(==这里我们不是jsp,所以肯定必然没有ModelAndView对应,就肯定为null==)
-
执行适配器对应的handle()方法也就是对应的controller的方法后,会返回
ModelAndView
-
判断当前请求的所选处理程序
是否选择处理异步处理
-
给对应返回的ModelAndView对象赋予
默认名
-
倒置
去执行每个拦截器链中每个拦截的applyPostHandle()
方法 -
执行
processDispatchResult()
做收尾工作- 判断是否有
ModelAndView
,有就渲染,没有执行下面的 - 去执行
triggerAfterCompletion()
,这里上面已经出现过,也就是去执行已经执行过preHandle
拦截器的afterCompletion()
方法
- 判断是否有
四、结尾
以上SpringMVC的流程就执行完毕了,因为不涉及到jsp的流程,所有DispatcherServlet
就拿到上面处理的响应结果,因为ModelAndView
为空,就没请求渲染视图了,直接响应tomcat,然后tomcat就返回给响应请求了
以上就是这次的全部内容,感谢你能看到这里 (๑ˉ∀ˉ๑)!!!