阿昌教你看懂SpringMVC执行流程

阿昌教你看懂SpringMVC执行流程

一、前言

Hello呀!!!阿昌又来也 ╰(°▽°)╯!!!

SpringMVC的执行流程大家应该都挺熟悉的,但是真的去debug源码的人应该算少数,这里阿昌一直都想自己记录一下debug-SpringMVC的执行流程源码,做一下总结,今天终于有机会记录一下SpringMVC执行流程

==同样我还是建议打开源码一起debug看!!!==

1、流程图

  • 执行图

在这里插入图片描述

更详细一点

在这里插入图片描述

2、基于版本

SpringBoot:2.4.1

3、前置的测试代码

这里debug只涉及到controllerInterceptor拦截器,且端口在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的代理了,对应的代理设计模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3QukQUNv-1639907575817)(../AppData/Roaming/Typora/typora-user-images/image-20211219165433034.png)]

  • 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就返回给响应请求了

在这里插入图片描述

以上就是这次的全部内容,感谢你能看到这里 (๑ˉ∀ˉ๑)!!!

Guess you like

Origin juejin.im/post/7049964265888972808