springboot-mvc

153 阅读5分钟

概况

三层架构分别代表

  • View,用于接受用户提交请求的代码
  • Service,系统的业务逻辑主要在这里编写
  • Dao,直接操作数据库在这里编写

MVC分别代表:

  • M指Model模型。承载数据,并对用户提交请求进行计算的模块,分为数据承载bean和业务处理bean。在三层架构中包含了Service和Dao两层
  • V指View视图。为用户提供使用界面,与用户进行直接交互。在三层架构中属于View层的页面相关的部分
  • C指Controller控制器。用于将用户请求转发给相应的Model处理,并处理Model的计算结果向用户提供相应相应。在三层架构中属于View层中Controller相关部分

入口介绍

MVC主要是通过DispatcherServlet实现的,他继承了HttpServlet类,沿用了原生的java servlet编程,在HttpServlet上面包装了若干层,主要处理几种逻辑

  1. 将POST,GET的概念转移到了路径映射中,方便统一处理
  2. 自定义增加了拦截器链,不满足条件直接返回
  3. 将消息按照content-type转换,是json或者html格式,文件上传下载,有统一的处理,可以自定义增加处理方式
  4. 最后处理视图,如果返回的是页面,扩展了视图模板(restful编程就不需要这一层,直接使用@RestController)

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 {
                //判断请求是否是 multipart post (常见的有 post 表单提交的数据)
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                //通过request获取handler,包括 intercepter 信息
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                //获取 handler 适配器
                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 (logger.isDebugEnabled()) {
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                //调用 intercepter.perHandler()方法
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.
                //调用controller方法发挥 ModelAndView
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                //当 ModelAndView 中不包含视图时获取默认视图
                applyDefaultViewName(processedRequest, mv);
                //调用 intercepter.perHandler()方法
                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);
            }
            //视图渲染并将渲染后的视图文件(html)或者 json 等写入Response body 返回给浏览器
            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);
                }
            }
        }
    }

路径映射

在doDispatch方法中,调用getHandler方法获取request的映射路径,DispatcherServlet中默认的handlerMappings有五种,他们属于AbstractHandlerMapping的子类

  1. SimpleUrlHandlerMapping。该bean在WebMvcAutoConfiguration的内部配置类 FaviconConfiguration中声明。它匹配的URL路径是**/favicon.ico,作用是响应浏览器获取收藏夹中的icon文件
  2. RequestMappingHandlerMapping。该bean在WebMvcAutoConfiguration的内部配置类 EnableWebMvcConfiguration中声明。它和XML的注解驱动一样,匹配在Controller中@RequestMapping注解指定的URL
  3. WelcomePageHandlerMapping。该bean在WebMvcAutoConfiguration的内部配置类 WebMvcAutoConfigurationAdapter中声明。给URL路径是/或者/index映射到欢迎页面
  4. BeanNameUrlHandlerMapping。bean的名称(也就是beanID)作为URL,由匹配该URL的Controller处理业务。Controller必须继承AbstractController几乎不会使用
  5. 第二个SimpleUrlHandlerMapping。该bean在WebMvcAutoConfiguration的内部配置类 EnableWebMvcConfiguration的父类WebMvcConfigurationSupport中声明,它是专门用于映射资源文件的handlerMapping

然后从项目启动时,初始化好的缓存中根据请求路径和请求方式获取HandlerMethod,方便执行具体的controller方法。

拦截器

拦截器和动态代理不同,他不是动态生成字节码,通过改变类来处理,他没有使用反射,在生成HandlerMethod实例的时候,根据路径匹配,将拦截器放入的对应的实例中,在使用的时候只需要显示的调用就行了

//invoke代码前,执行拦截器,不满足条件立即返回,后续不会对消息这些做处理,所以拦截器返回的如果要做处理,只能使用传统方法通过response中直接写入值
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
}
//invoke代码后,之后拦截器的后续操作
mappedHandler.applyPostHandle(processedRequest, response, mv);

消息转换

消息转换是在执行invoke之前的request的转换和invoke之后的response的转换,下面是执行的堆栈信息,主要讲response消息的转换

  1. DispatcherServlet->doDispatch方法的代码
//准备开始执行HandlerMethod,它里面有很多信息
ha.handle(processedRequest, response, mappedHandler.getHandler());
  1. AbstractHandlerMethodAdapter->handle方法的代码
//他是一个抽象方法
handleInternal(request, response, (HandlerMethod) handler);
  1. RequestMappingHandlerAdapter->handleInternal方法的代码
//里面有一些逻辑代码,核心是开始执行HandlerMethod
invokeHandlerMethod(request, response, handlerMethod);
  1. RequestMappingHandlerAdapter->invokeHandlerMethod方法的代码
//前面有一些设置,初始化,这里只执行
invocableMethod.invokeAndHandle(webRequest, mavContainer);
  1. ServletInvocableHandlerMethod->invokeAndHandle方法的代码
//第一句就很暴力,直接执行controler中的值,返回Object对象
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//这里是对返回值进行处理
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
  1. HandlerMethodReturnValueHandlerComposite->handleReturnValue方法的代码
//选择返回值的处理器
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
//进行处理
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
  1. RequestResponseBodyMethodProcessor->handleReturnValue方法的代码
//开始准备写入response
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
  1. AbstractMessageConverterMethodProcessor->writeWithMessageConverters方法的代码
//这里就是获得所有的messageConverters,按照顺序来处理,根据一些content-type这些值,来选择处理的messageConverters
for (HttpMessageConverter<?> converter : this.messageConverters) {
    //省略
}
  1. AbstractHttpMessageConverter->writeInternal方法

这是一个抽象类,抽象方法,这个抽象类是不是很熟悉,对吧,如果添加消息转换器,都会继承这个父类,然后复写writeInternal转换器

在springboot中一般都是这样添加消息转换器的

@Bean
public HttpMessageConverters customConverters() {
    //省略

    //这一句最终会放在前面第八步中的this.messageConverters,最前面
    return new HttpMessageConverters(fastJson);
}