今天很难,明天更难,后天也许更难
文章目录
回顾
上期说道,HandlerAdapter
存在的意义,就是充当request,reponse
与我们定义的各种形式handler
之间的参数适配,返回值适配。
当我们在享受着SpringMVC带来的多样化参数接收形式,以及简便的返回值操作时,殊不知,HandlerAdapter
在背后默默的为我们奉献着。
RequestMappingHandlerAdapter
适用于@RequestMapping
注解标注的Handler
,是处理我们定义的controller
接口最重要的HandlerAdapter
。基于他的重要地位,本文讲讲他是如何工作的。
1.初始化
先从其初始化开始下手
为了应对handler的各种样式的参数接收,在handlerApater
里,就需要大量的转换工具来类处理这些东西。
所以:初始化的过程中,就是要设置各种参数工具,返回值处理工具等等,这些工具可以是Spring
提供的或者开发人员自己定义的。
(1)
public RequestMappingHandlerAdapter() {
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316
this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4);
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(stringHttpMessageConverter);
this.messageConverters.add(new SourceHttpMessageConverter<Source>());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
public void afterPropertiesSet() {
(2)
initControllerAdviceCache();
(3)
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
1.1 设置HttpMessageConverter
HttpMessageConverter
负责将请求信息转换为一个对象(类型为 T),将对象(类型为 T)输出为响应信息
1.2 initControllerAdviceCache
这一步目的是对 @ControllerAdvice
标注的Bean的处理
这里简单介绍下@ControllerAdvice
注解意义,通过@ControllerAdvice
标注的类一般作为全局异常处理,全局数据绑定,全局数据预处理等。
例如:我们在一个Controller里抛出一个自定义异常时,通常都会有一个@ControllerAdvice
定义的类中处理此异常。
也就是说:@ControllerAdvice
标注类 后,他已经成为一种处理工具。自然的SpringMVC在初始化时会把这些 开发人员定义的处理工具找到 缓存起来。
源码中initControllerAdviceCache()
的逻辑是
- 找到该Advice Bean内所有的标注有
@ModelAttribute
但没标注@RequestMapping
的缓存到一起 - 找到该Advice Bean内所有的标注有
@InitBinder
的方法缓存在一起 - 实现了接口
RequestBodyAdvice/ResponseBodyAdvice
们,缓存到requestResponseBodyAdvice
集合的前面
1.3 设置默认参数解析工具
在getDefaultArgumentResolvers()
方法中设置了大量的SpringMVC已经准备好的参数解析工具。为handlerApater
处理做准备。
列如:我们常用@PathVariable注解。其实就是在此处设置的PathVariableMethodArgumentResolver
解析工具帮我们解析的。
@PostMapping("/post/{id}/{name}")
public void Post(@PathVariable(value = "id") String id,@PathVariable(value = "name") String name, HttpServletRequest request, HttpServletResponse response){
}
工具设置逻辑为:
- 先设置针对注解的参数解析工具
- 基于type类型的解析工具设置
- 设置用户定义的参数解析工具
- 最终解析工具,如果上边都没解析,此处设置解析工具最终处理
这些参数解析工具统一交给HandlerMethodArgumentResolverComposite
。从他的名字也可以看出解析工具混合器
,说白了就是解析工具集合
这里提一下自己的一点小理解: 我在很多框架中都见过类型
xxxComposite
混合器,本质上就是同一个东西的集合。
既然有了集合为啥还要多一层出来呢?
个人觉得混合器就好比工头,当我们跟工人打交道时,直接与工头交涉,比跟多个工人交涉方便的多。
总结为:分工明确,高内聚低耦合
1.4 设置返回值处理工具
提供对HandlerMethod返回值的支持,比如@ResponseBody
等同参数解析器逻辑差不多,
最终初始化HandlerMethodReturnValueHandlerComposite
混合器,承载返回值处理器集合
1.5 其他
除了上述初始化外,当然还有其他细节,感兴趣可以去阅读源码
2.handle处理
一切准备就绪后,下面就是调用了
再回头看下DispatcherServlet
中,HandlerAdapter
的调用起点
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
获取当前hangdler的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
走过拦截器的前置处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
通过适配器执行调用
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
过拦截器后置处理
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
RequestMappingHandlerAdapter
handle方法在父类中AbstractHandlerMethodAdapter
回调RequestMappingHandlerAdapter.handleInternal
方法
2.1 handleInternal
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) {
...
}
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;
}
这里只要是
- request请求方法的检查,
- 同一个Session下是否要串行(同步)
- 调用
invokeHandlerMethod
- 处理Cache-Control这个请求头
2.1 invokeHandlerMethod
执行handler方法,好戏正式登场
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);
//对handlerMethod进行封装。
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//设置参数处理器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
//设置返回值处理器
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);
}
//调用
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
由于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 我愿称你为最强
如果本文任何错误,请批评指教,不胜感激 !
如果文章哪些点不懂,可以联系我交流学习!
微信公众号:源码行动
享学源码,行动起来,来源码行动