SpringBoot源码系列(5):参数解析

前言

在SpringBoot/SpringMVC中,我们能在Controller中解析出请求的参数,本文谈一谈其背后的原理。本文中SpringBoot版本号为2.7.5。

原文地址:https://xuedongyun.cn/post/13517/

参数解析原理

DispatcherServlet

之前的文章已经提到,DispatcherServlet的核心方法是doDispatch。在doDispatch方法中,我们先通过HandlerMappings找到了handler。再为当前Handler找到HandlerAdapter

这一部分可以查看我之前的博文复习:SpringBoot源码系列(4):请求映射

mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

getHandlerAdapter方法,遍历this.handlerAdapters,来找到合适的HandlerAdapter

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("...");
}

其中this.handlerAdapters包含了四个,完成不同的功能

this.handlerAdapters = {ArrayList} size=4
	0 = (RequestMappingHandlerAdapter)	// 支持标注@RequestMapping
	1 = (HandlerFunctionAdapter)		// 支持函数式编程
	2 = {HttpRequestHandlerAdapter}
	3 = {SimpleControllerHandlerAdapter}

RequestMappingHandlerAdapter

通过步入可以看到,RequestMappingHandlerAdapter(其父类AbstractHandlerMethodAdapter)实现了supports方法。

这里主要进行了类型判断,看handler是否是HandlerMethod类型。(RequestMappingHandlerAdaptersupportsInternal总返回true)

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

DispatcherServlet

现在回到DispatcherServlet中,开始使用handlerAdapter真正执行handler方法

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

RequestMappingHandlerAdapter

通过步入可以看到,RequestMappingHandlerAdapter(其父类AbstractHandlerMethodAdapter)实现了handle方法。

@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {

    return handleInternal(request, response, (HandlerMethod) handler);
}

RequestMappingHandlerAdapter实现了handleInternal方法。如果设置synchronizeOnSession为true,那么对于同一个session,需要加锁。可以看到,核心方法为invokeHandlerMethod

// 代码有删改
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ModelAndView mav;
    checkRequest(request);
	
    // 同一个session加锁
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            // 加锁
        } else {
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    } else {
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }
	
    return mav;
}

invokeHandlerMethod方法。其核心在于请求值解析器和返回值处理器

// 代码有删改
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        // argumentResolvers:请求值解析器,用于确定目标方法每一个参数值是什么
        if (this.argumentResolvers != null) {
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        // returnValueHandlers:返回值处理器
        if (this.returnValueHandlers != null) {
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        
        // 执行并处理
        invocableMethod.invokeAndHandle(webRequest, mavContainer);

        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
}
// 请求值解析器
argumentResolvers = {ArrayList} size = 27
	0 = {RequestParamMethodArgumentResolver}
	1 = {RequestParamMapMethodArgumentResolver}
	2 = {PathVariableMethodArgumentResolver}
	3 = {PathVariableMapMethodArgumentResolver}
	4 = {MatrixVariableMethodArgumentResolver}
	5 = {MatrixVariableMapMethodArgumentResolver}
	6 = {ServletModelAttributeMethodProcessor
	7 = {RequestResponseBodyMethodProcessor}
	...
// 返回值处理器
returnValueHandlers = {ArrayList} size = 15
	0 = {ModelAndViewMethodReturnValueHandler}
	1 = {ModelMethodProcessor)
	2 = {ViewMethodReturnValueHandler}
	3 = {ResponseBodyEmitterReturnValueHandler}
	4 = {StreamingResponseBodyReturnValueHandler}
	...

HandlerMethodArgumentResolver

我们可以简单看一下请求值解析器的接口设计。其中supportsParameter方法用于判断解析器是否支持解析当前参数。若支持,则将调用resolveArgument方法进行解析

public interface HandlerMethodArgumentResolver {
	
    boolean supportsParameter(MethodParameter parameter);

    @Nullable
    Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

ServletInvocableHandlerMethod

RequestMappingHandlerAdapter中,调用了invocableMethod.invokeAndHandle。我们现在查看ServletInvocableHandlerMethodinvokeAndHandle方法

// 代码有删改
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    setResponseStatus(webRequest);
}

继续查看invokeForRequest方法。可以看到,使用了getMethodArgumentValues方法获取了所有请求参数的值。最终调用doInvoke方法,利用反射执行了我们Controller中的方法。

@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {
	
    // 获取所有参数的值
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    
    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);
        }
    }
    // 返回目标函数参数的值
    return args;
}

HandlerMethodArgumentResolverComposite

我们之前调用了this.resolvers.supportsParameter(parameter)用于判断参数解析器是否支持当前参数。现在来看一下其中的源码

@Override
public boolean supportsParameter(MethodParameter parameter) {
    // 拿到参数解析器
    return getArgumentResolver(parameter) != null;
}
@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;
}

我们可以看一些例子,明白resolver.supportsParameter背后是如何判断的

比如(第一个)RequestParamMethodArgumentResolver解析器:参数要标@RequestParam注解

if (parameter.hasParameterAnnotation(RequestParam.class)) {}

比如(第二个)RequestParamMapMethodArgumentResolver解析器:参数要标@RequestParam注解,参数要为Map类型,@RequestParam注解中要没有name

RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
return (requestParam != null && 
        Map.class.isAssignableFrom(parameter.getParameterType()) &&
        !StringUtils.hasText(requestParam.name()));

而后,this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory)用于解析参数,我们看一看其中源码

@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("...");
    }
    // 核心代码
    return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

HandlerMethodArgumentResolver

可以看到,参数解析靠的还是参数解析器的resolveArgument方法(所有参数解析器都实现了HandlerMethodArgumentResolver接口,我们上面也谈过)。

(举例一)路径变量

我们这里以PathVariable参数为例来看一看背后的原理。其中类继承关系:HandlerMethodArgumentResolver <- AbstractNamedValueMethodArgumentResolver <- PathVariableMethodArgumentResolver

首先,PathVariableMethodArgumentResolver实现了supportsParameter方法。参数要标@PathVariable注解,若参数为Map类型,@PathVariable注解中要没有value

@Override
public boolean supportsParameter(MethodParameter parameter) {
    if (!parameter.hasParameterAnnotation(PathVariable.class)) {
        return false;
    }
    if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
        PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
        return (pathVariable != null && StringUtils.hasText(pathVariable.value()));
    }
    return true;
}

其次,AbstractNamedValueMethodArgumentResolver实现resolveArgument方法

@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();

    // 为了处理注解中使用了SpEL表达式的情况,不用管
    Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);

    // 解析参数的值,核心
    Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);

    handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
    return arg;
}

PathVariableMethodArgumentResolver实现了resolveName方法

@Override
@SuppressWarnings("unchecked")
@Nullable
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
    Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(
            HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
    
    // uriTemplateVars存储路径变量的值,直接获取
    return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
}

(举例二)Servlet API

我们这里以Servlet API参数为例来看一看背后的原理。其中类继承关系:HandlerMethodArgumentResolver <- ServletRequestMethodArgumentResolver

首先,ServletRequestMethodArgumentResolver实现了supportsParameter方法。参数需要为特定的类型

@Override
public boolean supportsParameter(MethodParameter parameter) {
    Class<?> paramType = parameter.getParameterType();
    return (WebRequest.class.isAssignableFrom(paramType) ||
            ServletRequest.class.isAssignableFrom(paramType) ||
            MultipartRequest.class.isAssignableFrom(paramType) ||
            HttpSession.class.isAssignableFrom(paramType) ||
            (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
            (Principal.class.isAssignableFrom(paramType) && !parameter.hasParameterAnnotations()) ||
            InputStream.class.isAssignableFrom(paramType) ||
            Reader.class.isAssignableFrom(paramType) ||
            HttpMethod.class == paramType ||
            Locale.class == paramType ||
            TimeZone.class == paramType ||
            ZoneId.class == paramType);
}

其次,ServletRequestMethodArgumentResolver实现了resolveArgument方法。根据具体类型执行不同的操作。

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

    Class<?> paramType = parameter.getParameterType();

    // WebRequest / NativeWebRequest / ServletWebRequest
    if (WebRequest.class.isAssignableFrom(paramType)) {
        if (!paramType.isInstance(webRequest)) {
            throw new IllegalStateException("...");
        }
        return webRequest;
    }

    // ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest
    if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
        return resolveNativeRequest(webRequest, paramType);
    }

    // HttpServletRequest required for all further argument types
    return resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));
}

我们以HttpServletRequest为例,执行了resolveNativeRequest方法,拿到原生ServletRequest并返回

private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {
    // 拿到原生ServletRequest,最后返回
    T nativeRequest = webRequest.getNativeRequest(requiredType);
    if (nativeRequest == null) {
        throw new IllegalStateException("...");
    }
    return nativeRequest;
}

(举例三)Model, Map原理

ModelMethodProcessorMapMethodProcessor参数解析器实现了supportsParameter方法

// 参数为Model
@Override
public boolean supportsParameter(MethodParameter parameter) {
    // 判断参数类型为Model
    return Model.class.isAssignableFrom(parameter.getParameterType());
}
// 参数为Map
@Override
public boolean supportsParameter(MethodParameter parameter) {
    // 判断参数类型为Map,且参数没有标注解
    return (Map.class.isAssignableFrom(parameter.getParameterType()) &&
            parameter.getParameterAnnotations().length == 0);
}

其次,它们都实现了resolveArgument方法

// 参数为Model
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                              NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    // 从modelAndViewContainer中获得Model
    return mavContainer.getModel();
}
// 参数为Map
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                              NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    // 从modelAndViewContainer中获得Model
    return mavContainer.getModel();
}

可以看到,无论是Model还是Map,返回的都是mavContainer里的ModelBindingAwareModelMap,根本上继承自MapModel),他们获取的都是同一个对象

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值