SpringBoot请求处理之控制器参数处理——源码学习

该文SpringBoot版本:2.3.4
在DispatchServlet的doDispacth方法中,通过mappedHandler = getHandler(processedRequest);获得请求应用的处理器(controller.method())和拦截器,但没有立刻执行处理器中对应的方法,而是通过HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());获得一个处理器的适配器,这个适配器主要是为了在原有处理器基础上,处理请求参数的返回值。这里面包含了大量的工作,下面主要来探究一下请求参数的处理。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		if (this.handlerAdapters != null) {
			for (HandlerAdapter adapter : this.handlerAdapters) {
				if (adapter.supports(handler)) {
					return adapter;
				}
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

容器中已经存在了一些适配器,循环遍历找到当前请求需要的。
在这里插入图片描述
其中调用adapter.supports(handler)去判断是否是当前handler适配的。
我们常用的类是RequestMappingHandlerAdapter,对应用@RequestMapping标注过的方法。
选择它的判断方法则是

@Override
	public final boolean supports(Object handler) {
		return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
	}

判断传进来的handler是否是HandlerMethod类型。
在获得HandlerAdapter后,在doDispatch()中调用mv = ha.handle(processedRequest, response, mappedHandler.getHandler());来执行方法。
一直追进来,最后InvocableHandlerMethod类中真正调用业务方法。

//InvocableHandlerMethod类
@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)) {
			return EMPTY_ARGS;
		}

		Object[] args = new Object[parameters.length];
		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[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;
	}
@Nullable
	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		if (result == null) {
		//循环判断容器中的参数解析器是否可以处理该参数
			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
				if (resolver.supportsParameter(parameter)) {
					result = resolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}

容器中已经存在了26个参数解析器
在这里插入图片描述
对应了不同的参数类型。
SpringMVC目标方法能写多少种参数类型,取决于参数解析器。
在这里插入图片描述
解析器中定义了两个方法,一个判断,一个处理。
举例来说PathVariableMethodArgumentResolver是判断参数是否有@PathVariable注解。

在这里插入图片描述
在确定参数解释器后,调用其中的resolveArgument来解析参数
下面还是@PathVariable对应的解析器

@Override
	@Nullable
	public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
		MethodParameter nestedParameter = parameter.nestedIfOptional();

		Object resolvedName = resolveStringValue(namedValueInfo.name);
		if (resolvedName == null) {
			throw new IllegalArgumentException(
					"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
		}
		//将参数值(现在还是字符串)从url中解析出来
		Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
		if (arg == null) {
			if (namedValueInfo.defaultValue != null) {
				arg = resolveStringValue(namedValueInfo.defaultValue);
			}
			else if (namedValueInfo.required && !nestedParameter.isOptional()) {
				handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
			}
			arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
		}
		else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
			arg = resolveStringValue(namedValueInfo.defaultValue);
		}

		if (binderFactory != null) {
		//WebDataBinder :web数据绑定器,将请求参数的值绑定到指定类型的变量、javabean中
		//其中会利用利用它里面的 Converters 将请求数据转成指定的数据类型
			WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
			try {
			//利用合适的数据转换器生成对应类型的对象并赋值
				arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
			}
			catch (ConversionNotSupportedException ex) {
				throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
						namedValueInfo.name, parameter, ex.getCause());
			}
			catch (TypeMismatchException ex) {
				throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
						namedValueInfo.name, parameter, ex.getCause());
			}
		}

		handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
		//返回hanlder需要的已赋值的形参对象
		return arg;
	}

在这里插入图片描述
内置124个类型转换器,当然也可以自定义类型转换–>实现converter接口–>配置

@Configuration
public class MyConfig {
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer(){
            @Override
            public void addFormatters(FormatterRegistry registry) {
                registry.addConverter(自定义的converter);
        }
    }
}

就这样循环的获取每一个形参,最后传递给业务方法,调用doInvoke(args)真正的执行业务方法。
在这里插入图片描述
在执行完Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);,就把参数解析和业务逻辑完成,后面就是返回值处理的过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值