编程语言
首页 > 编程语言> > Spring-framework5.0.2 源码阅读笔记 - MVC调用阶段

Spring-framework5.0.2 源码阅读笔记 - MVC调用阶段

作者:互联网

Spring MVC 调用阶段

注意: 本片博客所展示的代码中,删除了对核心流程以外的代码,为了不影响观看,所以只保留了最核心的代码

spring 初始化时序图 (免费克隆)
Spring-framework 5.0.2 中文注释源码下载

闲聊

加班加了一个多月,忙着写项目,现在终于搞完了,开始投身到博客中。
从最开始的 MVC 初始化,到 IOC 初始化,再到 DIAOP,按照启动顺序来讲,应该 MVC 是最后一个调用的阶段。在本文中会通过 Sping MVC 整个浏览器请求和返回浏览器的执行顺序和逻辑进行解读。

1. MVC 入口

Spring MVC 其实是对 Servlet 做了一次封装,不过这个封装就变得特别的庞大,也为我们这些开发人员带来了便利,下面我们就从 Servlet 开始寻找入口,因为在第一篇Spring系列的博客中就阐明了 Spring MVC 初始化的过程是由 Servlet#init() 方法作为入口的。但是 Spring MVCServlet 做了封装,那么入口被定义在 FrameworkServlet#doService() 方法中,由DispatcherServlet 实现,下面看源码:

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

	// Keep a snapshot of the request attributes in case of an include,
	// to be able to restore the original attributes after the 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();
			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);
	}

	try {
		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);
			}
		}
	}
}

从上面这段代码中,很容易就提取到了核心的方法调用,那么就是 doDispatch(),在此方法内,并没有看到任何关于对请求 get、post… 方法的处理,那么我们进入该方法中:

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 {
			// 1.检查是否是文件上传的请求
			processedRequest = checkMultipart(request);
			multipartRequestParsed = (processedRequest != request);

			// Determine handler for the current request.
			// 2.取得处理当前请求的controller,这里也称为hanlder,处理器,
			// 	 第一个步骤的意义就在这里体现了.这里并不是直接返回controller,
			//	 而是返回的HandlerExecutionChain请求处理器链对象,
			//	 该对象封装了handler和interceptors.
			mappedHandler = getHandler(processedRequest);
			// 如果handler为空,则返回404
			if (mappedHandler == null) {
				noHandlerFound(processedRequest, response);
				return;
			}

			// Determine handler adapter for the current request.
			//3. 获取处理request的处理器适配器handler adapter
			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

			// Process last-modified header, if supported by the handler.
			// 处理 last-modified 请求头
			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.
			// 4.实际的处理器处理请求,返回结果视图对象
			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) {
	}
	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);
			}
		}
	}
}

别看这段代码很长,逻辑都很清晰,逻辑梳理:

  1. 如果请求是文件上传的,那么需要对请求方式进行标记,最后如果是文件上传的,需要关闭 IO 资源
  2. getHanler() 获取到处理的方法,也就是 Controllerurl 映射关系,那么这个处理器返回的结果是一个 HandlerExecutionChain 对象,这个对象内部封装了 handlerinterceptors ,这一步主要是处理获取一个执行链,如果获取不到的情况下,就返回 404
  3. getHandlerAdapter(mappedHandler.getHandler()),这一步主要是获取到一个处理器适配器,用来后续处理请求。
  4. handler() 通过上一步获取到的处理器适配器,来处理结请求,也就是我们常说 controller,处理完毕后会返回一个 ModelAndView 对象
  5. applyDefaultViewName(processedRequest, mv): 结果视图对象的处理
  6. processDispatchResult() 解析视图,把解析后的视图,返回给浏览器。
  7. finally 块主要是处理成功后回调的,例如最后一步处理了被标记为文件上传的请求,对资源的关闭等等

请求流程

2. Controller 入口

常用 spring boot 做开发的 WEB 攻城狮都知道 Controller 是接收请求的地方,最终根据 controller 类上的 mapping + 方法上的 mapping 定位到唯一的方法执行,在第一小节中,简单阐述了 doDispatch() 中各个部分的功能,那么第二小节将一步步找到 controller,执行后返回的逻辑。以及视图处理,返回结果

在第一小节中,讲述了 handler() 方法是去调用 controller 的,那么我们进入 handler() 内部,来到 HandlerAdapter#handle 处理器适配器中,那么这里定义的 handler 只是一个抽象,实现类有很多,应该进入哪一个,这里先进入到 AbstractHandlerMethodAdapter#handle 中,然后这里调用了他自己的抽象方法 handleInternal,顺藤摸瓜,进入 handleInternal() 来到一个熟悉的类 RequestMappingHandlerAdapter#handleInternal, 恍然大悟(@RequestMapping),逐渐清醒,那么源码如下:

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	ModelAndView mav;
	// 检查请求的方法和是否需要会话
	checkRequest(request);

	// Execute invokeHandlerMethod in synchronized block if required.
	if (this.synchronizeOnSession) {
		HttpSession session = request.getSession(false);
		if (session != null) {
			Object mutex = WebUtils.getSessionMutex(session);
			synchronized (mutex) {
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No HttpSession available -> no mutex necessary
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}
	}
	else {
		// No synchronization on session demanded at all...
		mav = invokeHandlerMethod(request, response, handlerMethod);
	}

	if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
		if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
			applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
		}
		else {
			prepareResponse(response);
		}
	}
	return mav;
}

进入该方法,第一句就做请求验证,最终根据不同判断条件进行执行 invokeHandlerMethod 方法,最终拿到 ModelAndView 对象,那么 invokeHandlerMethod 到底做了什么,下面源码:

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	try {
		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		if (this.argumentResolvers != null) {
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		if (this.returnValueHandlers != null) {
			invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		}
		invocableMethod.setDataBinderFactory(binderFactory);
		invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
		modelFactory.initModel(webRequest, mavContainer, invocableMethod);
		mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

		AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
		asyncWebRequest.setTimeout(this.asyncRequestTimeout);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.setTaskExecutor(this.taskExecutor);
		asyncManager.setAsyncWebRequest(asyncWebRequest);
		asyncManager.registerCallableInterceptors(this.callableInterceptors);
		asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

		if (asyncManager.hasConcurrentResult()) {
			Object result = asyncManager.getConcurrentResult();
			mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
			asyncManager.clearConcurrentResult();
			if (logger.isDebugEnabled()) {
				logger.debug("Found concurrent result value [" + result + "]");
			}
			invocableMethod = invocableMethod.wrapConcurrentResult(result);
		}

		// 完成 request 上的参数和方法参数的数据进行绑定,执行方法
		invocableMethod.invokeAndHandle(webRequest, mavContainer);
		if (asyncManager.isConcurrentHandlingStarted()) {
			return null;
		}

		return getModelAndView(mavContainer, modelFactory, webRequest);
	}
	finally {
		webRequest.requestCompleted();
	}
}

在以上的代码中,其实最主要的是 invocableMethod.invokeAndHandle(webRequest, mavContainer) 这句代码,主要是完成 request上的参数进行绑定,并且通过反射执行方法,进入 invokeAndHandle(),源码如下:

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
	
	// 执行方法,拿到返回结果
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	setResponseStatus(webRequest);

	if (returnValue == null) {
		if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
			mavContainer.setRequestHandled(true);
			return;
		}
	}
	else if (StringUtils.hasText(getResponseStatusReason())) {
		mavContainer.setRequestHandled(true);
		return;
	}

	mavContainer.setRequestHandled(false);
	Assert.state(this.returnValueHandlers != null, "No return value handlers");
	try {
		this.returnValueHandlers.handleReturnValue(
				returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}
	catch (Exception ex) {
		if (logger.isTraceEnabled()) {
			logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
		}
		throw ex;
	}
}

3.执行 controller

通过调用方法 invokeForRequest(webRequest, mavContainer, providedArgs) 执行目标方法,获取执行结果,进入方法:

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	// 获取方法的形参列表
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
				"' with arguments " + Arrays.toString(args));
	}
	// 执行method方法
	Object returnValue = doInvoke(args);
	if (logger.isTraceEnabled()) {
		logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
				"] returned [" + returnValue + "]");
	}
	return returnValue;
}
  1. 获取方法形参:
    getMethodArgumentValues(request, mavContainer, providedArgs),在 spring 框架中,获取参数有两种情况,第一种是通过 @RequestParam(name="quest") ,另一种则是通过读取字节码进行获取参数,采用的是 asm框架
  2. 执行被命中的方法, doInvoke(args),执行 controller 方法,进入方法查看源码:
protected Object doInvoke(Object... args) throws Exception {
	// 通过反射工具,标记该类强行访问,无论是private 的mapping 还是 public,都一律被访问了,
	ReflectionUtils.makeAccessible(getBridgedMethod());
	try {
		// 通过 method.invoke(),执行该方法,得到一个返回结果
		return getBridgedMethod().invoke(getBean(), args);
	}
	catch (IllegalArgumentException ex) {
		assertTargetBean(getBridgedMethod(), getBean(), args);
		String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
		throw new IllegalStateException(getInvocationErrorMessage(text, args), ex);
	}
	catch (InvocationTargetException ex) {
		// Unwrap for HandlerExceptionResolvers ...
		Throwable targetException = ex.getTargetException();
		if (targetException instanceof RuntimeException) {
			throw (RuntimeException) targetException;
		}
		else if (targetException instanceof Error) {
			throw (Error) targetException;
		}
		else if (targetException instanceof Exception) {
			throw (Exception) targetException;
		}
		else {
			String text = getInvocationErrorMessage("Failed to invoke handler method", args);
			throw new IllegalStateException(text, targetException);
		}
	}
}

通过该方法,可以知道,即使方法被修饰为 privateprotected 都将会被执行,因为调用了 ReflectionUtils.makeAccessible(getBridgedMethod()),然后通过反射调用目标方法,获取执行结果。

4.总结

感觉这篇文章在划水,后续抽空重新写过。
至此,Spring 源码块的内容笔记就做完了.

标签:handler,Spring,request,mavContainer,response,MVC,null,方法,源码
来源: https://blog.csdn.net/qq_38800175/article/details/109543473