首页 > 代码库 > SpringMVC源码解析 - HandlerMethod
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 // HandlerMethod2 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为220 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 = http://www.mamicode.com/invoke(args);"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 RedirectView15 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 = http://www.mamicode.com/invokeForRequest(webRequest, mavContainer, providedArgs);"Error handling return value", returnValue), ex);28 }29 throw ex;30 }31 }
http://www.cnblogs.com/leftthen/p/5229204.html
SpringMVC源码解析 - HandlerMethod