17、SpringMVC进阶:参数处理流程源码解析

有道无术,术尚可求,有术无道,止于术。

Spring MVC:5.3.20

文章目录

    • 前言
    1. 获取处理适配器
    1. 进入适配器处理方法
    1. 参数解析器处理参数
    • 3.1 27种参数解析器
    • 3.2 缓存
    • 3.3 执行参数解析
    • 3.4 反射执行控制器方法

前言

在之前我们分析了Spring MVC支持多种方式将请求参数封装到对应的控制器方法参数中=》Spring MVC系列(3)- 获取请求参数的多种方式

也简单分析了,请求进来都是由DispatcherServlet来进行处理的=》Spring MVC系列(7)-DispatcherServlet处理请求流程源码分析

接下来我们以一个简单的控制类分析下Spring MVC处理参数的源码。

@RestController
@RequestMapping("/test")
public class TestController {
   
     

    @GetMapping("/test")
    public String test(@RequestParam("name") String name) {
   
     
        return name;
    }

}

1. 获取处理适配器

DispatcherServletdoDispatch()中,首先会获取映射处理器,也就是根据请求路径,获取到对应的控制器Controller中的方法,比如:

http://localhost:8080/test/test?name=zhangsan
=》 org.pearl.boot.tstudy.controller.TestController#test(String)

接着DispatcherServlet会根据处理器来获取处理适配器HandlerAdapter,由他它调用具体的方法对用户发来的请求来进行处理,源码如下:

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

getHandlerAdapter方法中,会循环所有的适配器,找到能处理该请求的适配器并返回。

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   
     
        if (this.handlerAdapters != null) {
   
     
        // 1. 循环所有处理适配器
            Iterator var2 = this.handlerAdapters.iterator();

            while(var2.hasNext()) {
   
     
            	// 2. 找到能处理该请求的适配器并返回
                HandlerAdapter adapter = (HandlerAdapter)var2.next();
                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");
    }

HandlerAdapter接口源码如下:

public interface HandlerAdapter {
   
     
    // 判断是否支持传入的handler
    boolean supports(Object handler);
    // 使用handler处理请求
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    // 获取资源的LastModified值
    long getLastModified(HttpServletRequest request, Object handler);
}

HandlerAdapter实现类,代表了每一种类型的适配器,主要有四种:

  • RequestMappingHandlerAdapter:支持标注了@RequestMapping方法
  • HandlerFunctionAdapter:处理 HandlerFunction 类型的接口方法(函数式编程)
  • HttpRequestHandlerAdapter:处理实现了 HttpRequestHandler 接口的方法
  • SimpleControllerHandlerAdapter:主要用来处理实现了 Controller 接口的方法

因为案例中使用的@GetMapping("/test"),所以获取到的适配器是RequestMappingHandlerAdapter
*

2. 进入适配器处理方法

接着适配器就进入到其handle方法处理了。

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

handle方法进入到RequestMappingHandlerAdapterhandleInternal方法进行处理。

    protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
   
     
     	// 1. 检查请求方式和Session
        this.checkRequest(request);
        ModelAndView mav;
        // 2. 是否开启了Session并发控制,开了会进入到同步锁中
        if (this.synchronizeOnSession) {
   
     
            HttpSession session = request.getSession(false);
            if (session != null) {
   
     
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized(mutex) {
   
     
                    mav = this.invokeHandlerMethod(request, response, handlerMethod);
                }
            } else {
   
     
                mav = this.invokeHandlerMethod(request, response, handlerMethod);
            }
        } else {
   
     
        	// 3. 执行目标方法
            mav = this.invokeHandlerMethod(request, response, handlerMethod);
        }

        if (!response.containsHeader("Cache-Control")) {
   
     
            if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
   
     
                this.applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            } else {
   
     
                this.prepareResponse(response);
            }
        }

        return mav;
    }

handleInternal方法进入到invokeHandlerMethod方法中,执行目标方法。

    @Nullable
    // HandlerMethod =》org.pearl.boot.tstudy.controller.TestController#test(String)
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
   
     
    	// 1. 将Request和Response进行封装
        ServletWebRequest webRequest = new ServletWebRequest(request, response);

        Object result;
        try {
   
     
        	// 2. 获取WebDataBinderFactory 工厂对象,工厂创建WebDataBinder对象,WebDataBinder的作用就是把web请求的parameters绑定到JavaBean
            WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);
            // 3. 获取ModelFactory 工厂对象,他的主要作用是初始化Model和将Model中的参数更新到SessionAttributes中
            ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);
            // 4. 创建InvocableHandlerMethod对象,用于调用处理器方法,处理给定的请求
            ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
            // 4.1 设置参数解析器
            if (this.argumentResolvers != null) {
   
     
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }
			// 4.2 设置返回值解析器
            if (this.returnValueHandlers != null) {
   
     
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }
			// 4.3 设置数据绑定工厂
            invocableMethod.setDataBinderFactory(binderFactory);
            // 4.4 设置ParameterNameDiscoverer,发现方法和构造函数的参数名
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
            // 5. 创建ModelAndView容器,主要是用来返回Model对象
            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            // 5.1 添加属性
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            // 5.2 初始化Model
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            // 5.3 如果控制器方法重定向时,默认的Model是否使用
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
            // 6. 封装为异步请求
            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);
            // 6.1 获取异步管理器,支持异步请求处理
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
            if (asyncManager.hasConcurrentResult()) {
   
     
                result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                LogFormatUtils.traceDebug(this.logger, (traceOn) -> {
   
     
                    String formatted = LogFormatUtils.formatValue(result, !traceOn);
                    return "Resume with async result [" + formatted + "]";
                });
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }
			// 7. 执行处理器
            invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
            if (!asyncManager.isConcurrentHandlingStarted()) {
   
     
            	// 8. 返回ModelAndView实例, 后面进行视图解析
                ModelAndView var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);
                return var15;
            }

            result = null;
        } finally {
   
     
            webRequest.requestCompleted();
        }

        return (ModelAndView)result;
    }

invokeHandlerMethod方法中,最重要的就是invokeAndHandle方法。

    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
   
     
    	// 1. 处理请求并返回
        Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
        // 设置响应状态为200 
        this.setResponseStatus(webRequest);
        // 2. 处理返回值
        if (returnValue == null) {
   
     
            if (this.isRequestNotModified(webRequest) || this.getResponseStatus() != null || mavContainer.isRequestHandled()) {
   
     
                this.disableContentCachingIfNecessary(webRequest);
                mavContainer.setRequestHandled(true);
                return;
            }
        } else if (StringUtils.hasText(this.getResponseStatusReason())) {
   
     
            mavContainer.setRequestHandled(true);
            return;
        }

        mavContainer.setRequestHandled(false);
        Assert.state(this.returnValueHandlers != null, "No return value handlers");

        try {
   
     
        	// 3. 返回值处理器
            this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
        } catch (Exception var6) {
   
     
            if (logger.isTraceEnabled()) {
   
     
                logger.trace(this.formatErrorForReturnValue(returnValue), var6);
            }

            throw var6;
        }
    }

invokeAndHandle方法中会调用invokeForRequest方法处理请求

    @Nullable
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
   
     
    	// 1. 获取方法参数对应的值
        Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
   
     
            logger.trace("Arguments: " + Arrays.toString(args));
        }
		// 2. 执行控制器上的方法
        return this.doInvoke(args);
    }

invokeForRequest方法就是处理参数的核心方法,调用参数解析器对参数处理。

    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
   
     
    	// 1. 获取方法中的参数集合
        MethodParameter[] parameters = this.getMethodParameters();
        if (ObjectUtils.isEmpty(parameters)) {
   
     
            return EMPTY_ARGS;
        } else {
   
     
            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) {
   
     
                	// 2. 参数解析器不支持该参数解析,抛出IllegalStateException
                    if (!this.resolvers.supportsParameter(parameter)) {
   
     
                        throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
                    }

                    try {
   
     
                    	// 3. 调用参数解析器解析参数
                        args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
                    } catch (Exception var10) {
   
     
                        if (logger.isDebugEnabled()) {
   
     
                            String exMsg = var10.getMessage();
                            if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
   
     
                                logger.debug(formatArgumentError(parameter, exMsg));
                            }
                        }

                        throw var10;
                    }
                }
            }
			// 4. 返回参数
            return args;
        }
    }

this.getMethodParameters()方法中,获取MethodParameter集合,每一个MethodParameter对象都封装了该请求方法的参数位置(0开始)、类型、标识注解等信息,比如public String test(@RequestParam("name") String name)方法获取到的MethodParameter集合如下所示:
*

3. 参数解析器处理参数

this.resolvers.resolveArgument会进行参数解析,可以看到resolvers中包含了27种类型的参数解析器和缓存。
*

3.1 27种参数解析器

参数解析器的顶级接口为HandlerMethodArgumentResolver,声明了两个方法,源码如下:

public interface HandlerMethodArgumentResolver {
   
     
	// 是否支持该类型MethodParameter 解析
    boolean supportsParameter(MethodParameter parameter);
	
	// 解析参数
    @Nullable
    Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}

默认这里显示的有27个,我们也可以自定义参数解析器:
*

比如常见的解析器有:

  • RequestParamMethodArgumentResolver:解析@RequestParam 注解的参数
  • PathVariableMethodArgumentResolver:将请求URL中的模板变量映射到接口方法的参数解析器,@PathVariable注解
  • RequestHeaderMethodArgumentResolver:标记了@RequestHeader注解,解析请求头参数绑定到接口参数上

3.2 缓存

argumentResolverCache是一个ConcurrentHashMap类型的缓存,将方法参数对象和对应的解析器缓存起来,第一次解析后放入缓存,第二次再请求该方法就从缓存中获取解析器,提高效率。

 private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap(256);

3.3 执行参数解析

执行参数解析时,首先会获取到该参数的解析器,首先在缓存中获取,没有的话,就会循环所有的解析器,直到找到一个支持该参数的解析器,然后再放入到缓存中。
*

接着开始解析参数,因为我们案例使用的是@RequestParam注解,所以获取到的是RequestParamMethodArgumentResolver解析器

    @Nullable
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
   
     
    	// 1. 获取该参数的解析器
        HandlerMethodArgumentResolver resolver = this.getArgumentResolver(parameter);
        if (resolver == null) {
   
     
            throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
        } else {
   
     
        	// 2. 解析并返回
            return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
        }
    }

RequestParamMethodArgumentResolver解析参数源码如下:

    @Nullable
    public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
   
     
    	// 1. 获取RequestParam注解信息,包含参数的名称,是否必须
        AbstractNamedValueMethodArgumentResolver.NamedValueInfo namedValueInfo = this.getNamedValueInfo(parameter);
        MethodParameter nestedParameter = parameter.nestedIfOptional();
        // 2. 通过表达式获取到方法参数上的名称
        Object resolvedName = this.resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
        // 3. 没有根据RequestParam注解 找到对应的参数,抛出IllegalArgumentException
        if (resolvedName == null) {
   
     
            throw new IllegalArgumentException("Specified name must not resolve to null: [" + namedValueInfo.name + "]");
        } else {
   
     
        	// 4.1 根据名称从请求中获取值,@RequestParam("name") String name=》zhangsan
            Object arg = this.resolveName(resolvedName.toString(), nestedParameter, webRequest);
            if (arg == null) {
   
     
            	// 4.2 没拿到,看是否有默认值
                if (namedValueInfo.defaultValue != null) {
   
     
                    arg = this.resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
                } else if (namedValueInfo.required && !nestedParameter.isOptional()) {
   
     
                	// 4.3 没拿到,又是必填项,抛出异常
                    this.handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
                }
				// 4.4 没有必填 也没有默认值,处理Null值
                arg = this.handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
            } else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
   
     
            	// 4.5 为空并且存在默认值,则处理空值
                arg = this.resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
            }
			// 5. 处理参数绑定
            if (binderFactory != null) {
   
     
          		// 5.1 工厂创建WebDataBinder 对象,可以对参数进行转换,然后绑定
                WebDataBinder binder = binderFactory.createBinder(webRequest, (Object)null, namedValueInfo.name);

                try {
   
     
                    arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
                } catch (ConversionNotSupportedException var11) {
   
     
                    throw new MethodArgumentConversionNotSupportedException(arg, var11.getRequiredType(), namedValueInfo.name, parameter, var11.getCause());
                } catch (TypeMismatchException var12) {
   
     
                    throw new MethodArgumentTypeMismatchException(arg, var12.getRequiredType(), namedValueInfo.name, parameter, var12.getCause());
                }

                if (arg == null && namedValueInfo.defaultValue == null && namedValueInfo.required && !nestedParameter.isOptional()) {
   
     
                    this.handleMissingValueAfterConversion(namedValueInfo.name, nestedParameter, webRequest);
                }
            }

            this.handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
            // 6. 返回参数值
            return arg;
        }
    }

在请求中获取到该参数对应的请求值:

    @Nullable
    protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
   
     
    	// 1. 获取HttpServletRequest
        HttpServletRequest servletRequest = (HttpServletRequest)request.getNativeRequest(HttpServletRequest.class);
        Object arg;
        if (servletRequest != null) {
   
     
        	// 2. 调用方法,获取到文件参数,如果没有,则返回一个new Object
            arg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
            // 3. 存在文件参数,直接返回
            if (arg != MultipartResolutionDelegate.UNRESOLVABLE) {
   
     
                return arg;
            }
        }

        arg = null;、
        // 4. 获取文件参数
        MultipartRequest multipartRequest = (MultipartRequest)request.getNativeRequest(MultipartRequest.class);
        if (multipartRequest != null) {
   
     
        	// 5. 存在文件则获取文件流并转为MultipartFile对象
            List<MultipartFile> files = multipartRequest.getFiles(name);
            if (!files.isEmpty()) {
   
     
                arg = files.size() == 1 ? files.get(0) : files;
            }
        }
		// 6. 没有文件则根据参数名称,从request中拿到参数对应的值
        if (arg == null) {
   
     
            String[] paramValues = request.getParameterValues(name);
            if (paramValues != null) {
   
     
                arg = paramValues.length == 1 ? paramValues[0] : paramValues;
            }
        }
		// 7. 返回参数对应的值
        return arg;
    }

3.4 反射执行控制器方法

最后,通过反射执行控制器中的方法,并将参数值传递过去,整个参数处理的流程就结束了:

    @Nullable
    protected Object doInvoke(Object... args) throws Exception {
   
     
    	// 1. 获取执行的方法
        Method method = this.getBridgedMethod();

        try {
   
     
        	// 2. 执行方法,进入到控制器方法中(反射),参数也带过去了。
            return KotlinDetector.isSuspendingFunction(method) ? CoroutinesUtils.invokeSuspendingFunction(method, this.getBean(), args) : method.invoke(this.getBean(), args);
        } catch (IllegalArgumentException var5) {
   
     
            this.assertTargetBean(method, this.getBean(), args);
            String text = var5.getMessage() != null ? var5.getMessage() : "Illegal argument";
            throw new IllegalStateException(this.formatInvokeError(text, args), var5);
        } catch (InvocationTargetException var6) {
   
     
            // ....
        }
    }

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: