1、前言
在前面的《Spring MVC组件HandlerAdapter》和《Spring MVC组件HandlerMapping(二)》博客中,我们知道HandlerMethod类是一个基于方法的处理器,包括了该处理器对应的方法和实例Bean,并提供了一些访问方法参数、方法返回值、方法注解等方法。这一节,我们将分析Spring框架中是如何设计并使用HandlerMethod的。
2、类结构
HandlerMethod类图结构非常简单,一共就三个类,并且依次继承。其中,HandlerMethod类主要用于封装Handler和其中具体处理请求的Method方法,而InvocableHandlerMethod类中增加了对参数的处理逻辑并且定义了一个处理request请求的方法,ServletInvocableHandlerMethod类又在父类的基础上增加了对返回值得处理逻辑,下面我们依次分析三个类结构和实现逻辑。
3、HandlerMethod
属性字段:
//handler对应的bean实例或对应的beanName
private final Object bean;
//用于新建HandlerMethod时传入的Handler(也就是bean属性)是String的情况,这时需要使用beanFactory根据传入的String作为beanName获取到对应的bean
@Nullable
private final BeanFactory beanFactory;
//处理器的Class类型
private final Class<?> beanType;
//真正处理请求的方法,即处理器中真正处理请求的方法
private final Method method;
//如果method是bridge method则设置为其所对应的原有方法,否则直接设置为method
private final Method bridgedMethod;
//处理请求的方法的参数
private final MethodParameter[] parameters;
//响应状态,HttpStatus枚举类型
@Nullable
private HttpStatus responseStatus;
//当前响应状态的原因
@Nullable
private String responseStatusReason;
//解析后的HandlerMethod,
@Nullable
private HandlerMethod resolvedFromHandlerMethod;
//该method方法上的所有注解
@Nullable
private volatile List<Annotation[][]> interfaceParameterAnnotations;
//描述信息,格式:类名#方法名(参数1,参数2……)
private final String description;
构造函数:
在HandlerMethod类中,提供了五个构造函数,都是为了初始化上述提到的这些字段属性,这里以下面这个构造函数为例,进行分析:
public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
Assert.hasText(beanName, "Bean name is required");
Assert.notNull(beanFactory, "BeanFactory is required");
Assert.notNull(method, "Method is required");
//初始化bean属性,这里存储的是beanName,在其他构造函数中,可能存bean实例
this.bean = beanName;
//对应beanFactory,为了处理bean是beanName的情况,这里根据beanName,获取对应的beanType
this.beanFactory = beanFactory;
//获取beanType
Class<?> beanType = beanFactory.getType(beanName);
if (beanType == null) {
throw new IllegalStateException("Cannot resolve bean type for bean with name '" + beanName + "'");
}
this.beanType = ClassUtils.getUserClass(beanType);
//处理器中用于处理请求的方法
this.method = method;
//桥接方法
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
//初始化参数集合,这里通过initMethodParameters()方法实现,返回的是HandlerMethodParameter类型的数组
this.parameters = initMethodParameters();
//解析注解@ResponseStatus,然后初始化响应状态和原因两个字段属性
evaluateResponseStatus();
//初始化描述信息
this.description = initDescription(this.beanType, this.method);
}
initMethodParameters()方法实现了处理请求的方法的参数的解析,获取桥接方法的参数,然后创建MethodParameter数组,数组的实际元素是HandlerMethodParameter,这个类是一个内部类,继承至MethodParameter类,表示方法的参数。在内部类中,又增加了处理方法注解的逻辑。
private MethodParameter[] initMethodParameters() {
int count = this.bridgedMethod.getParameterCount();
MethodParameter[] result = new MethodParameter[count];
for (int i = 0; i < count; i++) {
result[i] = new HandlerMethodParameter(i);
}
return result;
}
在HandlerMethod类中,主要就是完成了这些属性的定义和初始化等工作,在它的子类InvocableHandlerMethod中,又增加了参数处理和请求处理方法调用的功能,后面我们将分析InvocableHandlerMethod类。
4、InvocableHandlerMethod类
InvocableHandlerMethod类继承至HandlerMethod类,然后增加了底层方法调用及其方法参数处理的相关能力。下面我们逐步分析:
属性字段:
//常量值,表示方法参数为0的情况
private static final Object[] EMPTY_ARGS = new Object[0];
//WebDataBinder工厂类,用于绑定参数到JavaBean对象
@Nullable
private WebDataBinderFactory dataBinderFactory;
//参数解析器
private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
//参数名称解析器
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
构造函数:
在这个类中的构造函数,主要是继承了父类中的方法,这里不再粘贴代码。
底层方法:
在该类中,定义了处理请求的方法invokeForRequest(),首先处理请求参数,然后调用doInvoke()方法实现请求的处理。
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//请求参数的处理,这里使用了参数解析器
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
//实际处理请求的方法
return doInvoke(args);
}
处理请求参数的方法在getMethodArgumentValues()方法中实现,代码如下:
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//通过父类方法,获取方法的参数集合
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {//如果为空,则返回size=0的数组
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
//遍历处理每一个参数,处理结果存到局部变量args中
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
//判断是否符合要求
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {//如果该参数明确,且不需要解析,就直接继续下一个参数的解析
continue;
}
//否则,判断解析器是否支持该参数
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
//解析参数,并放到args中指定的位置上
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
该方法主要实现父类属性bridgedMethod的调用,通过反射机制实现。只有两句核心代码,其他的都是异常处理。
@Nullable
protected Object doInvoke(Object... args) throws Exception {
//设置方法为可访问
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
//调用方法
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, args), ex);
}
catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.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 {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}
5、ServletInvocableHandlerMethod类
ServletInvocableHandlerMethod类继承至InvocableHandlerMethod类,在原有的基础上,又增加了返回值处理的能力,同时增加了注解@ResponseStatus的解析,并设置响应头的功能。为了增加处理返回值的能力,增加了返回值解析器属性returnValueHandlers,这里不在贴出源码。这里我们主要分析请求处理方法invokeAndHandle()的实现逻辑。
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
//调用父类InvocableHandlerMethod中的方法,获取返回值,该方法在前面已经分析过了。
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//设置响应信息,比较简单,不再具体分析
setResponseStatus(webRequest);
//当返回值为null时,进行处理,并返回
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
//当响应头不为空时,设置已处理状态,并返回
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
//设置为当前已处理状态为false,表示没有处理完成
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
//调用返回值解析器进行返回值的处理
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
到这里,我们已经把三个类分析完了,我们知道了三个类的作用。那它们是如何在Spring MVC中应用的呢?下面我们将结合前面分析的RequestMappingHandlerAdapter类,来分析ServletInvocableHandlerMethod在Spring MVC的应用。
6、应用
通过前面的学习,我们知道,DispatcherServlet类的doDispatch()方法是处理请求的核心方法,在该方法中,首先通过getHandlerAdapter()获取到了当前处理器对应的HandlerAdapter实例,然后再通过调用HandlerAdapter实例的handle()方法实现request的处理。当HandlerAdapter是RequestMappingHandlerAdapter类型的时候,最终会调用到该类的handleInternal()方法,该方法中又会调用invokeHandlerMethod()方法实现request请求的处理,在这个方法中,就开始使用到了ServletInvocableHandlerMethod类,完成了该类的创建和调用,代码如下:
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//省略……
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//省略配置
//调用
invocableMethod.invokeAndHandle(webRequest, mavContainer);
}
前面分析RequestMappingHandlerAdapter类的时候,分析了这个方法。这里想要说明的就是,这里调用invocableMethod.invokeAndHandle()方法,实际上就是调用了ServletInvocableHandlerMethod的invokeAndHandle()方法,然后在该方法中,又调用了父类InvocableHandlerMethod的invokeForRequest()方法,最后又通过调用doInvoke()方法,采用 反射的方式,实现了处理器中对应的request请求处理方法的调用。代码在前面已经分析过了,这里不再重复贴出代码。