当前位置:   article > 正文

SpringMVC执行流程(源码分析)_classutils.getuserclass

classutils.getuserclass


前言

网上关于SpringMVC执行流程的文章,大多数都是用纯文字来描述请求到处理以及响应的整个流程,给人的感觉就像是看了跟没看一样,更像是一道背诵的面试题,摒弃了很多细节(比如:拦截器在什么时候执行?对于响应的内容在哪里做统一处理?为什么一个请求进来能够准确的知道由谁处理?),我想这些才是我们需要关注的重点,由此我将跟随源码,解开这些谜题。


一、SpringMVC大体流程是什么顺序?

1.用户发送请求至前端控制器DispatcherServlet

2.DispatcherServlet收到请求调用处理器映射器HandlerMapping

3.处理器映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet

4.DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作。

5.执行处理器Handler(Controller,也叫页面控制器)。

6.Handler执行完成返回ModelAndView

7.HandlerAdapterHandler执行结果ModelAndView返回到DispatcherServlet

8.DispatcherServletModelAndView传给ViewReslover视图解析器。
9.ViewReslover解析后返回具体View

10.DispatcherServletView进行渲染视图(即将模型数据model填充至视图中)。

11.DispatcherServlet响应用户。

PS:是不是感觉好像看了,又感觉没看,这纯纯的就是一道面试背诵题。

二、请求URL如何与HandlerMethod对应?

PS:请打开编译器,跟着源码执行,不然很容易走丢。

1.AbstractHandlerMethodMapping初始化HandlerMethod

处理器映射器一般指的是RequestMappingHandlerMapping,它记录这每个请求应该由哪个HandlerMethod来处理(它更像是一个路由,控制着每个请求应该去向哪里),对于我们定义的Controller对应处理哪个Request URL的代码实现是在AbstractHandlerMethodMapping实现。RequestMappingHandlerMapping的大体结构如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/94274efe2a36427b9a0ddad4ae405b21.png

2.根据源码分析AbstractHandlerMethodMapping初始化HandlerMethod流程

如上图所看到的,AbstractHandlerMethodMapping实现了InitializingBeanInitializingBean接口为bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是实现该接口的类,在bean的属性初始化后都会执行该方法。那么AbstractHandlerMethodMapping初始化HandlerMethod的入口应该就是afterPropertesSet方法,打开编译器找到AbstractHandlerMethodMappingafterPropertiesSet方法。

	// Handler method detection
	/**
	 * Detects handler methods at initialization.
	 * @see #initHandlerMethods
	 */
	@Override
	public void afterPropertiesSet() {
	   //初始化HandlerMethod入口
		initHandlerMethods();
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

查看initHandlerMethods方法做了什么,getCandidateBeanNames获取当前应用上下文所有bean名称,挨个遍历bean名称,判断后调用processCandidateBean方法,那么我们重点查看processCandidateBean方法。

	/**
	 * Scan beans in the ApplicationContext, detect and register handler methods.
	 * @see #getCandidateBeanNames()
	 * @see #processCandidateBean
	 * @see #handlerMethodsInitialized
	 */
	protected void initHandlerMethods() {
		for (String beanName : getCandidateBeanNames()) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

obtainApplicationContext().getType根据Bean的名称获取Bean的类型,并且判断beanType是否为空,isHandler方法是在RequestMappingHandlerMapping中进行了重写,判断是否有Controller或者RequestMapping注解。如果有的话,继续走detectHandlerMethods方法。其实这一步已经将没有Controller或者RequestMapping注解的bean筛除(我记得ListableBeanFactory.getBeansWithAnnotation就可以筛选出带有指定注解的Bean,不知道为啥这里不这样做),不进行MethodHandler初始化。

	protected void processCandidateBean(String beanName) {
		Class<?> beanType = null;
		try {
			beanType = obtainApplicationContext().getType(beanName);
		}
		catch (Throwable ex) {
			// An unresolvable bean type, probably from a lazy bean - let's ignore it.
			if (logger.isTraceEnabled()) {
				logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
			}
		}
		//beanType不为空并且用Controller或者RequestMapping注解
		if (beanType != null && isHandler(beanType)) {
			detectHandlerMethods(beanName);
		}
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

RequestMappingHandlerMappingisHandler判断该beanType是否有注解Controller.class或者RequestMapping.class,这时候有人可以会说那么RestController怎么办?其实RestController底层也Controller+ResponseBody,那么这条件自然也成立。

	/**
	 * {@inheritDoc}
	 * <p>Expects a handler to have either a type-level @{@link Controller}
	 * annotation or a type-level @{@link RequestMapping} annotation.
	 */
	@Override
	protected boolean isHandler(Class<?> beanType) {
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

现在继续回到AbstractHandlerMethodMapping,继续将断点打到detectHandlerMethods方法,看看detectHandlerMethods做了什么。首先判断传入的handler是一个bean名称还是一个实例,如果是一个名称,那么通过依赖查找根据bean名称获取实例。ClassUtils.getUserClass获取用户自定义的Class(Controller),然后调用MethodIntrospectorselectMethods方法。

	/**
	 * Look for handler methods in the specified handler bean.
	 * @param handler either a bean name or an actual handler instance
	 * @see #getMappingForMethod
	 */
	protected void detectHandlerMethods(Object handler) {
	    //handler可以是一个bean的名字也可以是一个实例,如果是名字,那么将会通过依赖查找通过名字获取bean的实例。
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
		   //获取用户自定义的Class
			Class<?> userType = ClassUtils.getUserClass(handlerType);
			//重点难点在于这里
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

查看MethodIntrospector.selectMethods方法是如何实现的,传入两个参数,一个是我们刚刚获得的bean,另一个是MetadataLookup,这是整个MethodHandler初始化的重点和难点。MethodHandler与Request URL的对应关系就在这里实现。将断点打到for (Class<?> currentHandlerType : handlerTypes) { 这一行,遍历currentHandlerType调用ReflectionUtils.doWithMethods方法,进入ReflectionUtils.doWithMethods你会看到该方法采用反射机制获取到了该Bean的所声明的所有方法,通过For循环遍历每个Method,然后调用mc.doWith(method)方法。mc.doWith(method)方法所执行的代码我已在图中标记出来,我们重点看metadataLookup.inspect(specificMethod)这一行,它将调用getMappingForMethod(method, userType)方法,method就是我们通过反射获取的Method,userType就是bean Class,getMappingForMethod 的具体实现在RequestMappingHandlerMapping类中的getMappingForMethod方法。

	public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
		final Map<Method, T> methodMap = new LinkedHashMap<>();
		Set<Class<?>> handlerTypes = new LinkedHashSet<>();
		Class<?> specificHandlerType = null;

		if (!Proxy.isProxyClass(targetType)) {
			specificHandlerType = ClassUtils.getUserClass(targetType);
			handlerTypes.add(specificHandlerType);
		}
		handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));

		for (Class<?> currentHandlerType : handlerTypes) {
			final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);

			ReflectionUtils.doWithMethods(currentHandlerType, method -> {
			     //mc.doWith(method)方法的具体实现
				Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
				T result = metadataLookup.inspect(specificMethod);
				if (result != null) {
					Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
					if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
						methodMap.put(specificMethod, result);
					}
				}
			}, ReflectionUtils.USER_DECLARED_METHODS);
		}

		return methodMap;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

RequestMappingHandlerMapping中的getMappingForMethod方法代码实现如下所示,传入一个MethodBean Class,调用createRequestMappingInfo方法构建一个RequestMappingInfo 对象,createRequestMappingInfo其实就是获取该Method上的RequestMapping注解信息,然后将这些信息(比如:method、params、headers、consumes等)构建成一个RequestMappingInfo。如果构建的info不是空的,那么获取Bean Class上的RequestMapping信息然后构建成一个RequestMappingInfo。然后调用typeInfo.combine(info)typeInfoinfo 组成,此时就好比将Controller上的RequestMappingMethod上的RequestMapping进行组合,得到一个完整的URLRequestMethod。然后返回这个RequestMappingInfo ,此时一个Method就对应一个RequestMappingInfo,而RequestMappingInfo包含了这个Method的请求路径以及请求方法。然后再次回到MethodIntrospector.selectMethods方法中,我们继续讨论
MethodIntrospector.selectMethods方法。

	@Override
	@Nullable
	protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
				info = typeInfo.combine(info);
			}
			String prefix = getPathPrefix(handlerType);
			if (prefix != null) {
				info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
			}
		}
		return info;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

MethodIntrospector.selectMethods实现代码如下所示,此时获取到Method对应的RequestMappingInfo,并且通过Map将其存起来并返回。此时MethodIntrospector.selectMethods方法执行完毕,将回到AbstractHandlerMethodMappingdetectHandlerMethods方法中。似乎现在我们已经获取了每个Method并且与之对应的RequestMappingInfo,每个RequestMappingInfo中维护着每个Method的请求路径和请求方法。此时初始化工作流程已经接近尾声,现在到了调用methods.forEach((method, mapping) 这一步,调用registerHandlerMethod方法,传入Bean Class和对应的Method以及对应的RequestMappingInfo,registerHandlerMethod方法调用了mappingRegistry.register(mapping, handler, method);,我们看看mappingRegistry.register做了什么?

	public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
		final Map<Method, T> methodMap = new LinkedHashMap<>();
		Set<Class<?>> handlerTypes = new LinkedHashSet<>();
		Class<?> specificHandlerType = null;

		if (!Proxy.isProxyClass(targetType)) {
			specificHandlerType = ClassUtils.getUserClass(targetType);
			handlerTypes.add(specificHandlerType);
		}
		handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));

		for (Class<?> currentHandlerType : handlerTypes) {
			final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);

			ReflectionUtils.doWithMethods(currentHandlerType, method -> {
				Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
				//获取**RequestMappingInfo**
				T result = metadataLookup.inspect(specificMethod);
				if (result != null) {
					Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
					if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
					    //将method与RequestMappingInfo的关系存入Map中。
						methodMap.put(specificMethod, result);
					}
				}
			}, ReflectionUtils.USER_DECLARED_METHODS);
		}
		//返回Map,其保存着每个Method与**RequestMappingInfo**的关系。
		return methodMap;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

mappingRegistry.register实现代码如下,重点看我打断点的那行,创建一个HandlerMethod,然后将mapping(包含Method的请求路径和方法)和HandlerMethod关联起来放入一个Map中。此时请求URLHandlerMethod已经对应,那么当一个请求来时,RequestMappingHandlerMapping就知道该用哪个HandlerMethod去处理请求。
在这里插入图片描述

3.总结一下

对于初始化HandlerMethod与Request URL的关系,也就是处理器映射器(RequestMappingInfoHandlerMapping)根据请求url找到具体的处理器(HandlerMethod),总体来说难点就在于MethodIntrospector.selectMethods那一段代码的实现。需要打断点多熟悉一下流程。可能有朋友问,这个跟SpringMVC流程有什么关系?这个操作时为SpringMVC做铺垫,真正的流程是很复杂的,如果我们不去了解它如何实现一个请求进入SpringMVC,如何找到这个请求对应的方法实现。那么将毫无意义,接下来将正式的分析DispatcherServlet如何处理请求,以及拦截器何时生效?SpringMVC统一响应如何实现?

三、一个请求到SpringMVC的完整流程

1.DispatcherServlet如何接收用户请求?

DispatcherServlet这个类应该算得上是SpringMVC的核心类,一个请求的接收和处理以及响应,大部分都是在该类实现。DispatcherServlet继承了FrameworkServlet,而FrameworkServlet又继承了HttpServletBeanHttpServletBean则继承了HttpServlet,具体结构如下:
在这里插入图片描述
当用户发送一个请求到服务器后,首先会进入FrameworkServlet的doxxx方法,xxx是请求的方式(比如GET、POST、PUT等),本次我使用GET请求为例,可以看到FrameworkServlet重写了各种请求方式。
在这里插入图片描述
当一个GET请求到达server后,会调用FrameworkServlet的doGet方法,然后调用processRequest方法。
在这里插入图片描述
进入processRequest后主要看doService方法,传入HttpServletRequest和HttpServletRespones两个参数。
在这里插入图片描述
进入doService方法发现已经跳到了DispatcherServletdoSerivce方法。doService方法主要是为Request增加一些属性,重点看doDispatch方法的实现。
在这里插入图片描述
doDispatch才是整个DispatchServlet处理请求的核心,接下来将重点分析DispatchServlet如何拦截请求以及处理请求并返回结果。在这里插入图片描述

2.分析DispatchServlet.doDispatch方法如何处理请求

首先看checkMultipart方法,判断是否一个multipart request,如果是的话,需要转化成一个StandardMultipartHttpServletRequest,关于SpringMVC文件上传的使用和原理,在之前的文章有简单的概述。
在这里插入图片描述
重点是getHandler方法,该方法通过处理器映射器HandlerMapping找到可以处理该请求的HandlerExecutionChain(拦截器+HandlerMethod)。通过遍历handlerMappings获取对应的处理器链,默认有三个HandlerMapping
在这里插入图片描述
通过点击mapping.getHandler方法,进入到AbstractHandlerMappinggetHandler方法,然后调用getHandlerInternal方法,然后进入AbstractHandlerMethodMapping.getHandlerInternal方法。
在这里插入图片描述getUrlPathHelper().getLookupPathForRequest(request)方法用于获取请求路径,然后把当前request的请求路径通过使用request.setAttribute(LOOKUP_PATH, lookupPath)存起来,在后面会使用。可以通过debug查看当前request有哪些属性:
在这里插入图片描述
然后进入
lookupHandlerMethod(lookupPath, request)方法找寻当前请求路径对应的HandlerMethod
,对于HandlerMethod与请求路径的映射关系,我已经在第一节讲过了,这里不再讨论。进入lookupHandlerMethod方法,根据请求路径查RequestMappingInfo信息,
在这里插入图片描述
继续往下到addMatchingMappingsgetMatchingMapping方法将返回请求路径对应的一个RequestMappingInfothis.mappingRegistry.getMappings().get(mapping) 获取处理该请求的HandlerMethod
在这里插入图片描述
如果lookupHandlerMethod获取的HandlerMethod不为空,那么调用handlerMethod.createWithResolvedBean()创建Bean实例。
在这里插入图片描述在这里插入图片描述
执行完
getHandlerInternal
后,将返回一个HandlerMethod,该HandlerMethod包含处理该请求的方法以及该方法的实例对象。那么就可以通过反射执行该方法了(这个稍后代码里有实现)
在这里插入图片描述
继续往下走到getHandlerExecutionChain(handler, request),该方法是获取一个可执行的处理器链
在这里插入图片描述
进入getHandlerExecutionChain方法后,将创建一个HandlerExecutionChain对象,并将handlerHandlerInterceptor都放入到HandlerExecutionChain中。
在这里插入图片描述
到这里,以及构建完成一个处理器链HandlerExecutionChain,它包含该请求要经过的处理器和处理该请求的HandlerMethod,继续回到doDispatch方法,目前已经执行完了getHandler(processedRequest)方法并且获取到了执行器链,接下来会判断处理器链是否为空,如果是那么响应404
在这里插入图片描述
在这里插入图片描述
下一步则到了获取处理器适配器
HandlerAdapter
,通过getHandlerAdapter(mappedHandler.getHandler())方法将HandlerMethod传入去。
在这里插入图片描述
getHandlerAdapter通过遍历handlerAdapters找到支持该handler的适配器。默认适配器有4个,如下图所示:
在这里插入图片描述
adapter.supports(handler)方法用于判断该适配器是否支持该handler,这里看AbstractHandlerMethodAdater.supports方法
在这里插入图片描述
第一个条件是满足的,只需要看
supportsInternal
这个方法,可以看到RequestMappingHandlerAdaptersupportsInternal方法直接返回true。
在这里插入图片描述
所以获取的适配器是RequestMappingHandlerAdapter,这个可以用debug查看:
在这里插入图片描述
继续往下走来到了拦截器对请求的处理:
在这里插入图片描述
进入applyPreHandle可以看到通过遍历拦截器,然后调用拦截器的preHandle进行处理,如果preHandle不是返回true,那么applyPreHandle返回false,则直接结束此次请求处理。
在这里插入图片描述在这里插入图片描述
此时我们就知道拦截器的preHandle方法在什么时候生效了,是在生成处理器链HandlerExecutionChain已经并且获取HandlerAdapter后,会对请求进行拦截处理。

在遍历调用拦截器HandlerInterceptorpreHandle方法后,接下来到了RequestMappingHandlerAdaper对请求进行参数校验、封装。
在这里插入图片描述
对于请求参数的校验、封装以及调用对应的HandlerMethod(采用反射的方式)都在RequestMappingHandlerAdapterhandleInternal方法中进行处理。
在这里插入图片描述
checkRequest方法用于检查是否支持当前请求方式
在这里插入图片描述
重点看invokeHandlerMethod方法的实现,主要做了几件事情:
1.将requestresponse封装成ServletWebRequest对象
2.创建ServletInvocableHandlerMethod实例
3.ServletInvocableHandlerMethod实例注册参数解析器(HandlerMethodArgumentResolverComposite),用于解析请求参数
4.ServletInvocableHandlerMethod实例注册统一返回值解析器(HandlerMethodReturnValueHandlerComposite),用于统一处理请求返回数据。这个可以让我们自定义请求返回格式。具体应用将在后面文章中介绍。
5.创建视图容器, 用于封装视图,
6.调用ServletInvocableHandlerMethod.invokeAndHandle方法,进行参数解析和参数校验以及HandlerMethod反射执行对应请求方法。

/**
	 * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
	 * if view resolution is required.
	 * @since 4.2
	 * @see #createInvocableHandlerMethod(HandlerMethod)
	 */
	@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();
				LogFormatUtils.traceDebug(logger, traceOn -> {
					String formatted = LogFormatUtils.formatValue(result, !traceOn);
					return "Resume with async result [" + formatted + "]";
				});
				invocableMethod = invocableMethod.wrapConcurrentResult(result);
			}
			//调用ServletInvocableHandlerMethod.invokeAndHandle方法,进行参数解析和参数校验以及HandlerMethod反射执行对应请求方法
			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

调用ServletInvocableHandlerMethodinvokeAndHandle方法,这里主要是解析请求参数,校验请求参数以及调用HandlerMethod,利用反射的原理执行方法。具体实现在invokeForRequest方法中

	public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

        //invokeForRequest将进行请求参数校验和封装以及对应HandlerMethod调用
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		setResponseStatus(webRequest);

		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				disableContentCachingIfNecessary(webRequest);
				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(formatErrorForReturnValue(returnValue), ex);
			}
			throw ex;
		}
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

InvocableHandlerMethod.invokeForRequest方法主要是获取请求参数,校验封装请求参数,然后调用doInvoke反射执行对应的方法,并将方法返回值返回给上一层。

	@Nullable
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
        //获取方法所需的参数值
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Arguments: " + Arrays.toString(args));
		}
		//将方法所需参数值传入,反射调用具体的方法,将方法返回值返回。
		return doInvoke(args);
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

进入getMethodArgumentValues方法,该方法主要用于获取请求方法参数,校验方法参数值。getMethodParameters方法用于获取请求参数,this.resolvers.resolveArgument用于获取对应参数的值,具体实现是在AbstractNamedValueMethodArgumentResolver.resolveArgument中,感兴趣的可以打断点看一下实现过程。这里不再讲述。

	protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
       //获取请求方法参数
		MethodParameter[] parameters = getMethodParameters();
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}

        //创建请求参数对象
		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
			 //获取请求参数的值
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
			}
			catch (Exception ex) {
				// Leave stack trace for later, exception may actually be resolved and handled...
				if (logger.isDebugEnabled()) {
					String exMsg = ex.getMessage();
					if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
						logger.debug(formatArgumentError(parameter, exMsg));
					}
				}
				throw ex;
			}
		}
		//返回获取方法参数的所有值
		return args;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

通过getMethodArgumentValues获取请求参数值后,调用doInvoke方法,这个方法就很简单明了,直接通过反射调用执行方法,并返回执行方法的返回值。

	@Nullable
	protected Object doInvoke(Object... args) throws Exception {
		ReflectionUtils.makeAccessible(getBridgedMethod());
		try {
		   //反射调用方法并返回方法返回值
			return getBridgedMethod().invoke(getBean(), args);
		}
		catch (IllegalArgumentException ex) {
			assertTargetBean(getBridgedMethod(), getBean(), args);
			String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
			throw new IllegalStateException(formatInvokeError(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 {
				throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
			}
		}
	}

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

调用完成后回到invokeAndHandle方法,returnValue是业务方法响应的内容,调用HandlerMethodReturnValueHandlerComposite对返回内容进行同一处理,进入HandlerMethodReturnValueHandlerComposite.handleReturnValue方法,对响应内容进行处理。
在这里插入图片描述
handleReturnValue方法对调用selectHandler找到能够处理改返回类型的HandlerMethodReturnValueHandler,找不到对应能够处理的HandlerMethodReturnValueHandler时会抛出Unknown return value type:xxxx异常,如果找到能够处理的handler,那么将调用handleReturnValue方法处理请求。

/**
	 * Iterate over registered {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers} and invoke the one that supports it.
	 * @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
	 */
	@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
			//找到能够处理该返回类型的HandlerMethodReturnValueHandler 
		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) {
		//没有则抛出异常
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
		//有则调用其handleReturnValue方法
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

selectHandler方法遍历所有实现HandlerMethodReturnValueHandler接口的类,通过supportsReturnType判断该handler是否能处理该返回类型。SpiringMVC默认处理返回值的类是RequestResponseBodyMethodProcessor,因为它的supportsReturnType判断该返回类型的类上是否有ResponseBody注解。
在这里插入图片描述
在这里插入图片描述
此时RequestMappingHandlerAdaper处理请求已经处理完成,继续回到DispatcherServlet.doDispatch方法中,在处理完请求之后,将调用mappedHandler.applyPostHandle方法,该方法就是循环遍历调用拦截器HandlerInterceptorpostHandle方法。那么我们就知道了拦截器的postHandle方法是在处理完请求之后才调用的。
在这里插入图片描述
那么拦截器HandlerInterceptor的afterCompletion是在什么时候执行的呢?在调用完mappedHandler.applyPostHandle之后,紧接着调用processDispatchResult方法,然后在processDispatchResult方法最后调用了mappedHandler.triggerAfterCompletion方法,该方法循环遍历调用HandlerInterceptorafterCompletion方法。此时我们就知道拦截器HandlerInterceptorpreHandlepostHandleafterCompletion的调用顺序以及在什么时候调用了。到此SpringMVC流程大致结束。

总结

本章讲解的SpringMVC执行流程比较粗略,如果要搞清楚这个流程,需要自己多打断点理解。SpringMVC流程总体不是很复杂,基本上跟着源码Debug几次就明白了。我个人认为了解这个流程还是很重要的,并不是仅仅用于面试,而是要用于实际问题。当初我也是因为公司要求我对于不同的请求,要响应不同的数据格式。我才了解到HandlerMethodReturnValueHandler,出于好奇将整个SpiringMVC流程大体看了一遍。对于自定义响应格式(HandlerMethodReturnValueHandler)的使用,将在后面的文章中介绍。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号