一次GET请求在springmvc中是的处理流程

在这里插入图片描述
在这里插入图片描述

1:入口

我们知道根据servlet规范,servlet调用的入口方法是service,在springmvc中的这个servlet就是org.springframework.web.servlet.DispatcherServlet,但是我们看其源码会发现,并没有这个入口的service方法,实际上该方法是在其父类org.springframework.web.servlet.FrameworkServlet中,源码如下:
org.springframework.web.servlet.FrameworkServlet#service

@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 {
		super.service(request, response);
	}
}

因为本文分析的是GET请求的处理流程,因此会调用super.service(request, response),到这里也就找到了我们的入口程序了,好的,那么我们现在就正式开始吧!

2:FrameworkServlet#service

源码在org.springframework.web.servlet.FrameworkServlet,如下:

@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
    // <1>
	HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
	// <2>
	if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
		processRequest(request, response);
	}
	else { // <3>
		super.service(request, response);
	}
}

<1>处代码是通过org.springframework.http.HttpMethod获取请求方法对象,是一个枚举,通过map进行维护,用到了枚举提供的values方法获取所有枚举,源码如下:

public enum HttpMethod {

	GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
	private static final Map<String, HttpMethod> mappings = new HashMap<>(16);

	static {
		for (HttpMethod httpMethod : values()) {
			mappings.put(httpMethod.name(), httpMethod);
		}
	}

	@Nullable
	public static HttpMethod resolve(@Nullable String method) {
		return (method != null ? mappings.get(method) : null);
	}

	public boolean matches(String method) {
		return (this == resolve(method));
	}
}

<2>处代码是单独处理PATCH请求。<3>处是我们要分析的重点,会调用父类,即javax.servlet.HttpServletservice方法,开始这个过程。

3:HttpServlet#service

通过org.springframework.web.servlet.FrameworkServlet调用super.service(req, res)会执行到javax.servlet.http.HttpServlet#service(req, res)方法,源码如下:

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) {
              doGet(req, resp);
          } else {
              long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
              if (ifModifiedSince < lastModified) {
                  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 {
          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);
      }
  }

代码中除了正常的doXXX方法调用外,主要是一些缓存相关的逻辑判断,不影响我们的流程,可忽略,因为我们的是GET请求,则会调用doGet(req, res)方法,而该方法在org.springframework.web.servlet.FrameworkServlet中重写了,因此会调用到这里。

4:FrameworkServlet#doGet

源码如下:

protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
	processRequest(request, response);
}

直接委托给了processReqeust(req, res)方法,这里doGet其实只是起到了中转调用的作用。

5:FrameworkServlet#processRequest

该方法中其实也并没有什么核心逻辑,源码如下:

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 记录开始处理请求的开始时间,这样能够通过日志监控处理时长
		long startTime = System.currentTimeMillis();
		// 记录异常信息
		Throwable failureCause = null;
		try {
			// <1>
			doService(request, response);
		}
		catch (ServletException | IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			logResult(request, response, failureCause, asyncManager);
            // <1>
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

<1>处代码为重点,该方法是一个抽象的模板方法,具体实现在类org.springframework.web.servlet.DispatcherServlet中。<2>是在请求处理完毕后发布requesthandled事件,从而记录相关请求处理相关信息,如,处理时间,处理错误信息等。

6:FrameworkServlet#doService

直接看源码:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
	logRequest(request);
	if (WebUtils.isIncludeRequest(request)) {
		attributesSnapshot = new HashMap<>();
		Enumeration<?> attrNames = request.getAttributeNames();
		while (attrNames.hasMoreElements()) {
			String attrName = (String) attrNames.nextElement();
			if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
				attributesSnapshot.put(attrName, request.getAttribute(attrName));
			}
		}
	}
	
	// <1>
	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());
	// <2>
	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);
	}	
	try {
		// <3>
		doDispatch(request, response);
	}
	finally {
		if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Restore the original attribute snapshot, in case of an include.
			if (attributesSnapshot != null) {
				restoreAttributesAfterInclude(request, attributesSnapshot);
			}
		}
	}
}

<1>处代码是将spring相关的一些对象设置到request中,如Servlet WebApplicationContext,本地化组件等。<2>处代码是通过重定向组件来处理重定向数据,其中代码request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));中将Map数据放到一个不可修改的map中,因为只是为了传递数据,这样做可以有效的防止由于误操作导致一些bug的发生,是一种很好的编程方式,值得学习。<3>处代码是是真正处理请求的地方。

7:DispatcherServlet#doDispatch

可以配合这里来进行学习,看源码:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	// <1>
	HttpServletRequest processedRequest = request;
	// <2>
	HandlerExecutionChain mappedHandler = null;
	// <3>
	boolean multipartRequestParsed = false;

	WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

	try {
		// <4>
		ModelAndView mv = null;
		// <5>
		Exception dispatchException = null;

		try {
			// <6>
			processedRequest = checkMultipart(request);
			multipartRequestParsed = (processedRequest != request);

			// <7>
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null) {
				noHandlerFound(processedRequest, response);
				return;
			}

			// <8>
			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;
				}
			}
			// <9>
			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}
            // <10>
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}
            // <11>
			applyDefaultViewName(processedRequest, mv);
			// <12>
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		}
		catch (Exception ex) {
		    // <13>
			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);
		}
		// <14>
		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 {
			// <15>
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}
}

<1>,<2>,<3>,<4>,<5>代码都是记录相关变量,<6>处检查是否为文件上传请求,如果是则将HttpServletRequest转换为专门处理文件上的类MultipartHttpServletRequest,<7>通过请求获取对应的业务handler,获取方法也比较简单,直接循环所有的HandlerMapping,找到封装有对应请求handler的handlermapping,从而获取handler,源码如下:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	if (this.handlerMappings != null) {
		for (HandlerMapping mapping : this.handlerMappings) {
			HandlerExecutionChain handler = mapping.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
	}
	return null;
}

返回的对象是org.springframework.web.servlet.HandlerExecutionChain对象,封装了目标handler+HandlerInterceptor,简单看下HandlerInterceptor源码:

public interface HandlerInterceptor {
	// 在调用目标handler之前调用该方法
	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return true;
	}

	// 在调用目标handler之后调用该方法
	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}

	// 在目标handler执行完毕(渲染结束)之后调用该方法(一般通过事件触发方式调用)
	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}

}

一共3个方法preHandle,postHandle,afterCompletion,执行时机已经在注释中说明,可以看下。<8>处代码是获取支持handler的HandlerAdapter(HandlerAdapter的作用是屏蔽不同handler调用方式的差异性),获取方法和获取Handler一样也是通过循环的方式,源码如下:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	if (this.handlerAdapters != null) {
		for (HandlerAdapter adapter : this.handlerAdapters) {
			if (adapter.supports(handler)) {
				return adapter;
			}
		}
	}
	throw new ServletException("No adapter for handler [" + handler +
			"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

其中方法support(handler)用来判断当前handler适配器是否支持目标handler,如果是支持则是我们需要的HandlerAdapter,直接返回即可。<9>为执行HandlerInterceptor的preHandle方法,注意该方法要返回true,否则后续逻辑都不会执行了,使用的过程中一定要小心。<10>处代码为调用handler(一般就是我们在controller中标注了@RequestMapping的方法),返回就是ModelAndView,<11>处代码为在没有视图的情况下,应用默认的视图名称。<12>处代码为调用HandlerInterceptor的postHandle方法。<13>为如果是发生了异常则记录异常对象到全局变量。<14>为处理请求结果,正常的,异常的都会处理。<15>处代码判断如果是文件上传请求,则处理产生的临时文件。接下来看下processDispatchResult是如何处理最终的视图等信息的。

8:DispatcherServlet#processDispatchResult

源码:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {
    // <1>
	boolean errorView = false;

	// <2>
	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);
		}
	}

	// <3>
	if (mv != null && !mv.wasCleared()) {
		// 具体渲染页面的方法
		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;
	}

	// <4>
	if (mappedHandler != null) {
		mappedHandler.triggerAfterCompletion(request, response, null);
	}
}

<1>处为是否是异常视图的标记,如果是获取了异常视图则该值会被修改为true,<2>处代码为如果是发生了异常,则获取异常视图,一般为通过processHandlerException方法使用HandleExceptionViewResolver组件生成ModelAndView,方法源码如下:

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
			@Nullable Object handler, Exception ex) throws Exception {

	// Success and error responses may use different content types
	request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

	// Check registered HandlerExceptionResolvers...
	ModelAndView exMv = null;
	if (this.handlerExceptionResolvers != null) {
		for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
			exMv = resolver.resolveException(request, response, handler, ex);
			if (exMv != null) {
				break;
			}
		}
	}
	if (exMv != null) {
		if (exMv.isEmpty()) {
			request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
			return null;
		}
		// We might still need view name translation for a plain error model...
		if (!exMv.hasView()) {
			String defaultViewName = getDefaultViewName(request);
			if (defaultViewName != null) {
				exMv.setViewName(defaultViewName);
			}
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Using resolved error view: " + exMv, ex);
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Using resolved error view: " + exMv);
		}
		WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
		return exMv;
	}

	throw ex;
}

做法也比较简单,就是通过循环HandlerExceptionResolver组件数组handlerExceptionResolvers找到错误视图ModelAndView。另外注意代码WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());是设置相关的错误信息到request中,<3>处代码为渲染页面,后续详细分析,渲染完毕后如果是错误页面则调用WebUtils.clearErrorRequestAttributes(request);清理相关错误信息。<4>处代码为页面渲染完毕后触发HandlerInterceptor的afterCompletion方法。接下来看页面渲染方法render。

9:DispatcherServlet#render

源码:

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
	// <1>
	Locale locale =
			(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
	response.setLocale(locale);

	View view;
	String viewName = mv.getViewName();
	//<2>
	if (viewName != null) {
		// We need to resolve the view name.
		view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
		if (view == null) {
			throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
					"' in servlet with name '" + getServletName() + "'");
		}
	}
	else {
		// <3>		
		view = mv.getView();
		if (view == null) {
			throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
					"View object in servlet with name '" + getServletName() + "'");
		}
	}

	// Delegate to the View object for rendering.
	if (logger.isTraceEnabled()) {
		logger.trace("Rendering view [" + view + "] ");
	}
	try {
	   // <4>
		if (mv.getStatus() != null) {
			response.setStatus(mv.getStatus().value());
		}
		// <5>
		view.render(mv.getModelInternal(), request, response);
	}
	catch (Exception ex) {
		if (logger.isDebugEnabled()) {
			logger.debug("Error rendering view [" + view + "]", ex);
		}
		throw ex;
	}
}

<1>处代码获取本地话信息,并设置到响应中,<2>处为在有viewName情况下解析出View,<3>处为没有viewName情况下直接通过ModelAndView获取View,<4>处设置响应状态码,<5>使用ViewResolver组件完成页面最终的渲染。

写在后面

参考文章列表

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值