SpringMVC源码解析 - HandlerMethod
2016-03-03 08:15
651 查看
HandlerMethod及子类主要用于封装方法调用相关信息,子类还提供调用,参数准备和返回值处理的职责.
分析下各个类的职责吧(顺便做分析目录):
HandlerMethod 封装方法定义相关的信息,如类,方法,参数等.
使用场景:HandlerMapping时会使用
InvocableHandlerMethod 添加参数准备,方法调用功能
使用场景:执行使用@ModelAttribute注解会使用
ServletInvocableHandlerMethod 添加返回值处理职责,ResponseStatus处理
使用场景:执行http相关方法会使用,比如调用处理执行
1. HandlerMethod
HandlerMethod其实可以简单理解为保持方法信息的pojo.
所以这边主要就是看下定义的属性:
大部分应该是看看注释就能理解了,我们解释下下面:
这边所有的熟悉都是final类型的,不可修改,所以如果出现修改需要new.
如果bean是string,是在createWithResolvedBean找容器获取实例的.
MethodParameter类封装了参数相关的信息.
提供获取返回值,判断是否void类型,还有读取注解
createWithResolvedBean逻辑其实很简单:
确认下如果bean是String类型的,那么从容器BeanFactory中获取,并使用private HandlerMethod(HandlerMethod handlerMethod, Object handler) new一个新的.
MethodParameter,可以根据method方法和parameterIndex(参数下标)唯一确定,其他属性都可以根据他们两获取.
由于反射中没有参数名的信息,而这边需要,所以Spring添加了一个参数名查找器ParameterNameDiscover.
这边返回值的类型是存储在parameters属性中的,下标用-1区分.
MethodParameter在HandlerMethod有两个内部类的子类.
2. InvocableHandlerMethod
习惯性的,看到Invocable,会想到Spring会不会定义一个接口来定义可调用概念,不过没有.
这边添加了2个职责:参数准备和方法执行.
参数准备委托HandlerMethodArgumentResolver进行具体的解析.解析的时候需要用到WebDataBinder,所以顺便带上.对参数解析器有兴趣可以移步这里
2.1 先来看看参数准备工作部分吧.
查找某个参数值的逻辑:
a, 先委托参数名查找器获取参数名
b,从外部提供的参数清单中查找值(竟然是根据类型判断的)
c,如果没有直接提供,使用参数解析器创建
d,如果还是没有获得,直接报错
2.2 方法执行
这边的逻辑其实很简单:
委托获取方法执行需要的参数
强制将方法变为可用
处理方法执行过程中的异常
3. ServletInvocableHandlerMethod
委托HandlerMethodReturnValueHandler添加返回值处理功能
添加@ResponseStatus注解支持.
这边使用的@ResponseStatus注解两个属性:value状态码,reason 写入response的说明文字
3.1 设置response status时的逻辑:
responseStatus没设置就返回
responseReason存在则进入error
把responseStatus设置到request,RedirectView需要使用
3.2 invokeAndHandle
委托父类执行请求
添加ResponseStatus支持
然后判断怎么样才是执行完毕,满足一下任意一个:
request的notModified为真,使用@ResponseStatus注解,mavContainer的requestHandled为真
委托HandlerMethodReturnValueHandler封装返回值
分析下各个类的职责吧(顺便做分析目录):
HandlerMethod 封装方法定义相关的信息,如类,方法,参数等.
使用场景:HandlerMapping时会使用
InvocableHandlerMethod 添加参数准备,方法调用功能
使用场景:执行使用@ModelAttribute注解会使用
ServletInvocableHandlerMethod 添加返回值处理职责,ResponseStatus处理
使用场景:执行http相关方法会使用,比如调用处理执行
1. HandlerMethod
HandlerMethod其实可以简单理解为保持方法信息的pojo.
所以这边主要就是看下定义的属性:
package org.springframework.web.method; public class HandlerMethod { /** 什么鬼,给子类提供logger,到现在为止源码中不多见 */ protected final Log logger = LogFactory.getLog(HandlerMethod.class); // 方法所在的类,如果是String类型,可以去容器中获取 private final Object bean; // 方法 private final Method method; // 类管理的容器 private final BeanFactory beanFactory; // 方法的参数 private final MethodParameter[] parameters; // 如果方法是bridged方法,则对应原始方法 private final Method bridgedMethod; // ... }
大部分应该是看看注释就能理解了,我们解释下下面:
这边所有的熟悉都是final类型的,不可修改,所以如果出现修改需要new.
如果bean是string,是在createWithResolvedBean找容器获取实例的.
MethodParameter类封装了参数相关的信息.
提供获取返回值,判断是否void类型,还有读取注解
createWithResolvedBean逻辑其实很简单:
确认下如果bean是String类型的,那么从容器BeanFactory中获取,并使用private HandlerMethod(HandlerMethod handlerMethod, Object handler) new一个新的.
// HandlerMethod public HandlerMethod createWithResolvedBean() { Object handler = this.bean; if (this.bean instanceof String) { String beanName = (String) this.bean; handler = this.beanFactory.getBean(beanName); } return new HandlerMethod(this, handler); }
MethodParameter,可以根据method方法和parameterIndex(参数下标)唯一确定,其他属性都可以根据他们两获取.
由于反射中没有参数名的信息,而这边需要,所以Spring添加了一个参数名查找器ParameterNameDiscover.
这边返回值的类型是存储在parameters属性中的,下标用-1区分.
MethodParameter在HandlerMethod有两个内部类的子类.
package org.springframework.core; public class MethodParameter { // 参数所在方法 private final Method method; // 参数的构造方法 private final Constructor constructor; // 参数下标 private final int parameterIndex; // 参数类型 private Class<?> parameterType; // Type类型的参数类型 private Type genericParameterType; // 参数使用的注解 private Annotation[] parameterAnnotations; // 参数名查找器 private ParameterNameDiscoverer parameterNameDiscoverer; // 参数名 private String parameterName; // 参数嵌套级别,如Map<String>中map为1,string为2 private int nestingLevel = 1; // 每层的下标 /** Map from Integer level to Integer type index */ Map<Integer, Integer> typeIndexesPerLevel; // 什么鬼?就一个构造方法用了,其他都没有使用 Map<TypeVariable, Type> typeVariableMap; // ... }
2. InvocableHandlerMethod
习惯性的,看到Invocable,会想到Spring会不会定义一个接口来定义可调用概念,不过没有.
这边添加了2个职责:参数准备和方法执行.
参数准备委托HandlerMethodArgumentResolver进行具体的解析.解析的时候需要用到WebDataBinder,所以顺便带上.对参数解析器有兴趣可以移步这里
2.1 先来看看参数准备工作部分吧.
查找某个参数值的逻辑:
a, 先委托参数名查找器获取参数名
b,从外部提供的参数清单中查找值(竟然是根据类型判断的)
c,如果没有直接提供,使用参数解析器创建
d,如果还是没有获得,直接报错
package org.springframework.web.method.support; public class InvocableHandlerMethod extends HandlerMethod { // 参数解析器 private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite(); // 参数解析器需要用到 private WebDataBinderFactory dataBinderFactory; // 参数名查找器 private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); private Object[] getMethodArgumentValues( NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(parameterNameDiscoverer); // 查找参数名 GenericTypeResolver.resolveParameterType(parameter, getBean().getClass()); // 从提供的参数值providedArgs中找值 args[i] = resolveProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } // 使用参数解析器解析 if (argumentResolvers.supportsParameter(parameter)) { try { args[i] = argumentResolvers.resolveArgument(parameter, mavContainer, request, dataBinderFactory); continue; } catch (Exception ex) { throw ex; } } // 参数获取不到是需要报错的 if (args[i] == null) { String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i); throw new IllegalStateException(msg); } } return args; } private Object resolveProvidedArgument(MethodParameter parameter, Object... providedArgs) { if (providedArgs == null) { return null; } for (Object providedArg : providedArgs) { // 竟然是根据类型判断的 if (parameter.getParameterType().isInstance(providedArg)) { return providedArg; } } return null; } // ... }
2.2 方法执行
这边的逻辑其实很简单:
委托获取方法执行需要的参数
强制将方法变为可用
处理方法执行过程中的异常
package org.springframework.web.method.support; public class InvocableHandlerMethod extends HandlerMethod { // ... public final Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); Object returnValue = invoke(args); return returnValue; } private Object invoke(Object... args) throws Exception { ReflectionUtils.makeAccessible(this.getBridgedMethod()); try { return getBridgedMethod().invoke(getBean(), args); } catch (IllegalArgumentException e) { String msg = getInvocationErrorMessage(e.getMessage(), args); throw new IllegalArgumentException(msg, e); } catch (InvocationTargetException e) { // Unwrap for HandlerExceptionResolvers ... Throwable targetException = e.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 { String msg = getInvocationErrorMessage("Failed to invoke controller method", args); throw new IllegalStateException(msg, targetException); } } } }
3. ServletInvocableHandlerMethod
委托HandlerMethodReturnValueHandler添加返回值处理功能
添加@ResponseStatus注解支持.
这边使用的@ResponseStatus注解两个属性:value状态码,reason 写入response的说明文字
3.1 设置response status时的逻辑:
responseStatus没设置就返回
responseReason存在则进入error
把responseStatus设置到request,RedirectView需要使用
// ServletInvocableHandlerMethod private void setResponseStatus(ServletWebRequest webRequest) throws IOException { if (this.responseStatus == null) { return; } if (StringUtils.hasText(this.responseReason)) { webRequest.getResponse().sendError(this.responseStatus.value(), this.responseReason); } else { webRequest.getResponse().setStatus(this.responseStatus.value()); } // to be picked up by the RedirectView webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, this.responseStatus); }
3.2 invokeAndHandle
委托父类执行请求
添加ResponseStatus支持
然后判断怎么样才是执行完毕,满足一下任意一个:
request的notModified为真,使用@ResponseStatus注解,mavContainer的requestHandled为真
委托HandlerMethodReturnValueHandler封装返回值
// ServletInvocableHandlerMethod public final void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); if (returnValue == null) { if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) { mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(this.responseReason)) { 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; } }
相关文章推荐
- Spring官网下载dist.zip的几种方法
- MyEclipse2015 编写js报 'Calculating completion proposals..' has encountered a problem.
- Java接口详解
- 深入探讨 Java 类加载器
- (java)Binary Tree Preorder Traversal
- (java)Missing Number
- 运行程序中出现的小问题
- Java图形界面
- java基础总结第一篇(JAVA简介)
- Spring MVC返回json数据的配置方式
- Java支持的数据类型有哪些?什么是自动拆装箱?
- spring RestTemplate用法详解
- spring异步线程的处理 @@Async介绍
- spring代码笔记
- java-tip-Collections.synchronized系列生成的容器
- Java集合基础 一 几个实现类之间的比较
- java 3S
- 关于java内存使用的相关问题(寄存器、栈、堆、静态存储、常理存储、非RAM存储)
- java 对象赋值是引用还是克隆
- 异常总结