SpringMVC运行流程分析之前置流程

简介: SpringMVC运行流程分析之前置流程

前面我们分析了SpringMVC源码分析之策略对象初始化,那么接下来我们看一下SpringMVC的执行流程。


SpringMVC的核心流程主要是由DispatcherServlet处理的,DispatcherServlet也是springMVC的灵魂处理器。其本质是一个servlet,那么我们就按照servlet的观点去看一下。


① XXXServlet的请求确定

e903370dcbe544449443cc62d40ca43c.png

如下是HttpServlet的service方法,其唯一作用就是对请求和响应对象进行了类型转换。

@Override
public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException
{
    HttpServletRequest  request;
    HttpServletResponse response;
    if (!(req instanceof HttpServletRequest &&
            res instanceof HttpServletResponse)) {
        throw new ServletException("non-HTTP request or response");
    }
    request = (HttpServletRequest) req;
    response = (HttpServletResponse) res;
    service(request, response);
}

而FrameworkServlet的service方法覆盖了父类的 protected void service(HttpServletRequest req, HttpServletResponse resp)方法对PATCH 请求类型进行了拦截处理(因为其父类HttpServlet并没有拦截PATCH类型的请求,将会直接resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);)。

@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
  HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
  if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
    processRequest(request, response);
  }
  else {
  // 这里会调用父类HttpServlet的service方法
    super.service(request, response);
  }
}

HttpServlet的service(HttpServletRequest req, HttpServletResponse resp)方法。

protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
{
    String method = req.getMethod();
    if (method.equals(METHOD_GET)) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
            // servlet doesn't support if-modified-since, no reason
            // to go through further expensive logic
            doGet(req, resp);
        } else {
            long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            if (ifModifiedSince < lastModified) {
                // If the servlet mod time is later, call doGet()
                // Round down to the nearest second for a proper compare
                // A ifModifiedSince of -1 will always be less
                maybeSetLastModified(resp, lastModified);
                doGet(req, resp);
            } else {
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }
    } else if (method.equals(METHOD_HEAD)) {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);
    } else if (method.equals(METHOD_POST)) {
        doPost(req, resp);
    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);
    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);
    } else if (method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);
    } else if (method.equals(METHOD_TRACE)) {
        doTrace(req,resp);
    } else {
        //
        // Note that this means NO servlet supports whatever
        // method was requested, anywhere on this server.
        //
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}

基本就是根据请求类型转向不同的方法,如下所示FrameworkServlet重写了一系列doXXX方法。也就是说HttpServlet的service方法判断请求后,会转发到FrameworkServlet的doXXX处理。


而重写后doXXX方法核心本质是转发给了processRequest(request, response);


② FrameworkServlet的processRequest

这里其实就是为当前线程绑定local、request、response等对象。

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;
// 获取当前LocaleContext
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
// 创建新的localeContext -new SimpleLocaleContext(request.getLocale())
    LocaleContext localeContext = buildLocaleContext(request);
// 获取RequestAttributes
    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
// 如果previousAttributes 为null或者其是ServletRequestAttributes实例,则返回 new ServletRequestAttributes(request, response);    
// 否则返回null
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
// 异步请求管理器
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    // 注册请求回调拦截器
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// 向LocaleContextHolder RequestContextHolder的ThreadLocal中
//放入 localeContext   requestAttributes
    initContextHolders(request, localeContext, requestAttributes);
    try {
      doService(request, response);
    }
    catch (ServletException | IOException ex) {
      failureCause = ex;
      throw ex;
    }
    catch (Throwable ex) {
      failureCause = ex;
      throw new NestedServletException("Request processing failed", ex);
    }
    finally {
    // 恢复LocaleContext和RequestAttributes为原先的值,默认为null
      resetContextHolders(request, previousLocaleContext, previousAttributes);
      if (requestAttributes != null) {
        requestAttributes.requestCompleted();
      }
      logResult(request, response, failureCause, asyncManager);
      // 发布事件
      publishRequestHandledEvent(request, response, startTime, failureCause);
    }
  }

获取的localeContext 如下:

获取的requestAttributes 如下:

③ FrameworkServlet的doService

如下所示其doService方法是个抽象方法由子类DispatcherServlet实现

protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
      throws Exception;

子类DispatcherServlet的doService方法。

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
  logRequest(request);
  // Keep a snapshot of the request attributes in case of an include,
  // to be able to restore the original attributes after the include.
// 在include请求存在的的情况下保留请求属性的快照,
//以便能够在include请求之后恢复原始属性。
  Map<String, Object> attributesSnapshot = null;
  if (WebUtils.isIncludeRequest(request)) {
    attributesSnapshot = new HashMap<>();
    Enumeration<?> attrNames = request.getAttributeNames();
    while (attrNames.hasMoreElements()) {
      String attrName = (String) attrNames.nextElement();
// DEFAULT_STRATEGIES_PREFIX = org.springframework.web.servlet      
      if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
        attributesSnapshot.put(attrName, request.getAttribute(attrName));
      }
    }
  }
  // Make framework objects available to handlers and view objects.
  // 设置一些核心属性对象
  request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
  request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
  request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
  request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
// 闪存属性处理 
  if (this.flashMapManager != null) {
    FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
    if (inputFlashMap != null) {
      request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
    }
    request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
    request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
  }
// 解析请求路径
  RequestPath requestPath = null;
  if (this.parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) {
    requestPath = ServletRequestPathUtils.parseAndCache(request);
  }
  try {
  // 核心分派方法
    doDispatch(request, response);
  }
  finally {
// 非异步请求,且attributesSnapshot != null,那么将attributesSnapshot 恢复为请求属性
    if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
      // Restore the original attribute snapshot, in case of an include.
      if (attributesSnapshot != null) {
        restoreAttributesAfterInclude(request, attributesSnapshot);
      }
    }
    if (requestPath != null) {
      //request.removeAttribute(PATH_ATTRIBUTE)   
      ServletRequestPathUtils.clearParsedRequestPath(request);
    }
  }
}

最终这里走到了核心入口方法doDispatch(request, response);,这也是很多资料中分析springmvc流程的第一步。


这里首先对include请求(也就是请求包含)进行了判断, 在include请求存在的的情况下保留请求属性的快照,以便能够在include请求之后恢复原始属性。。那么什么是include请求呢?如下方法所示,即请求属性里面包含javax.servlet.include.request_uri,换而言之即并非外部浏览器或其他客户端发出的顶级请求。

public static boolean isIncludeRequest(ServletRequest request) {
  return (request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE) != null);
}

然后设置了一些核心请求属性对象,比如WebApplicationContext、localeResolver、themeResolver等。

闪存属性处理,本质是针对重定向请求参数传递。




目录
相关文章
|
7月前
|
设计模式 前端开发 JavaScript
【SpringMVC】工作流程及入门案例
【SpringMVC】工作流程及入门案例
24 0
|
5月前
|
JSON 数据格式 容器
SpringMVC运行流程分析之核心流程
SpringMVC运行流程分析之核心流程
17 0
|
3月前
|
前端开发 Java 应用服务中间件
SpringMvc拦截器和手写模拟SpringMvc工作流程源码详解
MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分。 M: Model,模型层,指工程中的JavaBean,作用是处理数据。 JavaBean分为两类: 1.实体类Bean:专门存储业务数据的,如Student User等 2.业务处理Bean:指Service或Dao对象,专门用于处理业务逻辑和数据访问。
|
4月前
|
开发框架 前端开发 Java
SpringMVC之入门:springmcx工作流程,springmvc的入门,静态资源处理器
SpringMVC之入门:springmcx工作流程,springmvc的入门,静态资源处理器
28 0
|
7月前
|
JSON 前端开发 Java
【SpringMVC】工作流程&入门案例的使用
【SpringMVC】工作流程&入门案例的使用
23 0
|
XML 前端开发 Java
浅谈SpringMVC核心组件及执行流程(含源码解析)
浅谈SpringMVC核心组件及执行流程(含源码解析)
126 0
浅谈SpringMVC核心组件及执行流程(含源码解析)
|
设计模式 存储 前端开发
SpringMVC入门(工作原理、框架流程、小实例的创建)
SpringMVC入门(工作原理、框架流程、小实例的创建)
98 0
SpringMVC入门(工作原理、框架流程、小实例的创建)
|
设计模式 前端开发 Java
Spring MVC 前端控制器 (DispatcherServlet)处理流程
Spring MVC 请求处理流程 用户发起请求,到 DispatcherServlet; 然后到 HandlerMapping 返回处理器链(包含拦截器和具体处理的 Handler); 调用处理器链的适配器 HandlerAdapter 来处理; 执行具体的方法,比如 @RequestMapper修饰的逻辑处理方法; 返回结果的视图解析器; 最后进行视图解析和渲染返回结果给用户;
202 0
SpringMVC源码解析DispatcherServlet#doDispatch方法流程(下)
SpringMVC源码解析DispatcherServlet#doDispatch方法流程(下)
148 0
SpringMVC源码解析DispatcherServlet#doDispatch方法流程(下)
|
设计模式 开发框架 前端开发
SpringMVC源码解析DispatcherServlet#doDispatch方法流程(上)
SpringMVC源码解析DispatcherServlet#doDispatch方法流程(上)
220 0
SpringMVC源码解析DispatcherServlet#doDispatch方法流程(上)