由于invokeHandlerMethod
方法旁系逻辑比较多。我们抓住主线来讲
首先 会将handlerMethod
封装成ServletInvocableHandlerMethod
那么,为啥要做此封装呢?
从其继承关系说起:
- 类
handlerMethod
: 是我们定义的接口方法的变体,只是数据的承载,不具有执行能力 - 类
InvocableHandlerMethod
: 赋予HandlerMethod可被执行能力。既然执行就涉及到参数的处理。其实吧,`InvocableHandlerMethod`就是为了参数的解析 - 类
ServletInvocableHandlerMethod
: 它是对InvocableHandlerMethod的扩展,它增加了返回值和响应状态码的处理 。本质就是为了返回值的处理
分工明确,很舒服。
调用ServletInvocableHandlerMethod.invokeAndHandle方法
开启两大工作
invocableMethod.invokeAndHandle(webRequest, mavContainer); 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); 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; } }
2.3 处理参数执行目标方法
上面说到参数的处理是放到InvocableHandlerMethod
完成的,invokeForRequest
方法逻辑就在InvocableHandlerMethod
invokeForRequest(webRequest, mavContainer, providedArgs); public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //解析参数 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); //反射调用目标方法 Object returnValue = doInvoke(args); //目标方法返回值返回 return returnValue; }
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { 1 MethodParameter[] parameters = getMethodParameters(); Object[] args = new Object[parameters.length]; 2 for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); args[i] = resolveProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } 3 if (this.argumentResolvers.supportsParameter(parameter)) { try { args[i] = this.argumentResolvers.resolveArgument( parameter, mavContainer, request, this.dataBinderFactory); continue; } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex); } throw ex; } } if (args[i] == null) { throw new IllegalStateException("Could not resolve method parameter at index " + parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() + ": " + getArgumentResolutionErrorMessage("No suitable resolver for", i)); } } return args; }
这里主要有3个点:
(1. 反射获取目标方法上的参数集合
(2. 遍历每个参数信息
(3. 对每个参数信息,都使用argumentResolvers
参数处理混合器查找一遍是否有符合当前参数的参数处理器,有就用找到的参数处理器,从request
中把参数值解析出来;没有就抛出异常
参数值解析完成后,就可以调用目标方法。获得返回值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
执行目标方法,到这里才算是真正的进入咱们开发人员写Controller里面了。
Object returnValue = doInvoke(args);
2.4 返回值处理
在上面调用完成目标方法后,获得了返回值,回到ServletInvocableHandlerMethod
进行返回值的处理
this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest );
handleReturnValue方法
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); } handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); }
返回值处理逻辑: 从返回值处理混合器中,找到支持目标方法返回的处理器,进行处理。
需要注意的是:
当我们使用@RequestBody
注解时,RequestResponseBodyMethodProcessor.handleReturnValue
处理返回值,会将返回值写入到输出流时,此时请求已经有了返回。
但是:HadnlerAdapter
的逻辑是没有走完的。
再回到RequestMappingHandlerAdapter.invokeHandlerMethod
方法的后半部分
invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } return getModelAndView(mavContainer, modelFactory, webRequest);
我们看到他依然会调用getModelAndView方法做一个结果统一性处理,返回一个ModelAndView
对象。
在DispatcherServlet
里会根据ModelAndView
对象的值进行处理。为null不渲染页面,不为空渲染页面。
至此:HandlerApater的执行原理大轮廓基本就是这样了。更多细节可以去阅读源码去体会
总结:
想想写个接口时,参数接收时如此方便,看完RequestMappingHandlerAdapter
后,不得不不感谢RequestMappingHandlerAdapter背后所做的工作
最后再次总结一下HandlerApater的主要工作:request 与 handler之间的参数映射,返回值处理,适配双方的不兼容问题
再次对Spring 框架体系膜拜,一个优秀的轮子,应该是分工明确,可扩展性好。
Spring 我愿称你为最强