SpingMVC参数解析过程(一)

  SpringMVC 可以用@PathVariable、@RequestParam等来接收不同的参数类型。下面研究其参数解析过程,不管是何种参数方式,都是从Request 中拿的参数。

1. 测试Controller

代码如下:

User 类:

package com.xm.ggn.test;

import lombok.Data;

@Data
public class User {

    private String username;

    private String fullname;

    private String createDate;
}

Controller 接口:

    @PostMapping("/user/add/{userId}")
    public User addUser(@RequestBody User user, @PathVariable String userId, @RequestParam String username, User user2) {
        System.out.println(user);
        System.out.println(userId);
        System.out.println(username);
        System.out.println(user2);
        return user;
    }

测试访问:

curl -X POST --header 'Content-Type: application/json' -d '{"username": "lisi", "fullname": "lisi"}' 'http://localhost:8088/user/add/3?username=zs'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:100   150    0   110  100    40   2340    851 --:--:-- --:--:-- --:--:--  6875{"success":true,"data":{"username":"lisi","fullname":"lisi","createDate":null},"msg":"成功","errorCode":"0"}

2. 分析其解析过程

1. SpringMVC 入口是在方法:org.springframework.web.servlet.DispatcherServlet#doDispatch, 其大致流程如下:

(1)  获取一个处理器执行器链: HandlerExecutionChain (包含Handler 和Inteceptors)。 org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler

(2)  遍历处理器链获取一个HandlerAdapter 处理器适配器, 适配的类型是org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter。 

(3)  调用到方法开始处理:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod

2. 接下来研究处理过程的参数解析环节

1. org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle

2. 调用到 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal

    @Override
    protected ModelAndView handleInternal(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ModelAndView mav;
        checkRequest(request);

        // Execute invokeHandlerMethod in synchronized block if required.
        if (this.synchronizeOnSession) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized (mutex) {
                    mav = invokeHandlerMethod(request, response, handlerMethod);
                }
            }
            else {
                // No HttpSession available -> no mutex necessary
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // No synchronization on session demanded at all...
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }

        if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
            if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            }
            else {
                prepareResponse(response);
            }
        }

        return mav;
    }

3. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod

4. org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle

5. org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest

    @Nullable
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Arguments: " + Arrays.toString(args));
        }

        return this.doInvoke(args);
    }

6. 参数解析:org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues

    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        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) {
                    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 var10) {
                        if (this.logger.isDebugEnabled()) {
                            String exMsg = var10.getMessage();
                            if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                                this.logger.debug(formatArgumentError(parameter, exMsg));
                            }
                        }

                        throw var10;
                    }
                }
            }

            return args;
        }
    }

  可以看到参数解析是在这里,根据参数的个数,然后循环遍历获取参数信息。

1》单个参数的解析过程如下:org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument

    @Nullable
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        HandlerMethodArgumentResolver resolver = this.getArgumentResolver(parameter);
        if (resolver == null) {
            throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
        } else {
            return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
        }
    }

获取到参数解析器,然后进行参数的解析,获取参数解析器的方法如下:

    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        HandlerMethodArgumentResolver result = (HandlerMethodArgumentResolver)this.argumentResolverCache.get(parameter);
        if (result == null) {
            Iterator var3 = this.argumentResolvers.iterator();

            while(var3.hasNext()) {
                HandlerMethodArgumentResolver resolver = (HandlerMethodArgumentResolver)var3.next();
                if (resolver.supportsParameter(parameter)) {
                    result = resolver;
                    this.argumentResolverCache.put(parameter, resolver);
                    break;
                }
            }
        }

        return result;
    }

  可以看到先从缓存里面拿,如果缓存里面没有拿到,遍历MVC内置的一些解析器,然后判断是否支持该参数的解析,如果支持解析完存入缓存map,下次直接从缓存中拿。这个类内置了大概30个参数解析器,如下:

主要的参数解析器为:

RequestResponseBodyMethodProcessor - 解析@RequestBody和@ResponseBody 的参数

PathVariableMethodArgumentResolver- 解析@PathVariable 注解的

RequestParamMethodArgumentResolver - 解析@RequestParam 注解修饰的参数

ServletModelAttributeMethodProcessor - 解析默认不加任何注解的参数以及解析@ModelAttribute 注解修饰的参数

2》 然后开始解析参数调用的方法是参数解析器的resolveArgument 方法

分析四个解析过程:

(1)如果是RequestBody 修饰的参数:

org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#resolveArgument

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

        parameter = parameter.nestedIfOptional();
        Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
        String name = Conventions.getVariableNameForParameter(parameter);

        if (binderFactory != null) {
            WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
            if (arg != null) {
                validateIfApplicable(binder, parameter);
                if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                    throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
                }
            }
            if (mavContainer != null) {
                mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
            }
        }

        return adaptArgumentIfNecessary(arg, parameter);
    }

请求转交给父类:org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters(org.springframework.http.HttpInputMessage, org.springframework.core.MethodParameter, java.lang.reflect.Type)

    protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
            Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

        MediaType contentType;
        boolean noContentType = false;
        try {
            contentType = inputMessage.getHeaders().getContentType();
        }
        catch (InvalidMediaTypeException ex) {
            throw new HttpMediaTypeNotSupportedException(ex.getMessage());
        }
        if (contentType == null) {
            noContentType = true;
            contentType = MediaType.APPLICATION_OCTET_STREAM;
        }

        Class<?> contextClass = parameter.getContainingClass();
        Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
        if (targetClass == null) {
            ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
            targetClass = (Class<T>) resolvableType.resolve();
        }

        HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
        Object body = NO_VALUE;

        EmptyBodyCheckingHttpInputMessage message;
        try {
            message = new EmptyBodyCheckingHttpInputMessage(inputMessage);

            for (HttpMessageConverter<?> converter : this.messageConverters) {
                Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
                GenericHttpMessageConverter<?> genericConverter =
                        (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
                if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
                        (targetClass != null && converter.canRead(targetClass, contentType))) {
                    if (message.hasBody()) {
                        HttpInputMessage msgToUse =
                                getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                        body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                                ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                        body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                    }
                    else {
                        body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
                    }
                    break;
                }
            }
        }
        catch (IOException ex) {
            throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
        }

        if (body == NO_VALUE) {
            if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
                    (noContentType && !message.hasBody())) {
                return null;
            }
            throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
        }

        MediaType selectedContentType = contentType;
        Object theBody = body;
        LogFormatUtils.traceDebug(logger, traceOn -> {
            String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
            return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
        });

        return body;
    }

  这里实际就是读取请求体中的数据,然后转换成JSON对象。

  判断messageConverter 消息转换器是否能读取消息,对于普通的Bean 支持的转换器是org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 然后处理消息,也就是读取请求体重的数据,然后转换成对应的java Bean。 注意这里会交给JackSON工具包的com.fasterxml.jackson.databind.ObjectMapper#_readMapAndClose 方法。

(2) 如果是@PathVariable 修饰的参数: 请求交给org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument

父类会调用子类的org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver#resolveName 方法进行解析参数:

    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);
        return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
    }

  实际就是从参数模板的Map 中根据名称获取到一个参数的值

(3) 如果是@RequestParam 注解修饰的参数:

最后也是会打到org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument

请求打到:org.springframework.web.method.annotation.RequestParamMethodArgumentResolver#resolveName。

    protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
        HttpServletRequest servletRequest = (HttpServletRequest)request.getNativeRequest(HttpServletRequest.class);
        Object arg;
        if (servletRequest != null) {
            arg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
            if (arg != MultipartResolutionDelegate.UNRESOLVABLE) {
                return arg;
            }
        }

        arg = null;
        MultipartRequest multipartRequest = (MultipartRequest)request.getNativeRequest(MultipartRequest.class);
        if (multipartRequest != null) {
            List<MultipartFile> files = multipartRequest.getFiles(name);
            if (!files.isEmpty()) {
                arg = files.size() == 1 ? files.get(0) : files;
            }
        }

        if (arg == null) {
            String[] paramValues = request.getParameterValues(name);
            if (paramValues != null) {
                arg = paramValues.length == 1 ? paramValues[0] : paramValues;
            }
        }

        return arg;
    }

  实际就是调用request.getParameterValues 获取到参数的值 

(4) 如果没有注解修饰或者@ModelAttribute 修饰的参数:

org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument

    public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
        Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
        String name = ModelFactory.getNameForParameter(parameter);
        ModelAttribute ann = (ModelAttribute)parameter.getParameterAnnotation(ModelAttribute.class);
        if (ann != null) {
            mavContainer.setBinding(name, ann.binding());
        }

        Object attribute = null;
        BindingResult bindingResult = null;
        if (mavContainer.containsAttribute(name)) {
            attribute = mavContainer.getModel().get(name);
        } else {
            try {
                attribute = this.createAttribute(name, parameter, binderFactory, webRequest);
            } catch (BindException var10) {
                if (this.isBindExceptionRequired(parameter)) {
                    throw var10;
                }

                if (parameter.getParameterType() == Optional.class) {
                    attribute = Optional.empty();
                }

                bindingResult = var10.getBindingResult();
            }
        }

        if (bindingResult == null) {
            WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
            if (binder.getTarget() != null) {
                if (!mavContainer.isBindingDisabled(name)) {
                    this.bindRequestParameters(binder, webRequest);
                }

                this.validateIfApplicable(binder, parameter);
                if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) {
                    throw new BindException(binder.getBindingResult());
                }
            }

            if (!parameter.getParameterType().isInstance(attribute)) {
                attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
            }

            bindingResult = binder.getBindingResult();
        }

        Map<String, Object> bindingResultModel = bindingResult.getModel();
        mavContainer.removeAttributes(bindingResultModel);
        mavContainer.addAllAttributes(bindingResultModel);
        return attribute;
    }

3.  自定义自己的参数解析器以及DataBinder 

 1. 声明一个注解用于从标记使用自定义的参数处理器

package com.xm.ggn.test.paramresolver;

import java.lang.annotation.*;

/**
 * 标记Head中取值的注解
 */
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HeaderParam {

}

2. 编写一个参数处理器,模拟从Header 中获取到参数之后设置值

package com.xm.ggn.test;

import com.xm.ggn.test.paramresolver.HeaderParam;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.core.MethodParameter;
import org.springframework.objenesis.instantiator.util.ClassUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import sun.reflect.misc.FieldUtil;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class HeaderParamMethodArgumentResolver implements HandlerMethodArgumentResolver {

    /**
     * 查看是否有
     *
     * @param parameter
     * @return
     */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(HeaderParam.class);
    }

    /**
     * 解析参数: 从Header 中拿取属性
     *
     * @param parameter
     * @param mavContainer
     * @param webRequest
     * @param binderFactory
     * @return
     * @throws Exception
     */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest servletRequest = (HttpServletRequest) webRequest.getNativeRequest(HttpServletRequest.class);
        Class<?> parameterType = parameter.getParameterType();
        String parameterName = parameter.getParameterName();
        // 如果是基本数据类型设置值之后返回
        if (isPrimitive(parameterType)) {
            return getArg(parameterType, servletRequest.getHeader(parameterName));
        }

        // 如果是String获取之后返回
        if (String.class.equals(parameterType)) {
            return servletRequest.getHeader(parameterName);
        }

        // 包装类型的话直接创造对象,然后遍历属性进行设置
        Field[] fields = FieldUtil.getDeclaredFields(parameterType);
        Object result = ClassUtils.newInstance(parameterType);
        Object arg = null;
        for (Field field : fields) {
            arg = servletRequest.getHeader(field.getName());
            if (arg == null) {
                continue;
            }
            if (isPrimitive(field.getType())) {
                Class<?> parType = field.getType();
                arg = getArg(parType, arg);
            }
            if (arg == null) {
                continue;
            }
            Method setter = getSetterMethod(parameterType, field);
            if (setter != null) {
                setter.invoke(result, arg);
            }
        }
        return result;
    }

    /**
     * 获取到Set 方法
     *
     * @param clazz
     * @param field
     * @return
     * @throws NoSuchMethodException
     */
    private Method getSetterMethod(Class<?> clazz, Field field) throws NoSuchMethodException {
        return clazz.getDeclaredMethod("set" + toUpperCaseFirstOne(field.getName()), field.getType());
    }

    /**
     * 字段第一个转为大写
     *
     * @param fieldName
     * @return
     */
    private String toUpperCaseFirstOne(String fieldName) {
        if (Character.isUpperCase(fieldName.charAt(0))) {
            return fieldName;
        }

        return String.valueOf(Character.toUpperCase(fieldName.charAt(0))) + fieldName.substring(1);
    }

    private static final Class[] PRIMITIVE_CLAZZ = {Byte.class, Short.class, Integer.class, Long.class, Character.class, Boolean.class, Float.class, Double.class};

    /**
     * 是否是8种基本类型的包装类型
     *
     * @param clazz
     * @return
     */
    private boolean isPrimitive(Class<?> clazz) {
        return ArrayUtils.contains(PRIMITIVE_CLAZZ, clazz);
    }

    private Object getArg(Class<?> primitiveClass, Object value) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        if (value == null) {
            return null;
        }

        // 获取到类型名称
        if (isPrimitive(primitiveClass)) {
            // 还可以用 String methodName = "parse" + firstUpperprimitiveName 构造方法名称; 然后用 clazz.getDeclaredMethod(methodName, String.class) 获取方法
            String primitiveName = primitiveClass.getSimpleName();
            switch (primitiveName) {
                case "Byte":
                    return Byte.parseByte(primitiveName);
                case "Short":
                    return Short.parseShort(primitiveName);
                case "Integer":
                    return Integer.parseInt(primitiveName);
                case "Long":
                    return Long.parseLong(primitiveName);
                case "Character":
                    value.toString().charAt(0);
                case "Boolean":
                    return Boolean.parseBoolean(primitiveName);
                case "Float":
                    return Float.parseFloat(primitiveName);
                case "Double":
                    return Double.parseDouble(primitiveName);
            }
        }

        return null;
    }

}

3. 注册到SpringMVC 的参数解析器列表中

package com.xm.ggn.config;

import com.xm.ggn.test.HeaderParamMethodArgumentResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import java.util.List;

/**
 * 1.JSON返回实体类报错2.设置页面的默认页面
 *
 * @author Administrator
 */
@Configuration
public class MVCConfig extends WebMvcConfigurerAdapter {
    /**
     * 解决JSON返回实体类报错
     */
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(false);
    }

    /**
     * 设置页面的默认页面
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("forward:/index.html");
        registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
        super.addViewControllers(registry);
    }

    /**
     * 将UserArgumentResolver将入到处理器队列中来
     *
     * @param argumentResolvers
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        if (argumentResolvers.isEmpty()) {
            argumentResolvers.add(0, new HeaderParamMethodArgumentResolver());
        } else {
            argumentResolvers.set(0, new HeaderParamMethodArgumentResolver());
        }
    }
}

4. 编写测试Controller

    @PostMapping("/user/add/{userId}")
    public User addUser(@RequestBody User user, @PathVariable String userId, @RequestParam String username, @HeaderParam User user2, @HeaderParam String headKey, User user3) {
        System.out.println(user);
        System.out.println(userId);
        System.out.println(username);
        System.out.println(user2);
        System.out.println(headKey);
        System.out.println(user3);
        return user;
    }

 

  上面据完成了一个简单的自定义参数解析器,从header 中读取属性。

 

补充: 对于HttpServletRequest、HttpSession、ServletResponse 等参数解析过程

1. 对于这样的参数解析是在org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument:

    @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("Unsupported parameter type [" +
                    parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
        }
        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }

     获取到的 resolver 是:org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver 或者 ServletResponseMethodArgumentResolver

2.org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver 源码如下:

package org.springframework.web.servlet.mvc.method.annotation;

import java.io.InputStream;
import java.io.Reader;
import java.security.Principal;
import java.time.ZoneId;
import java.util.Locale;
import java.util.TimeZone;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.core.MethodParameter;
import org.springframework.http.HttpMethod;
import org.springframework.lang.UsesJava8;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.servlet.support.RequestContextUtils;

/**
 * Resolves request-related method argument values of the following types:
 * <ul>
 * <li>{@link WebRequest}
 * <li>{@link ServletRequest}
 * <li>{@link MultipartRequest}
 * <li>{@link HttpSession}
 * <li>{@link Principal}
 * <li>{@link InputStream}
 * <li>{@link Reader}
 * <li>{@link HttpMethod} (as of Spring 4.0)
 * <li>{@link Locale}
 * <li>{@link TimeZone} (as of Spring 4.0)
 * <li>{@link java.time.ZoneId} (as of Spring 4.0 and Java 8)
 * </ul>
 *
 * @author Arjen Poutsma
 * @author Rossen Stoyanchev
 * @author Juergen Hoeller
 * @since 3.1
 */
public class ServletRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {

    @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) ||
                Principal.class.isAssignableFrom(paramType) ||
                InputStream.class.isAssignableFrom(paramType) ||
                Reader.class.isAssignableFrom(paramType) ||
                HttpMethod.class == paramType ||
                Locale.class == paramType ||
                TimeZone.class == paramType ||
                "java.time.ZoneId".equals(paramType.getName()));
    }

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

        Class<?> paramType = parameter.getParameterType();
        if (WebRequest.class.isAssignableFrom(paramType)) {
            if (!paramType.isInstance(webRequest)) {
                throw new IllegalStateException(
                        "Current request is not of type [" + paramType.getName() + "]: " + webRequest);
            }
            return webRequest;
        }

        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
            Object nativeRequest = webRequest.getNativeRequest(paramType);
            if (nativeRequest == null) {
                throw new IllegalStateException(
                        "Current request is not of type [" + paramType.getName() + "]: " + request);
            }
            return nativeRequest;
        }
        else if (HttpSession.class.isAssignableFrom(paramType)) {
            HttpSession session = request.getSession();
            if (session != null && !paramType.isInstance(session)) {
                throw new IllegalStateException(
                        "Current session is not of type [" + paramType.getName() + "]: " + session);
            }
            return session;
        }
        else if (InputStream.class.isAssignableFrom(paramType)) {
            InputStream inputStream = request.getInputStream();
            if (inputStream != null && !paramType.isInstance(inputStream)) {
                throw new IllegalStateException(
                        "Request input stream is not of type [" + paramType.getName() + "]: " + inputStream);
            }
            return inputStream;
        }
        else if (Reader.class.isAssignableFrom(paramType)) {
            Reader reader = request.getReader();
            if (reader != null && !paramType.isInstance(reader)) {
                throw new IllegalStateException(
                        "Request body reader is not of type [" + paramType.getName() + "]: " + reader);
            }
            return reader;
        }
        else if (Principal.class.isAssignableFrom(paramType)) {
            Principal userPrincipal = request.getUserPrincipal();
            if (userPrincipal != null && !paramType.isInstance(userPrincipal)) {
                throw new IllegalStateException(
                        "Current user principal is not of type [" + paramType.getName() + "]: " + userPrincipal);
            }
            return userPrincipal;
        }
        else if (HttpMethod.class == paramType) {
            return HttpMethod.resolve(request.getMethod());
        }
        else if (Locale.class == paramType) {
            return RequestContextUtils.getLocale(request);
        }
        else if (TimeZone.class == paramType) {
            TimeZone timeZone = RequestContextUtils.getTimeZone(request);
            return (timeZone != null ? timeZone : TimeZone.getDefault());
        }
        else if ("java.time.ZoneId".equals(paramType.getName())) {
            return ZoneIdResolver.resolveZoneId(request);
        }
        else {
            // Should never happen...
            throw new UnsupportedOperationException(
                    "Unknown parameter type [" + paramType.getName() + "] in " + parameter.getMethod());
        }
    }


    /**
     * Inner class to avoid a hard-coded dependency on Java 8's {@link java.time.ZoneId}.
     */
    @UsesJava8
    private static class ZoneIdResolver {

        public static Object resolveZoneId(HttpServletRequest request) {
            TimeZone timeZone = RequestContextUtils.getTimeZone(request);
            return (timeZone != null ? timeZone.toZoneId() : ZoneId.systemDefault());
        }
    }

}

  supportsParameter 方法可以看到其支持的类型。

  org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver#resolveArgument(java.lang.Class<?>, javax.servlet.http.HttpServletRequest) 方法可以看到其获取对应类型的参数的具体策略。

3. 对于HttpSession 实际是调用org.apache.catalina.connector.RequestFacade#getSession(): (如果被过滤器或者监听器重写了Request 是调用自己Request的相关方法)

    @Override
    public HttpSession getSession() {

        if (request == null) {
            throw new IllegalStateException(
                            sm.getString("requestFacade.nullRequest"));
        }

        return getSession(true);
    }

    @Override
    public HttpSession getSession(boolean create) {

        if (request == null) {
            throw new IllegalStateException(
                            sm.getString("requestFacade.nullRequest"));
        }

        if (SecurityUtil.isPackageProtectionEnabled()){
            return AccessController.
                doPrivileged(new GetSessionPrivilegedAction(create));
        } else {
            return request.getSession(create);
        }
    }

继续调用到:org.apache.catalina.connector.Request#getSession(boolean)

    public HttpSession getSession(boolean create) {
        Session session = doGetSession(create);
        if (session == null) {
            return null;
        }

        return session.getSession();
    }

继续调用:

    protected Session doGetSession(boolean create) {

        // There cannot be a session if no context has been assigned yet
        Context context = getContext();
        if (context == null) {
            return null;
        }

        // Return the current session if it exists and is valid
        if ((session != null) && !session.isValid()) {
            session = null;
        }
        if (session != null) {
            return session;
        }

        // Return the requested session if it exists and is valid
        Manager manager = context.getManager();
        if (manager == null) {
            return null;      // Sessions are not supported
        }
        if (requestedSessionId != null) {
            try {
                session = manager.findSession(requestedSessionId);
            } catch (IOException e) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("request.session.failed", requestedSessionId, e.getMessage()), e);
                } else {
                    log.info(sm.getString("request.session.failed", requestedSessionId, e.getMessage()));
                }
                session = null;
            }
            if ((session != null) && !session.isValid()) {
                session = null;
            }
            if (session != null) {
                session.access();
                return session;
            }
        }

        // Create a new session if requested and the response is not committed
        if (!create) {
            return null;
        }
        boolean trackModesIncludesCookie =
                context.getServletContext().getEffectiveSessionTrackingModes().contains(SessionTrackingMode.COOKIE);
        if (trackModesIncludesCookie && response.getResponse().isCommitted()) {
            throw new IllegalStateException(sm.getString("coyoteRequest.sessionCreateCommitted"));
        }

        // Re-use session IDs provided by the client in very limited
        // circumstances.
        String sessionId = getRequestedSessionId();
        if (requestedSessionSSL) {
            // If the session ID has been obtained from the SSL handshake then
            // use it.
        } else if (("/".equals(context.getSessionCookiePath())
                && isRequestedSessionIdFromCookie())) {
            /* This is the common(ish) use case: using the same session ID with
             * multiple web applications on the same host. Typically this is
             * used by Portlet implementations. It only works if sessions are
             * tracked via cookies. The cookie must have a path of "/" else it
             * won't be provided for requests to all web applications.
             *
             * Any session ID provided by the client should be for a session
             * that already exists somewhere on the host. Check if the context
             * is configured for this to be confirmed.
             */
            if (context.getValidateClientProvidedNewSessionId()) {
                boolean found = false;
                for (Container container : getHost().findChildren()) {
                    Manager m = ((Context) container).getManager();
                    if (m != null) {
                        try {
                            if (m.findSession(sessionId) != null) {
                                found = true;
                                break;
                            }
                        } catch (IOException e) {
                            // Ignore. Problems with this manager will be
                            // handled elsewhere.
                        }
                    }
                }
                if (!found) {
                    sessionId = null;
                }
            }
        } else {
            sessionId = null;
        }
        session = manager.createSession(sessionId);

        // Creating a new session cookie based on that session
        if (session != null && trackModesIncludesCookie) {
            Cookie cookie = ApplicationSessionCookieConfig.createSessionCookie(
                    context, session.getIdInternal(), isSecure());

            response.addSessionCookieInternal(cookie);
        }

        if (session == null) {
            return null;
        }

        session.access();
        return session;
    } 

4. 对于ServletResponse 参数的解析是在: org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver

package org.springframework.web.servlet.mvc.method.annotation;

import java.io.OutputStream;
import java.io.Writer;
import java.lang.reflect.Method;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

/**
 * Resolves response-related method argument values of types:
 * <ul>
 * <li>{@link ServletResponse}
 * <li>{@link OutputStream}
 * <li>{@link Writer}
 * </ul>
 *
 * @author Arjen Poutsma
 * @author Rossen Stoyanchev
 * @since 3.1
 */
public class ServletResponseMethodArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        Class<?> paramType = parameter.getParameterType();
        return (ServletResponse.class.isAssignableFrom(paramType) ||
                OutputStream.class.isAssignableFrom(paramType) ||
                Writer.class.isAssignableFrom(paramType));
    }

    /**
     * Set {@link ModelAndViewContainer#setRequestHandled(boolean)} to
     * {@code false} to indicate that the method signature provides access
     * to the response. If subsequently the underlying method returns
     * {@code null}, the request is considered directly handled.
     */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

        if (mavContainer != null) {
            mavContainer.setRequestHandled(true);
        }

        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        Class<?> paramType = parameter.getParameterType();

        if (ServletResponse.class.isAssignableFrom(paramType)) {
            Object nativeResponse = webRequest.getNativeResponse(paramType);
            if (nativeResponse == null) {
                throw new IllegalStateException(
                        "Current response is not of type [" + paramType.getName() + "]: " + response);
            }
            return nativeResponse;
        }
        else if (OutputStream.class.isAssignableFrom(paramType)) {
            return response.getOutputStream();
        }
        else if (Writer.class.isAssignableFrom(paramType)) {
            return response.getWriter();
        }
        else {
            // should not happen
            Method method = parameter.getMethod();
            throw new UnsupportedOperationException("Unknown parameter type: " + paramType + " in method: " + method);
        }
    }

}

5. 总结: 对于ServletRequest、ServletResponse 实际是从org.springframework.web.context.request.NativeWebRequest 获取的。org.springframework.web.context.request.NativeWebRequest 对象包装了ServletRequest 和 ServletResponse 对象。 

(1). org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod 这一步封装成 ServletWebRequest 对象:(也就是将request和response 封装起来进行后续参数的解析)

ServletWebRequest webRequest = new ServletWebRequest(request, response);

(2). org.springframework.web.context.request.ServletWebRequest 实际继承自ServletRequestAttributes, 实际ServletRequest、ServletResponse 也是维护在 ServletRequestAttributes

package org.springframework.web.context.request;

import java.security.Principal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.WebUtils;

/**
 * {@link WebRequest} adapter for an {@link javax.servlet.http.HttpServletRequest}.
 *
 * @author Juergen Hoeller
 * @author Brian Clozel
 * @author Markus Malkusch
 * @since 2.0
 */
public class ServletWebRequest extends ServletRequestAttributes implements NativeWebRequest {

    private static final String ETAG = "ETag";

    private static final String IF_MODIFIED_SINCE = "If-Modified-Since";

    private static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";

    private static final String IF_NONE_MATCH = "If-None-Match";

    private static final String LAST_MODIFIED = "Last-Modified";

    private static final List<String> SAFE_METHODS = Arrays.asList("GET", "HEAD");

    /**
     * Pattern matching ETag multiple field values in headers such as "If-Match", "If-None-Match"
     * @see <a href="https://tools.ietf.org/html/rfc7232#section-2.3">Section 2.3 of RFC 7232</a>
     */
    private static final Pattern ETAG_HEADER_VALUE_PATTERN = Pattern.compile("\\*|\\s*((W\\/)?(\"[^\"]*\"))\\s*,?");

    /**
     * Date formats as specified in the HTTP RFC
     * @see <a href="https://tools.ietf.org/html/rfc7231#section-7.1.1.1">Section 7.1.1.1 of RFC 7231</a>
     */
    private static final String[] DATE_FORMATS = new String[] {
            "EEE, dd MMM yyyy HH:mm:ss zzz",
            "EEE, dd-MMM-yy HH:mm:ss zzz",
            "EEE MMM dd HH:mm:ss yyyy"
    };

    private static TimeZone GMT = TimeZone.getTimeZone("GMT");

    /** Checking for Servlet 3.0+ HttpServletResponse.getHeader(String) */
    private static final boolean servlet3Present =
            ClassUtils.hasMethod(HttpServletResponse.class, "getHeader", String.class);

    private boolean notModified = false;


    /**
     * Create a new ServletWebRequest instance for the given request.
     * @param request current HTTP request
     */
    public ServletWebRequest(HttpServletRequest request) {
        super(request);
    }

    /**
     * Create a new ServletWebRequest instance for the given request/response pair.
     * @param request current HTTP request
     * @param response current HTTP response (for automatic last-modified handling)
     */
    public ServletWebRequest(HttpServletRequest request, HttpServletResponse response) {
        super(request, response);
    }


    @Override
    public Object getNativeRequest() {
        return getRequest();
    }

    @Override
    public Object getNativeResponse() {
        return getResponse();
    }

    @Override
    public <T> T getNativeRequest(Class<T> requiredType) {
        return WebUtils.getNativeRequest(getRequest(), requiredType);
    }

    @Override
    public <T> T getNativeResponse(Class<T> requiredType) {
        return WebUtils.getNativeResponse(getResponse(), requiredType);
    }

    /**
     * Return the HTTP method of the request.
     * @since 4.0.2
     */
    public HttpMethod getHttpMethod() {
        return HttpMethod.resolve(getRequest().getMethod());
    }

    @Override
    public String getHeader(String headerName) {
        return getRequest().getHeader(headerName);
    }

    @Override
    public String[] getHeaderValues(String headerName) {
        String[] headerValues = StringUtils.toStringArray(getRequest().getHeaders(headerName));
        return (!ObjectUtils.isEmpty(headerValues) ? headerValues : null);
    }

    @Override
    public Iterator<String> getHeaderNames() {
        return CollectionUtils.toIterator(getRequest().getHeaderNames());
    }

    @Override
    public String getParameter(String paramName) {
        return getRequest().getParameter(paramName);
    }

    @Override
    public String[] getParameterValues(String paramName) {
        return getRequest().getParameterValues(paramName);
    }

    @Override
    public Iterator<String> getParameterNames() {
        return CollectionUtils.toIterator(getRequest().getParameterNames());
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        return getRequest().getParameterMap();
    }

    @Override
    public Locale getLocale() {
        return getRequest().getLocale();
    }

    @Override
    public String getContextPath() {
        return getRequest().getContextPath();
    }

    @Override
    public String getRemoteUser() {
        return getRequest().getRemoteUser();
    }

    @Override
    public Principal getUserPrincipal() {
        return getRequest().getUserPrincipal();
    }

    @Override
    public boolean isUserInRole(String role) {
        return getRequest().isUserInRole(role);
    }

    @Override
    public boolean isSecure() {
        return getRequest().isSecure();
    }


    @Override
    public boolean checkNotModified(long lastModifiedTimestamp) {
        return checkNotModified(null, lastModifiedTimestamp);
    }

    @Override
    public boolean checkNotModified(String etag) {
        return checkNotModified(etag, -1);
    }

    @Override
    public boolean checkNotModified(String etag, long lastModifiedTimestamp) {
        HttpServletResponse response = getResponse();
        if (this.notModified || !isStatusOK(response)) {
            return this.notModified;
        }

        // Evaluate conditions in order of precedence.
        // See https://tools.ietf.org/html/rfc7232#section-6

        if (validateIfUnmodifiedSince(lastModifiedTimestamp)) {
            if (this.notModified) {
                response.setStatus(HttpStatus.PRECONDITION_FAILED.value());
            }
            return this.notModified;
        }

        boolean validated = validateIfNoneMatch(etag);

        if (!validated) {
            validateIfModifiedSince(lastModifiedTimestamp);
        }

        // Update response

        boolean isHttpGetOrHead = SAFE_METHODS.contains(getRequest().getMethod());
        if (this.notModified) {
            response.setStatus(isHttpGetOrHead ?
                    HttpStatus.NOT_MODIFIED.value() : HttpStatus.PRECONDITION_FAILED.value());
        }
        if (isHttpGetOrHead) {
            if(lastModifiedTimestamp > 0 && isHeaderAbsent(response, LAST_MODIFIED)) {
                response.setDateHeader(LAST_MODIFIED, lastModifiedTimestamp);
            }
            if (StringUtils.hasLength(etag) && isHeaderAbsent(response, ETAG)) {
                response.setHeader(ETAG, padEtagIfNecessary(etag));
            }
        }

        return this.notModified;
    }

    private boolean isStatusOK(HttpServletResponse response) {
        if (response == null || !servlet3Present) {
            // Can't check response.getStatus() - let's assume we're good
            return true;
        }
        return response.getStatus() == 200;
    }

    private boolean isHeaderAbsent(HttpServletResponse response, String header) {
        if (response == null || !servlet3Present) {
            // Can't check response.getHeader(header) - let's assume it's not set
            return true;
        }
        return (response.getHeader(header) == null);
    }

    private boolean validateIfUnmodifiedSince(long lastModifiedTimestamp) {
        if (lastModifiedTimestamp < 0) {
            return false;
        }
        long ifUnmodifiedSince = parseDateHeader(IF_UNMODIFIED_SINCE);
        if (ifUnmodifiedSince == -1) {
            return false;
        }
        // We will perform this validation...
        this.notModified = (ifUnmodifiedSince < (lastModifiedTimestamp / 1000 * 1000));
        return true;
    }

    private boolean validateIfNoneMatch(String etag) {
        if (!StringUtils.hasLength(etag)) {
            return false;
        }
        Enumeration<String> ifNoneMatch;
        try {
            ifNoneMatch = getRequest().getHeaders(IF_NONE_MATCH);
        }
        catch (IllegalArgumentException ex) {
            return false;
        }
        if (!ifNoneMatch.hasMoreElements()) {
            return false;
        }
        // We will perform this validation...
        etag = padEtagIfNecessary(etag);
        while (ifNoneMatch.hasMoreElements()) {
            String clientETags = ifNoneMatch.nextElement();

            Matcher eTagMatcher = ETAG_HEADER_VALUE_PATTERN.matcher(clientETags);
            // Compare weak/strong ETags as per https://tools.ietf.org/html/rfc7232#section-2.3
            while (eTagMatcher.find()) {
                if (StringUtils.hasLength(eTagMatcher.group())
                        && etag.replaceFirst("^W/", "").equals(eTagMatcher.group(3))) {
                    this.notModified = true;
                    break;
                }
            }
        }
        return true;
    }

    private String padEtagIfNecessary(String etag) {
        if (!StringUtils.hasLength(etag)) {
            return etag;
        }
        if ((etag.startsWith("\"") || etag.startsWith("W/\"")) && etag.endsWith("\"")) {
            return etag;
        }
        return "\"" + etag + "\"";
    }

    private boolean validateIfModifiedSince(long lastModifiedTimestamp) {
        if (lastModifiedTimestamp < 0) {
            return false;
        }
        long ifModifiedSince = parseDateHeader(IF_MODIFIED_SINCE);
        if (ifModifiedSince == -1) {
            return false;
        }
        // We will perform this validation...
        this.notModified = ifModifiedSince >= (lastModifiedTimestamp / 1000 * 1000);
        return true;
    }

    public boolean isNotModified() {
        return this.notModified;
    }

    private long parseDateHeader(String headerName) {
        long dateValue = -1;
        try {
            dateValue = getRequest().getDateHeader(headerName);
        }
        catch (IllegalArgumentException ex) {
            String headerValue = getHeader(headerName);
            // Possibly an IE 10 style value: "Wed, 09 Apr 2014 09:57:42 GMT; length=13774"
            int separatorIndex = headerValue.indexOf(';');
            if (separatorIndex != -1) {
                String datePart = headerValue.substring(0, separatorIndex);
                dateValue = parseDateValue(datePart);
            }
        }
        return dateValue;
    }

    private long parseDateValue(String headerValue) {
        if (headerValue == null) {
            // No header value sent at all
            return -1;
        }
        if (headerValue.length() >= 3) {
            // Short "0" or "-1" like values are never valid HTTP date headers...
            // Let's only bother with SimpleDateFormat parsing for long enough values.
            for (String dateFormat : DATE_FORMATS) {
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US);
                simpleDateFormat.setTimeZone(GMT);
                try {
                    return simpleDateFormat.parse(headerValue).getTime();
                }
                catch (ParseException ex) {
                    // ignore
                }
            }
        }
        return -1;
    }

    @Override
    public String getDescription(boolean includeClientInfo) {
        HttpServletRequest request = getRequest();
        StringBuilder sb = new StringBuilder();
        sb.append("uri=").append(request.getRequestURI());
        if (includeClientInfo) {
            String client = request.getRemoteAddr();
            if (StringUtils.hasLength(client)) {
                sb.append(";client=").append(client);
            }
            HttpSession session = request.getSession(false);
            if (session != null) {
                sb.append(";session=").append(session.getId());
            }
            String user = request.getRemoteUser();
            if (StringUtils.hasLength(user)) {
                sb.append(";user=").append(user);
            }
        }
        return sb.toString();
    }


    @Override
    public String toString() {
        return "ServletWebRequest: " + getDescription(true);
    }

}
View Code

  可以看到org.springframework.web.context.request.ServletWebRequest#getNativeRequest(java.lang.Class<T>) 方法实际会调用到WebUtils 相关方法,org.springframework.web.util.WebUtils#getNativeRequest 如下: 可以看到是如果是需要的类型就直接返回,否则判断是否是包装类,如果是包装类就调用包装类内部属性然后继续调用。

    public static <T> T getNativeRequest(ServletRequest request, Class<T> requiredType) {
        if (requiredType != null) {
            if (requiredType.isInstance(request)) {
                return (T) request;
            }
            else if (request instanceof ServletRequestWrapper) {
                return getNativeRequest(((ServletRequestWrapper) request).getRequest(), requiredType);
            }
        }
        return null;
    }

 

posted @ 2021-04-15 22:59  QiaoZhi  阅读(397)  评论(0编辑  收藏  举报