网上关于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}
* 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
)的使用,将在后面的文章中介绍。