欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

springmvc源码-参数解析

程序员文章站 2022-07-10 17:32:24
该篇博客介绍了调用的部分逻辑,这篇主要介绍了实现Controller接口和实现HttpRequestHandler接口这两种方式的调用,这两种方式比较简单,就是通过强转,然后调用目标方法即可,麻烦的是下面这种,通过@Controller注解声明的controller,在这篇博客中会仔细介绍对于通过@Controller、@RequestMapping()这种通过注解,声明handlerMethod的方式,会分为以下三步:1.对请求中的参数进行解析2.调用目标方法3.对返回参数进行解析对参数进行解析...

该篇博客介绍了调用的部分逻辑,这篇主要介绍了实现Controller接口和实现HttpRequestHandler接口这两种方式的调用,这两种方式比较简单,就是通过强转,然后调用目标方法即可,麻烦的是下面这种,通过@Controller注解声明的controller,在这篇博客中会仔细介绍

对于通过@Controller、@RequestMapping()这种通过注解,声明handlerMethod的方式,会分为以下三步:
1.对请求中的参数进行解析
2.调用目标方法
3.对返回参数进行解析

我们首先看下调用链,我们要说的代码,之前的解析逻辑就是下面这个调用链

org.springframework.web.servlet.DispatcherServlet#doDispatch
	org.springframework.web.servlet.HandlerAdapter#handle
		org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
			org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
				org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
					org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle


较为核心的方法,就在这里
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	/**
	 * 调用实际的目标方法
	 * 在这个方法中完成了两步操作
	 * 	1.对参数的解析
	 * 	2.对目标方法的调用
	 */
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	setResponseStatus(webRequest);

	/**
	 * 下面这里是对返回参数进行解析,举例:
	 * @ResponseBody 注解就是在下面这里被处理的
	 */
	if (returnValue == null) {
		if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
			mavContainer.setRequestHandled(true);
			return;
		}
	}
	else if (StringUtils.hasText(getResponseStatusReason())) {
		mavContainer.setRequestHandled(true);
		return;
	}

	mavContainer.setRequestHandled(false);
	Assert.state(this.returnValueHandlers != null, "No return value handlers");
	try {
		/**
		 * 处理返回结果
		 * 如果返回结果是对象,并且加了 @ResponseBody的注解,那么spring内部维护了一个 HttpMessageConverters
		 */
		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;
	}
}

一、对参数的处理

我上面有写过,上面的一个方法中,完成了三个步骤的处理,我们先来看对参数的解析


@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	/**
	 * 第一步:解析入参
	 */
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
				"' with arguments " + Arrays.toString(args));
	}
	/**
	 * 第二步调用对应的方法,获取返回值
	 */
	Object returnValue = doInvoke(args);
	if (logger.isTraceEnabled()) {
		logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
				"] returned [" + returnValue + "]");
	}
	return returnValue;
}

我们接着看具体的解析逻辑

下面这个方法,里面的逻辑就是这样的
1.获取到所有的入参
2.依次去解析每个入参,判断哪个参数解析器可以解析,然后进行解析

/**
 * Get the method argument values for the current request.
 * 从request中获取到当前方法的入参,也就是args
 */
private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable 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(this.parameterNameDiscoverer);
		/**
		 * 下面这行代码,看注释的意思是:从提供的参数列表中获取对应的值,但是从前面的调用链中会发现,入参的providedArgs是null
		 */
		args[i] = resolveProvidedArgument(parameter, providedArgs);
		if (args[i] != null) {
			continue;
		}
		/**
		 * 遍历所有的参数解析器,看哪个解析器可以解析,如果参数解析器返回true,就表示可以解析
		 * 如果我们要扩展参数解析器,就需要看下这里的逻辑
		 * 需要学习下这里的argumentResolvers是在哪里赋值的
		 */
		if (this.argumentResolvers.supportsParameter(parameter)) {
			try {
				/**
				 * 这里就是用parameter对应的解析器去解析该参数
				 */
				args[i] = this.argumentResolvers.resolveArgument(
						parameter, mavContainer, request, this.dataBinderFactory);
				continue;
			}
			catch (Exception ex) {
				if (logger.isDebugEnabled()) {
					logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
				}
				throw ex;
			}
		}
		if (args[i] == null) {
			throw new IllegalStateException("Could not resolve method parameter at index " +
					parameter.getParameterIndex() + " in " + parameter.getExecutable().toGenericString() +
					": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
		}
	}
	return args;
}

下面这个方法就是具体判断的逻辑,举个例子:
比如我自定义了一个注解@XXXX,然后自定义一个参数解析器,就是用来解析这个自定义的注解,那就只需要在supportsParameter方法中判断,参数是否添加了@XXXX注解就可以,这个后面单独会介绍


/**
 * 遍历所有的参数处理器,找到处理该parameter的处理器,然后存入到map集合中,
 * 第二次获取处理该参数的处理器时,就无须再次遍历所有的support方法,直接从map缓存中获取即可
 * @param parameter
 * @return
 */
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
	HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
	if (result == null) {
		for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
			if (logger.isTraceEnabled()) {
				logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
						parameter.getGenericParameterType() + "]");
			}
			/**
			 * 核心方法就是这里,上面会遍历所有的参数解析器,依次去调用对应的supports方法,判断是否可以处理parameter
			 * 如果可以,就返回true,然后会把对应的methodArgumentResolver和对应的parameter关联起来
			 */
			if (methodArgumentResolver.supportsParameter(parameter)) {
				result = methodArgumentResolver;
				this.argumentResolverCache.put(parameter, result);
				break;
			}
		}
	}
	return result;
}

下面这里是具体去处理的方法

@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
		NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
	/**
	 * 这个方法是实际解析参数的逻辑,首先会先获取到参数对应的解析器
	 */
	HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
	if (resolver == null) {
		throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
	}
	/**
	 * 这里调用的就是处理的方法
	 */
	return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

二、调用目标方法

调用这里就不仔细说了,里面的逻辑也没多少

Object returnValue = doInvoke(args);

doInvoke中会调用method.invoke(obj,args);方法去调用目标方法

三、对返回值的处理

对返回值的出来,核心的方法在这里:

handleReturnValue
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	/**
	 * 拿到返回值之后,判断当前返回的对象,需要由哪个handler来处理,如果是json的话,是requestResponseBodyMethodProcessor来处理的
	 */
	HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
	if (handler == null) {
		throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
	}
	/**
	 * 这里是调用returnValueHandler的处理方法
	 */
	handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

其实对返回值的处理逻辑,思想和参数解析是基本一样的,先判断解析类是否支持解析该返回值,如果支持解析该返回值的话,就调用对应的解析方法


@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
	boolean isAsyncValue = isAsyncReturnValue(value, returnType);
	for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
		if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
			continue;
		}
		if (handler.supportsReturnType(returnType)) {
			return handler;
		}
	}
	return null;
}

我们以@ResponseBody注解为例来看下,在得到返回值之后,就判断方法或者类上是否有添加@ResponseBody注解

/**
 * 处理@ResponseBody注解修饰的方法,在获取到方法的返回值之后,会通过该类对返回值进行处理
 * @param returnType the method return type to check
 * @return
 */
@Override
public boolean supportsReturnType(MethodParameter returnType) {
	return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
			returnType.hasMethodAnnotation(ResponseBody.class));
}

以上就是对方法参数的解析、方法调用、返回值解析的逻辑,其实也比较简单:
1、首先遍历所有的参数解析器,判断是否支持解析该参数,如果支持,就解析
2、通过method.invoke()调用目标方法
3、对返回值进行判断处理,和参数解析的思想一致,这里用到的应该就是适配器模式

本文地址:https://blog.csdn.net/CPLASF_/article/details/110189503