Spring MVC学习笔记之Spring MVC组件HandlerMethod

导读:本篇文章讲解 Spring MVC学习笔记之Spring MVC组件HandlerMethod,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

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请求处理方法的调用。代码在前面已经分析过了,这里不再重复贴出代码。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/68795.html

(0)

相关推荐

半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!