美文网首页
SpringMVC源码解析——前置控制器DispatcherSe

SpringMVC源码解析——前置控制器DispatcherSe

作者: 橙味菌 | 来源:发表于2019-08-09 10:22 被阅读0次

    点击回顾SpringMVC请求响应流程

    前置控制器类

    前置控制器DispatcherServlet,间接继承自HttpServlet,本质上还是Servlet

    1565316555822.png

    当请求的url符合前置控制器DispatcherServlet的映射路径时,Servlet容器(Tomcat)会调用其service方法,其实现在父类FrameworkServlet中

    1. 查看FrameworkServlet的service方法(看看它怎么处理请求)
    @Override
    protected void service(...) throws ... {
        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        //增加对 PATCH 请求的处理
        if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
            processRequest(request, response);
        }
        else {
            super.service(request, response);
        }
    }
    
    //重写doGet()方法
    @Override
    protected final void doGet(...) throws ... {
        processRequest(request, response);
    }
    //重写doPost()方法
    @Override
    protected final void doPost(...) throws ... {
        processRequest(request, response);
    }
    

    FrameworkServlet的service方法处理了Patch、Get、Post三种类型请求(通过调用processRequest方法)

    1. 查看FrameworkServlet的processRequest方法(进一步看看请求如何被处理)
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
     ​
      long startTime = System.currentTimeMillis();
      Throwable failureCause = null;
     ​
      //获取之前的Locale上下文,同时从request中获取Locale上下文
      //Locale上下文接口用于获取对象Locale(用于构造Java国际化情景),只有一个方法——getLocale()
      LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
      LocaleContext localeContext = buildLocaleContext(request);
     ​
      //获取之前的Request属性数据,同时从request中获取新的Request属性数据
      RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
      ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
     ​
      WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
      asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
     ​
      //初始化上下文
      initContextHolders(request, localeContext, requestAttributes);
     ​
      try {
      //doService() FramworkServlet没有实现,只是简单定义,交由 DispatchServlet实现
      doService(request, response);
      }
      //异常处理,采用捕获,记录然后再抛出给高层的处理策略
      catch (ServletException ex) {
      failureCause = ex;
      throw ex;
      }
      catch (IOException ex) {
      failureCause = ex;
      throw ex;
      }
      catch (Throwable ex) {
      failureCause = ex;
      throw new NestedServletException("Request processing failed", ex);
      }
     ​
      finally {
      //如果失败,恢复上一次请求的上下文 
      ...
      省略
      ...
      }
     }
    

    可以看到FrameworkServlet的processRequest方法的职责是切换上下文(通过request设置上下文,遭遇异常会恢复原上下文),然后调用子类(DispatcherServlet)的doService方法

    1. 查看DispatcherServlet的doService方法(看看请求到底怎么被处理)

      protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
       ...
      
       //为request设置上下文及其他属性,方便其他方法需要时从request获取
       //...
       request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
       //省略
       //...
      ​
       try {
       //将请求进行分派处理
       doDispatch(request, response);
       }
       finally {
       //...
       //省略
       //...
       }
      }
      

      DispatcherServlet的doService方法给request装填更多数据(),然后调用doDispatch方法最终将请求进行分派处理

    2. 查看DispatcherServlet的doDispatch方法(看请求如何被分派并处理)

      protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
       HttpServletRequest processedRequest = request;
       //定义Handler执行链
       HandlerExecutionChain mappedHandler = null;
       boolean multipartRequestParsed = false;
      ​
       WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
      ​
       try {
       //定义ModelAndView
       ModelAndView mv = null;
       Exception dispatchException = null;
      ​
       try {
       //检查请求是否为文件上传请求,如果是使文件上传解析器可用
       processedRequest = checkMultipart(request);
       //如果是文件上传请求multipartRequestParsed值为true
       multipartRequestParsed = (processedRequest != request);
      ​
       //获取请求对应的handler执行链
       //getHandler方法在Handler映射器环节讲解
       mappedHandler = getHandler(processedRequest);
       if (mappedHandler == null || mappedHandler.getHandler() == null) {
       noHandlerFound(processedRequest, response);
       return;
       }
      ​
       // 为Handler执行链中的Handler构建适配器
       // mappedHandler.getHandler()获取的是Handler执行链的Handler——在Handler执行链环节讲解
       HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
      ​
       // request的类型
       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;
       }
       }
      ​
       if (!mappedHandler.applyPreHandle(processedRequest, response)) {
       return;
       }
      ​
       // 适配器执行Handler执行链,返回ModelAndView
       //适配器的handle方法在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) {
       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 {
       //...
       //省略
       //...
       }
      }
      

      可以看出doDispatch方法将请求分派给Handler执行链(调用Handler映射器得到Handler执行链、获取并调用Handler适配器执行Handler执行链),之后调用processDispatchResult对结果ModelAndView进行解析并装填响应结果

    3. 查看processDispatchResult方法(看看结果ModelAndView如何被装填进响应中)

      private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
       @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
       @Nullable Exception exception) throws Exception {
      ​
       boolean errorView = false;
      ​
       //若之前的流程存在异常,使用异常ModelAndView填装参数(此处重构手段是:使用特例对象取代null值)
       if (exception != null) {
       if (exception instanceof ModelAndViewDefiningException) {
       logger.debug("ModelAndViewDefiningException encountered", exception);
       mv = ((ModelAndViewDefiningException) exception).getModelAndView();
       }
       else {
       Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
       mv = processHandlerException(request, response, handler, exception);
       errorView = (mv != null);
       }
       }
      ​
       if (mv != null && !mv.wasCleared()) {
       // 通过ModelAndView渲染视图并填装至response
       render(mv, request, response);
       if (errorView) {
       WebUtils.clearErrorRequestAttributes(request);
       }
       }
       else {
       if (logger.isTraceEnabled()) {
       logger.trace("No view rendering, null ModelAndView returned.");
       }
       }
      ​
       if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
       // Concurrent handling started during a forward
       return;
       }
      ​
       if (mappedHandler != null) {
       mappedHandler.triggerAfterCompletion(request, response, null);
       }
      }
      ​
      protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
      
       // 获取请求的Locale对象并装入响应
       Locale locale =
       (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
       response.setLocale(locale);
      ​
       View view;
       String viewName = mv.getViewName();
       if (viewName != null) {
       // ModelAndView指定了视图名称
       // 解析指定名称的视图
       view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
       if (view == null) {
       throw new ServletException(略);
       }
       }
       else {
       // ModelAndView未指定视图名称
       // 从ModelAndView中取出View,此View由Handler(Controller)写入
       view = mv.getView();
       if (view == null) {
       throw new ServletException(略);
       }
       }
      ​
       // Delegate to the View object for rendering.
       if (logger.isTraceEnabled()) {
       logger.trace("Rendering view [" + view + "] ");
       }
       try {
       if (mv.getStatus() != null) {
       response.setStatus(mv.getStatus().value());
       }
       //将View装入响应中
       view.render(mv.getModelInternal(), request, response);
       }
       catch (Exception ex) {
       if (logger.isDebugEnabled()) {
       logger.debug("Error rendering view [" + view + "]", ex);
       }
       throw ex;
       }
      }
      ​
      protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
       Locale locale, HttpServletRequest request) throws Exception {
      ​
       if (this.viewResolvers != null) {
       //调用视图解析器生成指定名称的View
       for (ViewResolver viewResolver : this.viewResolvers) {
       //视图解析器的resolveViewName方法在视图解析器环节讲解
       View view = viewResolver.resolveViewName(viewName, locale);
       if (view != null) {
       return view;
       }
       }
       }
       return null;
      }
      

      processDispatchResult方法通过调用render方法渲染视图(View)并装填响应结果(异常情况也被封装成ModelAndView进行渲染),render方法则会根据ModelAndView的数据决定是使用指定视图名加载并填充视图(resolveViewName方法)还是直接从ModelAndView取出视图,然后用视图填装结果

    相关文章

      网友评论

          本文标题:SpringMVC源码解析——前置控制器DispatcherSe

          本文链接:https://www.haomeiwen.com/subject/qjlcjctx.html