spring-web InvocableHandlerMethod 源码分析
说明
- 本文基于 jdk 8, spring-framework 5.2.x 编写。
- @author JellyfishMIX - github / blog.jellyfishmix.com
- LICENSE GPL-2.0
类层次
HandlerMethod,处理器的方法的封装对象。HandlerMethod 只提供了处理器的方法的基本信息,不提供调用逻辑。
InvocableHandlerMethod,继承 HandlerMethod 类,可调用的 HandlerMethod 实现类。
ServletInvocableHandlerMethod#invokeAndHandle 方法
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
核心方法,处理请求。
- 执行调用处理请求,设置 responseStatus。
- 如果 returnValue 为空,设置 ModelAndViewContainer 为请求已处理然后 return,和 @ResponseStatus 注解相关。
- 设置 ModelAndViewContainer 为请求未处理,通过 HandlerMethodReturnValueHandlerComposite(HandlerMethodReturnValueHandler) 处理返回值。
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {@Nullableprivate HandlerMethodReturnValueHandlerComposite returnValueHandlers;public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 执行调用处理请求Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);// 设置 responseStatussetResponseStatus(webRequest);// 如果 returnValue 为空,设置 ModelAndViewContainer 为请求已处理然后 return,和 @ResponseStatus 注解相关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;}// 设置 ModelAndViewContainer 为请求未处理mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers != null, "No return value handlers");try {// 通过 HandlerMethodReturnValueHandlerComposite(HandlerMethodReturnValueHandler) 处理返回值this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);}throw ex;}}
}
ServletInvocableHandlerMethod#setResponseStatus 方法
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#setResponseStatus
调用 setResponseStatus(ServletWebRequest webRequest) 设置响应的状态码。
- 获得 status,和 @ResponseStatus 注解相关。
- 设置响应的状态码。有 reason,则设置 status + reason。无 reason,仅设置 status。
- RedirectView 相关。
private void setResponseStatus(ServletWebRequest webRequest) throws IOException {// 获得 status,和 @ResponseStatus 注解相关HttpStatus status = getResponseStatus();if (status == null) {return;}// 设置响应的状态码HttpServletResponse response = webRequest.getResponse();if (response != null) {String reason = getResponseStatusReason();// 有 reason,则设置 status + reasonif (StringUtils.hasText(reason)) {response.sendError(status.value(), reason);}else {// 无 reason,仅设置 statusresponse.setStatus(status.value());}}// To be picked up by RedirectView// RedirectView 相关webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);}
InvocableHandlerMethod
InvocableHandlerMethod#invokeForRequest 方法 – 执行调用处理请求
org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest
执行调用处理请求。
@Nullablepublic 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);}
InvocableHandlerMethod#getMethodArgumentValues 方法 – 解析参数
org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
- 获得方法的参数。
- 遍历方法参数,将参数解析成对应的类型。
- 获得当前遍历的 MethodParameter 对象,给它设置 parameterNameDiscoverer。
- 从 providedArgs 中获得参数。如果获得到,则进入下一个参数的解析,默认情况 providedArgs 不会传参。
- 判断 resolvers 是否支持当前的参数解析。
- 执行参数解析,解析成功后进入下一个参数的解析。
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 对象,给它设置 parameterNameDiscovererMethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);// 从 providedArgs 中获得参数。如果获得到,则进入下一个参数的解析,默认情况 providedArgs 不会传参。args[i] = findProvidedArgument(parameter, providedArgs);if (args[i] != null) {continue;}// 判断 resolvers 是否支持当前的参数解析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;}
InvocableHandlerMethod#doInvoke 方法
org.springframework.web.method.support.InvocableHandlerMethod#doInvoke
- 设置方法为可访问。
- 通过反射执行方法调用。BridgedMethod 基于泛型安全为了和 jdk 1.5 之前的字节码兼容,无需关注 BridgedMethod。
@Nullableprotected Object doInvoke(Object... args) throws Exception {// 设置方法为可访问ReflectionUtils.makeAccessible(getBridgedMethod());try {// 通过反射执行方法调用。BridgedMethod 基于泛型安全为了和 jdk 1.5 之前的字节码兼容,无需关注 BridgedMethodreturn 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);}}}
后言
- 本文可以看到很重要的两个组件 HandlerMethodArgumentResolver 用于获取请求参数,HandlerMethodReturnValueHandler 用于处理返回值,后续分析。
spring-web InvocableHandlerMethod 源码分析相关推荐
- Spring IOC 容器源码分析系列文章导读 1
1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...
- Spring框架—SpringBean源码分析
原文作者:Javadoop 原文地址:Spring IOC 容器源码分析 在继续往下之前,我们需要先了解 BeanDefinition.我们说 BeanFactory 是 Bean 容器,那么 Bea ...
- Spring IOC 容器源码分析
Spring IOC 容器源码分析 创建时间: 2017-11-15 00:00:00 [TOC] Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring ...
- Spring IOC 容器源码分析系列文章导读
1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...
- Spring IOC 容器源码分析 - 余下的初始化工作
1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bea ...
- Spring IOC 容器源码分析 - 填充属性到 bean 原始对象
1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...
- Spring IOC 容器源码分析 - 循环依赖的解决办法
1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...
- Spring IOC 容器源码分析 - 创建原始 bean 对象
1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...
- Spring IOC 容器源码分析 - 创建单例 bean 的过程
1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...
- Spring IOC 容器源码分析 - 获取单例 bean
1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...
最新文章
- 转帖-Linux 磁盘坏道检测和修复
- 防止SQL SERVER的事件探查器跟踪软件
- Java 包装类 自动装箱和拆箱
- linux查看双机热备运行,linux双机热备,故障切换!!!
- python配置文件模块_Python解析配置文件模块:ConfigPhaser
- 电子统计台账:中文标记月度流水账格式数据的转换,以及过滤模板的普遍适配性
- office2019安装D盘_office2016or2019安装在D盘的方法-可收藏备用
- matlab求方差、均方值和功率
- 【C语言】输出“*”菱形图案
- 设置html字体大小 js,js如何改变文章的字体大小
- eclipse折叠if语句块_「03」java中的方法以及控制语句
- js使用slice函数拆分数组
- 彬彬说,真正的喜欢一个人就会用心的去揣摩她,去知道她喜欢什么,什么性格...
- python报考软考哪个比较好_软考考什么比较好?
- 力扣解法汇总589- N 叉树的前序遍历
- 微信公众号开发之语音消息识别
- Bootstrap 模态框(Modal)的属性及方法
- 右键解决方案,没有“重定解决方案目标”这一菜单项
- 安装SQL Server以及SSMS
- 有了这款工具,定位线上问题事半功倍|云效工程师指北