享读SpringMVC源码4-感谢RequestMappingHandlerAdapter(下)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 享读SpringMVC源码4-感谢RequestMappingHandlerAdapter(下)

由于invokeHandlerMethod方法旁系逻辑比较多。我们抓住主线来讲

首先 会将handlerMethod封装成ServletInvocableHandlerMethod

那么,为啥要做此封装呢?

image.png


从其继承关系说起:

  • 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 我愿称你为最强


相关文章
|
6月前
|
Java 应用服务中间件 Spring
Spring5源码(50)-SpringMVC源码阅读环境搭建
Spring5源码(50)-SpringMVC源码阅读环境搭建
76 0
|
3月前
|
前端开发 Java Spring
SpringMVC种通过追踪源码查看是哪种类型的视图渲染器(一般流程方法)
这篇文章通过示例代码展示了如何在Spring MVC中编写和注册拦截器,以及如何在拦截器的不同阶段添加业务逻辑。
SpringMVC种通过追踪源码查看是哪种类型的视图渲染器(一般流程方法)
|
存储 前端开发 搜索推荐
(八)Spring源码解析:Spring MVC
(八)Spring源码解析:Spring MVC
95 1
|
6月前
|
Java 应用服务中间件 数据库连接
Spring5源码(51)-Servlet知识点回顾以及SpringMVC分析入口
Spring5源码(51)-Servlet知识点回顾以及SpringMVC分析入口
65 0
|
6月前
|
SQL JSON 前端开发
【源码免费下载】SpringBoot整合Spring+SpringMVC+MyBatisPlus案例:图书管理系统
【源码免费下载】SpringBoot整合Spring+SpringMVC+MyBatisPlus案例:图书管理系统
107 0
|
6月前
|
设计模式 前端开发 Java
[Spring ~源码] Spring的run方法以及SpringMVC执行流程
[Spring ~源码] Spring的run方法以及SpringMVC执行流程
|
前端开发 Java Spring
源码浅析SpringMVC请求的流转过程
Spring MVC框架使用了其”模型-视图-控制器”( Model-View-Controller )架构方式,用于开发灵活且松散耦合的 Web 应用程序。我们都使用过SpringMVC来处理信息,并渲染视图到Browser。但需要注意的是,在现在的架构中,大都采用了前后端分离的情况,而我们在使用SpringMVC的时候,只需要关注M(Model),C(Controller)这两个部分,而视图渲染的部分则交给了前端。
322 0
源码浅析SpringMVC请求的流转过程
|
Java Spring 容器
享读SpringMVC源码5-异常处理HandlerExceptionResolver(上)
享读SpringMVC源码5-异常处理HandlerExceptionResolver(上)
享读SpringMVC源码5-异常处理HandlerExceptionResolver(上)
|
JSON 缓存 Java
享读SpringMVC源码5-异常处理HandlerExceptionResolver(下)
享读SpringMVC源码5-异常处理HandlerExceptionResolver(下)
|
6月前
|
设计模式 前端开发 JavaScript
Spring MVC(一)【什么是Spring MVC】
Spring MVC(一)【什么是Spring MVC】