您的位置:首页 > 编程语言 > Java开发

SpringMVC源码解析 - HandlerMethod

2016-03-03 08:15 651 查看
HandlerMethod及子类主要用于封装方法调用相关信息,子类还提供调用,参数准备和返回值处理的职责.



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

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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: