springmvc源码-参数解析

该篇博客介绍了调用的部分逻辑,这篇主要介绍了实现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、对返回值进行判断处理,和参数解析的思想一致,这里用到的应该就是适配器模式

猜你喜欢

转载自blog.csdn.net/CPLASF_/article/details/110189503