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个参数解析器,如下:

SpingMVC参数解析过程

主要的参数解析器为:

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 等参数解析过程

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

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

public class ServletRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {

    @Nullable
    private static Class<?> pushBuilder;

    static {
        try {
            pushBuilder = ClassUtils.forName("javax.servlet.http.PushBuilder",
                    ServletRequestMethodArgumentResolver.class.getClassLoader());
        }
        catch (ClassNotFoundException ex) {
            // Servlet 4.0 PushBuilder not found - not supported for injection
            pushBuilder = null;
        }
    }


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

    @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(
                        "Current request is not of type [" + paramType.getName() + "]: " + webRequest);
            }
            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));
    }

    private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {
        T nativeRequest = webRequest.getNativeRequest(requiredType);
        if (nativeRequest == null) {
            throw new IllegalStateException(
                    "Current request is not of type [" + requiredType.getName() + "]: " + webRequest);
        }
        return nativeRequest;
    }

    @Nullable
    private Object resolveArgument(Class<?> paramType, HttpServletRequest request) throws IOException {
        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 (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) {
            return PushBuilderDelegate.resolvePushBuilder(request, paramType);
        }
        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 (ZoneId.class == paramType) {
            TimeZone timeZone = RequestContextUtils.getTimeZone(request);
            return (timeZone != null ? timeZone.toZoneId() : ZoneId.systemDefault());
        }

        // Should never happen...
        throw new UnsupportedOperationException("Unknown parameter type: " + paramType.getName());
    }


    /**
     * Inner class to avoid a hard dependency on Servlet API 4.0 at runtime.
     */
    private static class PushBuilderDelegate {

        @Nullable
        public static Object resolvePushBuilder(HttpServletRequest request, Class<?> paramType) {
            PushBuilder pushBuilder = request.newPushBuilder();
            if (pushBuilder != null && !paramType.isInstance(pushBuilder)) {
                throw new IllegalStateException(
                        "Current push builder is not of type [" + paramType.getName() + "]: " + pushBuilder);
            }
            return pushBuilder;

        }
    }

}

  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. 对于ServletRequest 实际是从org.springframework.web.context.request.NativeWebRequest 获取的, org.springframework.web.context.request.NativeWebRequest 对象包装了ServletRequest 和 ServletResponse 对象。

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

ServletWebRequest webRequest = new ServletWebRequest(request, response);

 

相关文章:

  • 2021-05-22
  • 2021-07-01
  • 2021-08-08
  • 2022-12-23
  • 2021-11-22
  • 2022-12-23
  • 2021-11-12
  • 2022-12-23
猜你喜欢
  • 2021-11-15
  • 2021-10-02
  • 2021-11-18
  • 2021-06-01
  • 2021-05-24
相关资源
相似解决方案