HandlerMethod及子类主要用于封装方法调用相关信息,子类还提供调用,参数准备和返回值处理的职责.

分析下各个类的职责吧(顺便做分析目录):

HandlerMethod 封装方法定义相关的信息,如类,方法,参数等.

  使用场景:HandlerMapping时会使用

InvocableHandlerMethod 添加参数准备,方法调用功能

  使用场景:执行使用@ModelAttribute注解会使用

ServletInvocableHandlerMethod 添加返回值处理职责,ResponseStatus处理

  使用场景:执行http相关方法会使用,比如调用处理执行

1. HandlerMethod

HandlerMethod其实可以简单理解为保持方法信息的pojo.

所以这边主要就是看下定义的属性:

  1. 1 package org.springframework.web.method;
  2. 2 public class HandlerMethod {
  3. 3 /** 什么鬼,给子类提供logger,到现在为止源码中不多见 */
  4. 4 protected final Log logger = LogFactory.getLog(HandlerMethod.class);
  5. 5 // 方法所在的类,如果是String类型,可以去容器中获取
  6. 6 private final Object bean;
  7. 7 // 方法
  8. 8 private final Method method;
  9. 9 // 类管理的容器
  10. 10 private final BeanFactory beanFactory;
  11. 11 // 方法的参数
  12. 12 private final MethodParameter[] parameters;
  13. 13 // 如果方法是bridged方法,则对应原始方法
  14. 14 private final Method bridgedMethod;
  15. 15 // ...
  16. 16 }

大部分应该是看看注释就能理解了,我们解释下下面:

  这边所有的熟悉都是final类型的,不可修改,所以如果出现修改需要new.

  如果bean是string,是在createWithResolvedBean找容器获取实例的.

  MethodParameter类封装了参数相关的信息.

  提供获取返回值,判断是否void类型,还有读取注解

createWithResolvedBean逻辑其实很简单:

确认下如果bean是String类型的,那么从容器BeanFactory中获取,并使用private HandlerMethod(HandlerMethod handlerMethod, Object handler) new一个新的.

  1. 1 // HandlerMethod
  2. 2 public HandlerMethod createWithResolvedBean() {
  3. 3 Object handler = this.bean;
  4. 4 if (this.bean instanceof String) {
  5. 5 String beanName = (String) this.bean;
  6. 6 handler = this.beanFactory.getBean(beanName);
  7. 7 }
  8. 8 return new HandlerMethod(this, handler);
  9. 9 }

MethodParameter,可以根据method方法和parameterIndex(参数下标)唯一确定,其他属性都可以根据他们两获取.

由于反射中没有参数名的信息,而这边需要,所以Spring添加了一个参数名查找器ParameterNameDiscover.

这边返回值的类型是存储在parameters属性中的,下标用-1区分.

MethodParameter在HandlerMethod有两个内部类的子类.

  1. 1 package org.springframework.core;
  2. 2 public class MethodParameter {
  3. 3 // 参数所在方法
  4. 4 private final Method method;
  5. 5 // 参数的构造方法
  6. 6 private final Constructor constructor;
  7. 7 // 参数下标
  8. 8 private final int parameterIndex;
  9. 9 // 参数类型
  10. 10 private Class<?> parameterType;
  11. 11 // Type类型的参数类型
  12. 12 private Type genericParameterType;
  13. 13 // 参数使用的注解
  14. 14 private Annotation[] parameterAnnotations;
  15. 15 // 参数名查找器
  16. 16 private ParameterNameDiscoverer parameterNameDiscoverer;
  17. 17 // 参数名
  18. 18 private String parameterName;
  19. 19 // 参数嵌套级别,如Map<String>中map为1,string为2
  20. 20 private int nestingLevel = 1;
  21. 21 // 每层的下标
  22. 22 /** Map from Integer level to Integer type index */
  23. 23 Map<Integer, Integer> typeIndexesPerLevel;
  24. 24 // 什么鬼?就一个构造方法用了,其他都没有使用
  25. 25 Map<TypeVariable, Type> typeVariableMap;
  26. 26 // ...
  27. 27 }

2. InvocableHandlerMethod

习惯性的,看到Invocable,会想到Spring会不会定义一个接口来定义可调用概念,不过没有.

这边添加了2个职责:参数准备和方法执行.

参数准备委托HandlerMethodArgumentResolver进行具体的解析.解析的时候需要用到WebDataBinder,所以顺便带上.对参数解析器有兴趣可以移步这里

2.1 先来看看参数准备工作部分吧.

查找某个参数值的逻辑:

  a, 先委托参数名查找器获取参数名

  b,从外部提供的参数清单中查找值(竟然是根据类型判断的)

  c,如果没有直接提供,使用参数解析器创建

  d,如果还是没有获得,直接报错

  1. 1 package org.springframework.web.method.support;
  2. 2
  3. 3 public class InvocableHandlerMethod extends HandlerMethod {
  4. 4 // 参数解析器
  5. 5 private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite();
  6. 6 // 参数解析器需要用到
  7. 7 private WebDataBinderFactory dataBinderFactory;
  8. 8 // 参数名查找器
  9. 9 private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
  10. 10
  11. 11 private Object[] getMethodArgumentValues(
  12. 12 NativeWebRequest request, ModelAndViewContainer mavContainer,
  13. 13 Object... providedArgs) throws Exception {
  14. 14
  15. 15 MethodParameter[] parameters = getMethodParameters();
  16. 16 Object[] args = new Object[parameters.length];
  17. 17 for (int i = 0; i < parameters.length; i++) {
  18. 18 MethodParameter parameter = parameters[i];
  19. 19 parameter.initParameterNameDiscovery(parameterNameDiscoverer);
  20. 20 // 查找参数名
  21. 21 GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
  22. 22 // 从提供的参数值providedArgs中找值
  23. 23 args[i] = resolveProvidedArgument(parameter, providedArgs);
  24. 24 if (args[i] != null) {
  25. 25 continue;
  26. 26 }
  27. 27 // 使用参数解析器解析
  28. 28 if (argumentResolvers.supportsParameter(parameter)) {
  29. 29 try {
  30. 30 args[i] = argumentResolvers.resolveArgument(parameter, mavContainer, request, dataBinderFactory);
  31. 31 continue;
  32. 32 } catch (Exception ex) {
  33. 33 throw ex;
  34. 34 }
  35. 35 }
  36. 36 // 参数获取不到是需要报错的
  37. 37 if (args[i] == null) {
  38. 38 String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
  39. 39 throw new IllegalStateException(msg);
  40. 40 }
  41. 41 }
  42. 42 return args;
  43. 43 }
  44. 44
  45. 45 private Object resolveProvidedArgument(MethodParameter parameter, Object... providedArgs) {
  46. 46 if (providedArgs == null) {
  47. 47 return null;
  48. 48 }
  49. 49 for (Object providedArg : providedArgs) {
  50. 50 // 竟然是根据类型判断的
  51. 51 if (parameter.getParameterType().isInstance(providedArg)) {
  52. 52 return providedArg;
  53. 53 }
  54. 54 }
  55. 55 return null;
  56. 56 }
  57. 57
  58. 58 // ...
  59. 59
  60. 60 }

2.2 方法执行

这边的逻辑其实很简单:

  委托获取方法执行需要的参数

  强制将方法变为可用

  处理方法执行过程中的异常

  1. 1 package org.springframework.web.method.support;
  2. 2
  3. 3 public class InvocableHandlerMethod extends HandlerMethod {
  4. 4 // ...
  5. 5 public final Object invokeForRequest(NativeWebRequest request,
  6. 6 ModelAndViewContainer mavContainer,
  7. 7 Object... providedArgs) throws Exception {
  8. 8 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
  9. 9 Object returnValue = invoke(args);
  10. 10 return returnValue;
  11. 11 }
  12. 12
  13. 13 private Object invoke(Object... args) throws Exception {
  14. 14 ReflectionUtils.makeAccessible(this.getBridgedMethod());
  15. 15 try {
  16. 16 return getBridgedMethod().invoke(getBean(), args);
  17. 17 }
  18. 18 catch (IllegalArgumentException e) {
  19. 19 String msg = getInvocationErrorMessage(e.getMessage(), args);
  20. 20 throw new IllegalArgumentException(msg, e);
  21. 21 }
  22. 22 catch (InvocationTargetException e) {
  23. 23 // Unwrap for HandlerExceptionResolvers ...
  24. 24 Throwable targetException = e.getTargetException();
  25. 25 if (targetException instanceof RuntimeException) {
  26. 26 throw (RuntimeException) targetException;
  27. 27 }
  28. 28 else if (targetException instanceof Error) {
  29. 29 throw (Error) targetException;
  30. 30 }
  31. 31 else if (targetException instanceof Exception) {
  32. 32 throw (Exception) targetException;
  33. 33 }
  34. 34 else {
  35. 35 String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
  36. 36 throw new IllegalStateException(msg, targetException);
  37. 37 }
  38. 38 }
  39. 39 }
  40. 40
  41. 41 }

3. ServletInvocableHandlerMethod

  委托HandlerMethodReturnValueHandler添加返回值处理功能

  添加@ResponseStatus注解支持.

这边使用的@ResponseStatus注解两个属性:value状态码,reason 写入response的说明文字

3.1 设置response status时的逻辑:

  responseStatus没设置就返回

  responseReason存在则进入error

  把responseStatus设置到request,RedirectView需要使用 

  1. 1 // ServletInvocableHandlerMethod
  2. 2 private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
  3. 3 if (this.responseStatus == null) {
  4. 4 return;
  5. 5 }
  6. 6
  7. 7 if (StringUtils.hasText(this.responseReason)) {
  8. 8 webRequest.getResponse().sendError(this.responseStatus.value(), this.responseReason);
  9. 9 }
  10. 10 else {
  11. 11 webRequest.getResponse().setStatus(this.responseStatus.value());
  12. 12 }
  13. 13
  14. 14 // to be picked up by the RedirectView
  15. 15 webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, this.responseStatus);
  16. 16 }

3.2 invokeAndHandle

  委托父类执行请求

  添加ResponseStatus支持

  然后判断怎么样才是执行完毕,满足一下任意一个:

    request的notModified为真,使用@ResponseStatus注解,mavContainer的requestHandled为真

  委托HandlerMethodReturnValueHandler封装返回值

  1. 1 // ServletInvocableHandlerMethod
  2. 2 public final void invokeAndHandle(ServletWebRequest webRequest,
  3. 3 ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
  4. 4
  5. 5 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
  6. 6
  7. 7 setResponseStatus(webRequest);
  8. 8
  9. 9 if (returnValue == null) {
  10. 10 if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
  11. 11 mavContainer.setRequestHandled(true);
  12. 12 return;
  13. 13 }
  14. 14 }
  15. 15 else if (StringUtils.hasText(this.responseReason)) {
  16. 16 mavContainer.setRequestHandled(true);
  17. 17 return;
  18. 18 }
  19. 19
  20. 20 mavContainer.setRequestHandled(false);
  21. 21
  22. 22 try {
  23. 23 this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
  24. 24 }
  25. 25 catch (Exception ex) {
  26. 26 if (logger.isTraceEnabled()) {
  27. 27 logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
  28. 28 }
  29. 29 throw ex;
  30. 30 }
  31. 31 }

http://www.cnblogs.com/leftthen/p/5229204.html

SpringMVC源码解析 - HandlerMethod的更多相关文章

  1. SpringMVC源码解析- HandlerAdapter - ModelFactory(转)

    ModelFactory主要是两个职责: 1. 初始化model 2. 处理器执行后将modle中相应参数设置到SessionAttributes中 我们来看看具体的处理逻辑(直接充当分析目录): 1 ...

  2. SpringMVC源码解析- HandlerAdapter - ModelFactory

    ModelFactory主要是两个职责: 1. 初始化model 2. 处理器执行后将modle中相应参数设置到SessionAttributes中 我们来看看具体的处理逻辑(直接充当分析目录): 1 ...

  3. springMVC源码解析--ViewResolver视图解析器执行(三)

    之前两篇博客springMVC源码分析--ViewResolver视图解析器(一)和springMVC源码解析--ViewResolverComposite视图解析器集合(二)中我们已经简单介绍了一些 ...

  4. 深入了解SpringMVC源码解析

    Spring MVC源码解析 Spring MVC的使用原理其实是通过配置一个Servlet来接管所有的请求,所有的请求由这个Servlet来进行分发处理. 我们可以从web.xml里面看出这一点 & ...

  5. springMVC源码解析--ViewResolverComposite视图解析器集合(二)

    上一篇博客springMVC源码分析--ViewResolver视图解析器(一)中我们介绍了一些springMVC提供的很多视图解析器ViewResolver,在开发的一套springMVC系统中是可 ...

  6. springMVC源码解析--HandlerMethodArgumentResolverComposite参数解析器集合(二)

    上一篇博客springMVC源码分析--HandlerMethodArgumentResolver参数解析器(一)中我们已经介绍了参数解析相关的东西,并且也提到了HandlerMethodArgume ...

  7. springmvc源码解析MvcNamespaceHandler之<mvc:view-resolvers>

    说在前面 本次主要介绍springmvc配置解析. springmvc配置解析 本次介绍MvcNamespaceHandler. 进入到这个方法org.springframework.web.serv ...

  8. SpringMVC源码解析

    一:springmvc运行过程: 1. dispatcherServlet 通过 HandlerMapping 找到controller2. controller经过后台逻辑处理得到结果集modela ...

  9. SpringMVC源码解析- HandlerAdapter初始化

    HandlerAdapter初始化时,主要是进行注解解析器初始化注册;返回值处理类初始化;全局注解@ControllerAdvice内容读取并缓存. 目录: 注解解析器初始化注册:@ModelAttr ...

随机推荐

  1. 20151225jquery学习笔记---折叠菜单UI

    折叠菜单(accordion),和选项卡一样也是一种在同一个页面上切换不同内容的功能UI.它和选项卡的使用几乎没有什么太大区别,只是显示的效果有所差异罢了.一. 使用 accordion使用 acco ...

  2. SDL实现按钮

    是的,按钮控件很常见,几乎在每一个Windows窗体内都能找到它的身影.SDL作为一套“一套开放源代码的跨平台多媒体开发库”,自然可以实现按钮.而按钮实现的重点,就是SDL的鼠标响应事件. SDL的鼠 ...

  3. HDU 3681 Prison Break(状态压缩dp + BFS)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3681 前些天花时间看到的题目,但写出不来,弱弱的放弃了.没想到现在学弟居然写出这种代码来,大吃一惊附加 ...

  4. boa服务器问题日志

    1. 某一次在登录boa服务器的时候,不知哪里的问题,无法登录「192.168.1.0-192.168.3.255」网段的设备,但是公司IP网段的机器都可以用.最终发现,问题出现在自己的PC添加了浏览 ...

  5. Codevs 5208 求乘方取模

    5208 求乘方取模 时间限制: 1 s 空间限制: 1000 KB 题目等级 : 未定级 题目描述 Description 给定非负整数A.B.M,求(A ^ B) mod M. 输入描述 Inpu ...

  6. IniParse解析类

    说明 iniParse这个类是一个解析ini文件的类,他的功能和Windows下GetPrivateProfileString的功能一样,可以很方便的保存读取配置. 当然他不是只有GetPrivate ...

  7. linux进程间通信--无名管道

    管道 只能用于具有亲缘关系的进程之间通信是一个半双工的通信模式, 具有固定的写读端和写端,管道可以看成一种特殊的文件,对它可以使用普通的read.write等操作 管道的创建: #include &l ...

  8. javascript 返回顶部

    <style> #linGoTopBtn {    POSITION: fixed; TEXT-ALIGN: center; LINE-HEIGHT: 30px; WIDTH: 30px; ...

  9. 【转】oracle PLSQL常用方法汇总

    原文:http://www.cnblogs.com/luluping/archive/2010/03/10/1682885.html 在SQLPLUS下,实现中-英字符集转换alter session ...

  10. javaScript中with的用法

    1 JavaScript中的with语句的作用是为逐级的对象访问提供命名空间式的速写方式, 也就是在指定的代码区域, 直接通过节点名称调用对象 初次接触到with用法,是这样一段代码: 1 2 3 4 ...