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

分析下各个类的职责吧(顺便做分析目录):
HandlerMethod 封装方法定义相关的信息,如类,方法,参数等.
使用场景:HandlerMapping时会使用
InvocableHandlerMethod 添加参数准备,方法调用功能
使用场景:执行使用@ModelAttribute注解会使用
ServletInvocableHandlerMethod 添加返回值处理职责,ResponseStatus处理
使用场景:执行http相关方法会使用,比如调用处理执行
1. HandlerMethod
HandlerMethod其实可以简单理解为保持方法信息的pojo.
所以这边主要就是看下定义的属性:

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

大部分应该是看看注释就能理解了,我们解释下下面:
这边所有的熟悉都是final类型的,不可修改,所以如果出现修改需要new.
如果bean是string,是在createWithResolvedBean找容器获取实例的.
MethodParameter类封装了参数相关的信息.
提供获取返回值,判断是否void类型,还有读取注解
createWithResolvedBean逻辑其实很简单:
确认下如果bean是String类型的,那么从容器BeanFactory中获取,并使用private HandlerMethod(HandlerMethod handlerMethod, Object handler) new一个新的.

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

MethodParameter,可以根据method方法和parameterIndex(参数下标)唯一确定,其他属性都可以根据他们两获取.
由于反射中没有参数名的信息,而这边需要,所以Spring添加了一个参数名查找器ParameterNameDiscover.
这边返回值的类型是存储在parameters属性中的,下标用-1区分.
MethodParameter在HandlerMethod有两个内部类的子类.

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

2. InvocableHandlerMethod
习惯性的,看到Invocable,会想到Spring会不会定义一个接口来定义可调用概念,不过没有.
这边添加了2个职责:参数准备和方法执行.
参数准备委托HandlerMethodArgumentResolver进行具体的解析.解析的时候需要用到WebDataBinder,所以顺便带上.对参数解析器有兴趣可以移步这里
2.1 先来看看参数准备工作部分吧.
查找某个参数值的逻辑:
a, 先委托参数名查找器获取参数名
b,从外部提供的参数清单中查找值(竟然是根据类型判断的)
c,如果没有直接提供,使用参数解析器创建
d,如果还是没有获得,直接报错

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

2.2 方法执行
这边的逻辑其实很简单:
委托获取方法执行需要的参数
强制将方法变为可用
处理方法执行过程中的异常

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

3. ServletInvocableHandlerMethod
委托HandlerMethodReturnValueHandler添加返回值处理功能
添加@ResponseStatus注解支持.
这边使用的@ResponseStatus注解两个属性:value状态码,reason 写入response的说明文字
3.1 设置response status时的逻辑:
responseStatus没设置就返回
responseReason存在则进入error
把responseStatus设置到request,RedirectView需要使用

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

3.2 invokeAndHandle
委托父类执行请求
添加ResponseStatus支持
然后判断怎么样才是执行完毕,满足一下任意一个:
request的notModified为真,使用@ResponseStatus注解,mavContainer的requestHandled为真
委托HandlerMethodReturnValueHandler封装返回值

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

http://www.cnblogs.com/leftthen/p/5229204.html
SpringMVC源码解析 - HandlerMethod的更多相关文章
- SpringMVC源码解析- HandlerAdapter - ModelFactory(转)
ModelFactory主要是两个职责: 1. 初始化model 2. 处理器执行后将modle中相应参数设置到SessionAttributes中 我们来看看具体的处理逻辑(直接充当分析目录): 1 ...
- SpringMVC源码解析- HandlerAdapter - ModelFactory
ModelFactory主要是两个职责: 1. 初始化model 2. 处理器执行后将modle中相应参数设置到SessionAttributes中 我们来看看具体的处理逻辑(直接充当分析目录): 1 ...
- springMVC源码解析--ViewResolver视图解析器执行(三)
之前两篇博客springMVC源码分析--ViewResolver视图解析器(一)和springMVC源码解析--ViewResolverComposite视图解析器集合(二)中我们已经简单介绍了一些 ...
- 深入了解SpringMVC源码解析
Spring MVC源码解析 Spring MVC的使用原理其实是通过配置一个Servlet来接管所有的请求,所有的请求由这个Servlet来进行分发处理. 我们可以从web.xml里面看出这一点 & ...
- springMVC源码解析--ViewResolverComposite视图解析器集合(二)
上一篇博客springMVC源码分析--ViewResolver视图解析器(一)中我们介绍了一些springMVC提供的很多视图解析器ViewResolver,在开发的一套springMVC系统中是可 ...
- springMVC源码解析--HandlerMethodArgumentResolverComposite参数解析器集合(二)
上一篇博客springMVC源码分析--HandlerMethodArgumentResolver参数解析器(一)中我们已经介绍了参数解析相关的东西,并且也提到了HandlerMethodArgume ...
- springmvc源码解析MvcNamespaceHandler之<mvc:view-resolvers>
说在前面 本次主要介绍springmvc配置解析. springmvc配置解析 本次介绍MvcNamespaceHandler. 进入到这个方法org.springframework.web.serv ...
- SpringMVC源码解析
一:springmvc运行过程: 1. dispatcherServlet 通过 HandlerMapping 找到controller2. controller经过后台逻辑处理得到结果集modela ...
- SpringMVC源码解析- HandlerAdapter初始化
HandlerAdapter初始化时,主要是进行注解解析器初始化注册;返回值处理类初始化;全局注解@ControllerAdvice内容读取并缓存. 目录: 注解解析器初始化注册:@ModelAttr ...
随机推荐
- Think in java浏览一
Think in java作为java语言的圣经书籍之一,几乎成为每个java程序员必看的书籍,不看都不好意思说自己是java程序员,不过一般也不说自己认真看了,就说自己翻了翻.作为写安卓的,当然也要 ...
- c语言学习之基础知识点介绍(十六):文件操作
一.文件的分类 1.文本文件:打开之后能看得懂的文件 2.二进制文件:打开之后看不懂,类似乱码之类的文件(视频,音频打开之后,能看.听,是应为电脑中装有播放器,播放器中含有解码器). 二.操作文件的步 ...
- Ext.Net学习笔记11:Ext.Net GridPanel的用法
Ext.Net学习笔记11:Ext.Net GridPanel的用法 GridPanel是用来显示数据的表格,与ASP.NET中的GridView类似. GridPanel用法 直接看代码: < ...
- Java工具类:获取long型唯一ID
直接上代码: import java.text.SimpleDateFormat; import java.util.Date; /** * 获取long型唯一ID */ public class I ...
- 安卓模拟器"bluestacks"在电脑上的设置.(宽,高)
可以在手机上找到大量英语学习APP. 习惯使用电脑的朋友,可以安装模拟器来使用这些APP. bluestacks 是一款比较好的模拟器. 但其默认的宽,高,却无法在软件中修改. 找到一个比较好的教程来 ...
- 12_复杂查询01_Mapper代理实现
[工程截图] [代码实现] [user.java] package com.Higgin.Mybatis.po; import java.util.Date; public class User { ...
- 访问Access数据库(有多个数据库时 体现多态)
如果想编写单机版MIS.小型网站等对数据库性能要求不高的系统,又不想安装SQLServer,可以使用Access(MDAC),只要一个mdb文件就可以了.使用Access创建mdb文件,建表.OleD ...
- Eclipse相关
JDK版本更换相关: 启动eclipse会报错:根据报错信息后面提示的eclipse配置信息,我将配置中的c:/xx/javaw.exe给移除了.并在eclipse.ini中配置了-vm d:/Jav ...
- 安装centos 6.7
安装centos 6.7 系统 首先系统安装引导,本次系统安装是通过虚拟机安装,与真是环境十分接近 系统引导后第一步是询问是否检测硬盘,选择不检测(Skip) 然后等待系统引导进入安装界面,进入后我们 ...
- css(html)背景图优化合并
图片本身的优化: 图像质量要求和图像文件大小决定你用什么格式的图片,用较小的图片文件呈现较好的图像质量. 当图片色彩过于丰富且无透明要求时,建议采用jpg格式并保存为较高质量. 当图片色彩过于丰富又有 ...