赞
踩
网上关于SpringMVC执行流程的文章,大多数都是用纯文字来描述请求到处理以及响应的整个流程,给人的感觉就像是看了跟没看一样,更像是一道背诵的面试题,摒弃了很多细节(比如:拦截器在什么时候执行?对于响应的内容在哪里做统一处理?为什么一个请求进来能够准确的知道由谁处理?),我想这些才是我们需要关注的重点,由此我将跟随源码,解开这些谜题。
1.用户发送请求至前端控制器DispatcherServlet。
2.DispatcherServlet收到请求调用处理器映射器HandlerMapping。
3.处理器映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet。
4.DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作。
5.执行处理器Handler(Controller,也叫页面控制器)。
6.Handler执行完成返回ModelAndView。
7.HandlerAdapter将Handler执行结果ModelAndView返回到DispatcherServlet。
8.DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9.ViewReslover解析后返回具体View。
10.DispatcherServlet对View进行渲染视图(即将模型数据model填充至视图中)。
11.DispatcherServlet响应用户。
PS:是不是感觉好像看了,又感觉没看,这纯纯的就是一道面试背诵题。
PS:请打开编译器,跟着源码执行,不然很容易走丢。
处理器映射器一般指的是RequestMappingHandlerMapping,它记录这每个请求应该由哪个HandlerMethod来处理(它更像是一个路由,控制着每个请求应该去向哪里
),对于我们定义的Controller对应处理哪个Request URL的代码实现是在AbstractHandlerMethodMapping实现。RequestMappingHandlerMapping的大体结构如下:
如上图所看到的,AbstractHandlerMethodMapping实现了InitializingBean,InitializingBean接口为bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是实现该接口的类,在bean的属性初始化后都会执行该方法。那么AbstractHandlerMethodMapping初始化HandlerMethod的入口应该就是afterPropertesSet方法,打开编译器找到AbstractHandlerMethodMapping的afterPropertiesSet方法。
// Handler method detection
/**
* Detects handler methods at initialization.
* @see #initHandlerMethods
*/
@Override
public void afterPropertiesSet() {
//初始化HandlerMethod入口
initHandlerMethods();
}
查看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());
}
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); } }
RequestMappingHandlerMapping中isHandler判断该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));
}
现在继续回到AbstractHandlerMethodMapping,继续将断点打到detectHandlerMethods方法,看看detectHandlerMethods做了什么。首先判断传入的handler是一个bean名称还是一个实例,如果是一个名称,那么通过依赖查找根据bean名称获取实例。ClassUtils.getUserClass获取用户自定义的Class(Controller),然后调用MethodIntrospector的selectMethods方法。
/** * 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); }); } }
查看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; }
RequestMappingHandlerMapping中的getMappingForMethod方法代码实现如下所示,传入一个Method和Bean Class,调用createRequestMappingInfo方法构建一个RequestMappingInfo 对象,createRequestMappingInfo其实就是获取该Method上的RequestMapping注解信息,然后将这些信息(比如:method、params、headers、consumes等)构建成一个RequestMappingInfo。如果构建的info不是空的,那么获取Bean Class上的RequestMapping信息然后构建成一个RequestMappingInfo。然后调用typeInfo.combine(info)将typeInfo与info 组成,此时就好比将Controller上的RequestMapping与Method上的RequestMapping进行组合,得到一个完整的URL和RequestMethod。然后返回这个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; }
MethodIntrospector.selectMethods实现代码如下所示,此时获取到Method对应的RequestMappingInfo,并且通过Map将其存起来并返回。此时MethodIntrospector.selectMethods方法执行完毕,将回到AbstractHandlerMethodMapping的detectHandlerMethods方法中。似乎现在我们已经获取了每个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; }
mappingRegistry.register实现代码如下,重点看我打断点的那行,创建一个HandlerMethod,然后将mapping(包含Method的请求路径和方法)和HandlerMethod关联起来放入一个Map中。此时请求URL与HandlerMethod已经对应,那么当一个请求来时,RequestMappingHandlerMapping就知道该用哪个HandlerMethod去处理请求。
对于初始化HandlerMethod与Request URL的关系,也就是处理器映射器(RequestMappingInfoHandlerMapping)根据请求url找到具体的处理器(HandlerMethod),总体来说难点就在于MethodIntrospector.selectMethods那一段代码的实现。需要打断点多熟悉一下流程。可能有朋友问,这个跟SpringMVC流程有什么关系?这个操作时为SpringMVC做铺垫,真正的流程是很复杂的,如果我们不去了解它如何实现一个请求进入SpringMVC,如何找到这个请求对应的方法实现。那么将毫无意义,接下来将正式的分析DispatcherServlet如何处理请求,以及拦截器何时生效?SpringMVC统一响应如何实现?
DispatcherServlet这个类应该算得上是SpringMVC的核心类,一个请求的接收和处理以及响应,大部分都是在该类实现。DispatcherServlet继承了FrameworkServlet,而FrameworkServlet又继承了HttpServletBean,HttpServletBean则继承了HttpServlet,具体结构如下:
当用户发送一个请求到服务器后,首先会进入FrameworkServlet的doxxx方法,xxx是请求的方式(比如GET、POST、PUT等),本次我使用GET请求为例,可以看到FrameworkServlet重写了各种请求方式。
当一个GET请求到达server后,会调用FrameworkServlet的doGet方法,然后调用processRequest方法。
进入processRequest后主要看doService方法,传入HttpServletRequest和HttpServletRespones两个参数。
进入doService方法发现已经跳到了DispatcherServlet的doSerivce方法。doService方法主要是为Request增加一些属性,重点看doDispatch方法的实现。
doDispatch才是整个DispatchServlet处理请求的核心,接下来将重点分析DispatchServlet如何拦截请求以及处理请求并返回结果。
首先看checkMultipart方法,判断是否一个multipart request,如果是的话,需要转化成一个StandardMultipartHttpServletRequest,关于SpringMVC文件上传的使用和原理,在之前的文章有简单的概述。
重点是getHandler方法,该方法通过处理器映射器HandlerMapping找到可以处理该请求的HandlerExecutionChain(拦截器+HandlerMethod)。通过遍历handlerMappings获取对应的处理器链,默认有三个HandlerMapping。
通过点击mapping.getHandler方法,进入到AbstractHandlerMapping的getHandler方法,然后调用getHandlerInternal方法,然后进入AbstractHandlerMethodMapping.getHandlerInternal方法。
getUrlPathHelper().getLookupPathForRequest(request)方法用于获取请求路径,然后把当前request的请求路径通过使用request.setAttribute(LOOKUP_PATH, lookupPath)存起来,在后面会使用。可以通过debug查看当前request有哪些属性:
然后进入lookupHandlerMethod(lookupPath, request)方法找寻当前请求路径对应的HandlerMethod,对于HandlerMethod与请求路径的映射关系,我已经在第一节讲过了,这里不再讨论。进入lookupHandlerMethod方法,根据请求路径查RequestMappingInfo信息,
继续往下到addMatchingMappings,getMatchingMapping方法将返回请求路径对应的一个RequestMappingInfo,this.mappingRegistry.getMappings().get(mapping) 获取处理该请求的HandlerMethod。
如果lookupHandlerMethod获取的HandlerMethod不为空,那么调用handlerMethod.createWithResolvedBean()创建Bean实例。
执行完getHandlerInternal后,将返回一个HandlerMethod,该HandlerMethod包含处理该请求的方法以及该方法的实例对象。那么就可以通过反射执行该方法了(这个稍后代码里有实现)
继续往下走到getHandlerExecutionChain(handler, request),该方法是获取一个可执行的处理器链
进入getHandlerExecutionChain方法后,将创建一个HandlerExecutionChain对象,并将handler和HandlerInterceptor都放入到HandlerExecutionChain中。
到这里,以及构建完成一个处理器链HandlerExecutionChain,它包含该请求要经过的处理器和处理该请求的HandlerMethod,继续回到doDispatch方法,目前已经执行完了getHandler(processedRequest)方法并且获取到了执行器链,接下来会判断处理器链是否为空,如果是那么响应404
下一步则到了获取处理器适配器HandlerAdapter,通过getHandlerAdapter(mappedHandler.getHandler())方法将HandlerMethod传入去。
getHandlerAdapter通过遍历handlerAdapters找到支持该handler的适配器。默认适配器有4个,如下图所示:
adapter.supports(handler)方法用于判断该适配器是否支持该handler,这里看AbstractHandlerMethodAdater.supports方法
第一个条件是满足的,只需要看supportsInternal这个方法,可以看到RequestMappingHandlerAdapter的supportsInternal方法直接返回true。
所以获取的适配器是RequestMappingHandlerAdapter,这个可以用debug查看:
继续往下走来到了拦截器对请求的处理:
进入applyPreHandle可以看到通过遍历拦截器,然后调用拦截器的preHandle进行处理,如果preHandle不是返回true,那么applyPreHandle返回false,则直接结束此次请求处理。
此时我们就知道拦截器的preHandle方法在什么时候生效了,是在生成处理器链HandlerExecutionChain已经并且获取HandlerAdapter后,会对请求进行拦截处理。
在遍历调用拦截器HandlerInterceptor的preHandle方法后,接下来到了RequestMappingHandlerAdaper对请求进行参数校验、封装。
对于请求参数的校验、封装以及调用对应的HandlerMethod(采用反射的方式
)都在RequestMappingHandlerAdapter的handleInternal方法中进行处理。
checkRequest方法用于检查是否支持当前请求方式
重点看invokeHandlerMethod方法的实现,主要做了几件事情:
1.将request和response封装成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(); } }
调用ServletInvocableHandlerMethod的invokeAndHandle方法,这里主要是解析请求参数,校验请求参数以及调用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; } }
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);
}
进入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; }
通过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); } } } }
调用完成后回到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); }
selectHandler方法遍历所有实现HandlerMethodReturnValueHandler接口的类,通过supportsReturnType判断该handler是否能处理该返回类型。SpiringMVC默认处理返回值的类是RequestResponseBodyMethodProcessor,因为它的supportsReturnType判断该返回类型的类上是否有ResponseBody注解。
此时RequestMappingHandlerAdaper处理请求已经处理完成,继续回到DispatcherServlet.doDispatch方法中,在处理完请求之后,将调用mappedHandler.applyPostHandle方法,该方法就是循环遍历调用拦截器HandlerInterceptor的postHandle方法。那么我们就知道了拦截器的postHandle方法是在处理完请求之后才调用的。
那么拦截器HandlerInterceptor的afterCompletion是在什么时候执行的呢?在调用完mappedHandler.applyPostHandle之后,紧接着调用processDispatchResult方法,然后在processDispatchResult方法最后调用了mappedHandler.triggerAfterCompletion方法,该方法循环遍历调用HandlerInterceptor的afterCompletion方法。此时我们就知道拦截器HandlerInterceptor的preHandle、postHandle和afterCompletion的调用顺序以及在什么时候调用了。到此SpringMVC流程大致结束。
本章讲解的SpringMVC执行流程比较粗略,如果要搞清楚这个流程,需要自己多打断点理解。SpringMVC流程总体不是很复杂,基本上跟着源码Debug几次就明白了。我个人认为了解这个流程还是很重要的,并不是仅仅用于面试,而是要用于实际问题。当初我也是因为公司要求我对于不同的请求,要响应不同的数据格式。我才了解到HandlerMethodReturnValueHandler,出于好奇将整个SpiringMVC流程大体看了一遍。对于自定义响应格式(HandlerMethodReturnValueHandler
)的使用,将在后面的文章中介绍。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。