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、对返回值进行判断处理,和参数解析的思想一致,这里用到的应该就是适配器模式
本文地址:https://blog.csdn.net/CPLASF_/article/details/110189503
上一篇: Cloud网管gateWay
下一篇: java 自建服务器