自学内容网 自学内容网

springboot系列--web相关知识探索三

一、前言

web相关知识探索二中研究了请求是如何映射到具体接口(方法)中的,本次文章主要研究请求中所带的参数是如何映射到接口参数中的,也即请求参数如何与接口参数绑定。主要有四种、分别是注解方式、Servlet API方式、复杂参数、以及自定义对象参数。本次主要研究注解方式以及Servlet API方式。

二、 注解方式

接口参数绑定主要涉及的一下注解:

@PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@CookieValue、@RequestBody、@MatrixVariable

其中@MatrixVariable矩阵变量注解实际开发中基本不用,跳过研究。主要研究一下springmvc是如何将请求中的参数绑定到下面一个个注解修饰的参数中的。

一、测试用例 

@RestController
public class MvcTestController {

    /**
     *
     * @param id
     * @param name
     * @param pv 这个map可以接受url上的所有值,
     *           key是id;value:url路径上的值
     *           key是name;value:
     * @param userAgent
     * @param header
     * @param age
     * @param list
     * @param params
     * @param _ga
     * @param cookie
     * @return
     */

    @GetMapping("/demo/{id}/test/{name}")
    public Map<String,Object> test(@PathVariable("id") Integer id,
                                     @PathVariable("name") String name,
                                     @PathVariable Map<String,String> pv,
                                     @RequestHeader("User-Agent") String userAgent,
                                     @RequestHeader Map<String,String> header,
                                     @RequestParam("age") Integer age,
                                     @RequestParam("list") List<String> list,
                                     @RequestParam Map<String,String> params,
                                     @CookieValue("_ga") String _ga,
                                     @CookieValue("_ga") Cookie cookie){


        Map<String,Object> map = new HashMap<>();

        map.put("id",id);
        map.put("name",name);
        map.put("pv",pv);
        map.put("userAgent",userAgent);
        map.put("headers",header);
        map.put("age",age);
        map.put("list",list);
        map.put("params",params);
        map.put("_ga",_ga);
        return map;
    }


    @GetMapping("/test/servlet")
    public Map testServlet(HttpServletRequest request){
        Map<String,Object> map = new HashMap<>();
        map.put("request",request);
        return map;
    }


    @PostMapping("/test/method")
    public Map testPostMethod(@RequestBody String content){
        Map<String,Object> map = new HashMap<>();
        map.put("content",content);
        return map;
    }
}

首先,请求进来统一是有前端控制器(中央处理器),也即是DispatcherServlet这个类进行处理。然后中央处理器就会去调用处理器映射器,也即是handlerMapping,handlerMapping通过请求方式以及请求路径找到匹配的handler(也就是我们常说的接口,也是具体的处理方法),之后DispatcherServlet调用处理器适配器,也就是handlerAdatper根据具体的handler规则去执行对应的handler。

二·、原理

get请求:http://localhost:8080/demo/1/test/zhangsan,进入到上一章研究到的,通过handlerMapping获取到具体的handler地方。

1、HandlerMapping中找到能处理请求的Handler(Controller.method())

2、然后为当前Handler 找一个适配器 HandlerAdapter,这个适配器一般就是RequestMappingHandlerAdapter

3、适配器执行目标方法并确定方法参数的每一个值

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                processedRequest = this.checkMultipart(request);
                multipartRequestParsed = processedRequest != request;
                // 这里就是上一张研究到获取合适的handler的地方
                mappedHandler = this.getHandler(processedRequest);
                if (mappedHandler == null) {
                    this.noHandlerFound(processedRequest, response);
                    return;
                }
// 这里就是获取到合适的处理器适配器的地方,源码在下方
                HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
       
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
                   // 这里是利用处理器适配器执行具体的handler     
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                this.applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception var20) {
                dispatchException = var20;
            } catch (Throwable var21) {
                dispatchException = new NestedServletException("Handler dispatch failed", var21);
            }

            this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
        } catch (Exception var22) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
        } catch (Throwable var23) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
        }

    } finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        } else if (multipartRequestParsed) {
            this.cleanupMultipart(processedRequest);
        }

    }
}

一、找到合适的处理器适配器 

// 找到合适的HandlerAdapter 
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    // 遍历所有的处理器适配器,直到找到合适的 ,总共会有4种   
    if (this.handlerAdapters != null) {
            Iterator var2 = this.handlerAdapters.iterator();

            while(var2.hasNext()) {
                HandlerAdapter adapter = (HandlerAdapter)var2.next();
                // 这里的handler刚好就是HandlerMethod 对象,具体可以查看上一张获取handler。
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
        }

        throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }



// 判断是否支持当前handler,
    public final boolean supports(Object handler) {
        return handler instanceof HandlerMethod && this.supportsInternal((HandlerMethod)handler);
    }

 

0--支持方法上标注@RequestMapping、@GetMpping等注解的处理器适配器

1--支持函数式编程的

 二、利用处理器适配器执行具体的handler

从mv = ha.handle(processedRequest, response, mappedHandler.getHandler());方法进入

 

 一、参数解析器--HandlerMethodArgumentResolver

1、参数解析器主要是确定将要执行的目标方法的每一个参数的值是什么

2、SpringMVC目标方法能写多少种参数类型。取决于参数解析器

例如:目标方法(controller中的方法,也即是具体接口)使用了@RequestParam注解,springmvc就会使用RequestParamMethodArgumentResolvers这个参数解析器进行解析,,其他注解同理,会用其他解析器解析。

// 这是参数解析器接口,具体实现有27种,
public interface HandlerMethodArgumentResolver {
    // 当前解析器是否支持解析这种参数
    boolean supportsParameter(MethodParameter var1);

    // 支持就调用 resolveArgument
    @Nullable
    Object resolveArgument(MethodParameter var1, @Nullable ModelAndViewContainer var2, NativeWebRequest var3, @Nullable WebDataBinderFactory var4) throws Exception;
}
二、返回值处理器--HandlerMethodReturnValueHandler

返回值处理器主要就是处理返回值类型的,有多少种返回值处理器就能处理多少种返回值类型。这期主要研究参数解析器,返回值处理器另外再研究。

public interface HandlerMethodReturnValueHandler {
    boolean supportsReturnType(MethodParameter var1);

    void handleReturnValue(@Nullable Object var1, MethodParameter var2, ModelAndViewContainer var3, NativeWebRequest var4) throws Exception;
}
 三、将参数解析器、返回值处理器包装到执行handler对象中

    @Nullable
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ServletWebRequest webRequest = new ServletWebRequest(request, response);

        ModelAndView var15;
        try {
            WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);
            ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);
            ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
            if (this.argumentResolvers != null) {
                // invocableMethod这个对象就是执行具体的handler的,把参数解析器包装进去
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }
// invocableMethod这个对象就是执行具体的handler的,把返回值处理器包装进去
            if (this.returnValueHandlers != null) {
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }

            invocableMethod.setDataBinderFactory(binderFactory);
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
            Object result;
            if (asyncManager.hasConcurrentResult()) {
                result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                LogFormatUtils.traceDebug(this.logger, (traceOn) -> {
                    String formatted = LogFormatUtils.formatValue(result, !traceOn);
                    return "Resume with async result [" + formatted + "]";
                });
                // 执行具体的handler,也就是controller中的接口方法
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }

            invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
            if (asyncManager.isConcurrentHandlingStarted()) {
                result = null;
                return (ModelAndView)result;
            }

            var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);
        } finally {
            webRequest.requestCompleted();
        }

        return var15;
    }

四、真正执行目标方法

 真正执行目标方法是在这个类ServletInvocableHandlerMethod里面的invokeAndHandle方法里的       

Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);这个方法

真正执行目标方法有三个重要的步骤:

1、将目标方法中的参数与请求进来的参数进行绑定以及判断是否能否绑定等等。也即是对请求参数进行解析,这里就需要用到参数解析器了

2、通过反射调用目标方法进行执行

3、将目标方法执行后的数据绑定,也就是对返回值进行处理,这里就用到返回值处理器了

    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        //这里是真正执行目标方法,执行这个方法,就会到controller里面的接口方法,下图可见
        Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
        this.setResponseStatus(webRequest);
        if (returnValue == null) {
            if (this.isRequestNotModified(webRequest) || this.getResponseStatus() != null || mavContainer.isRequestHandled()) {
                this.disableContentCachingIfNecessary(webRequest);
                mavContainer.setRequestHandled(true);
                return;
            }
        } else if (StringUtils.hasText(this.getResponseStatusReason())) {
            mavContainer.setRequestHandled(true);
            return;
        }

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

        try {
            this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
        } catch (Exception var6) {
            if (logger.isTraceEnabled()) {
                logger.trace(this.formatErrorForReturnValue(returnValue), var6);
            }

            throw var6;
        }
    }

// 执行目标方法源码       
@Nullable
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
  // 这里就是将请求参数与目标方法中的参数进行绑定
      Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }
        // 通过反射执行目标方法
        return this.doInvoke(args);
    }

一、将请求参数与目标方法上的参数进行绑定

1、判断是否支持解析当前参数,如果有一个不支持就会报错

// 这段代码主要就是确定  controllerr中具体接口方法上的参数的值。也就是把请求参数的值与目标方法参数进行绑定
  protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        // 这里就是获取controllerr中具体接口方法上的参数、以及对应的类型,参数的位置等等,也就是获取参数声明信息
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 (logger.isDebugEnabled()) {
                            String exMsg = var10.getMessage();
                            if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                                logger.debug(formatArgumentError(parameter, exMsg));
                            }
                        }

                        throw var10;
                    }
                }
            }

            return args;
        }
    }




// 判断解析器是否支持当前参数类型的解析,解析器上面有讲过,有27中解析器
    public boolean supportsParameter(MethodParameter parameter) {
        return this.getArgumentResolver(parameter) != null;
    }




@Nullable
    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
// this.argumentResolverCache刚进来的时候是空的
        HandlerMethodArgumentResolver result = (HandlerMethodArgumentResolver)this.argumentResolverCache.get(parameter);
        
if (result == null) {
// 第一次是空的,会进到这里,然后挨个遍历27个解析器
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;
    }





// 判断解析器是否支持当前参数解析,这里只拿PathVariable注解解析器源码举例
    public boolean supportsParameter(MethodParameter parameter) {
// 判断当前参数是否使用了PathVariable注解,没使用就不支持解析
        if (!parameter.hasParameterAnnotation(PathVariable.class)) {
            return false;
// 到了这里就说明当前参数一定是PathVariable注解修饰的,然后判断是不是map,如果不是map那么就能够支持
        } else if (!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
            return true;
        } else {
// 到这里就说明参数是map类型的,然后还需要做其他判断,也就是PathVariable注解中的value一定要有值,如果没有值,就说明这个注解的map想要全部接受url
// 上的参数,这种就需要用另外一个解析器进行处理了,如果有值就说明url上的参数是map类型的,名字是PathVariable上的value属性的值。然后会将这个mapp参数值映射到
// 这个参数上
            PathVariable pathVariable = (PathVariable)parameter.getParameterAnnotation(PathVariable.class);
            return pathVariable != null && StringUtils.hasText(pathVariable.value());
        }
    }

2、通过了参数解析判断后,将进行真正的参数解析。 

   
// 这里主要是进行参数解析这个方法是在HandlerMethodArgumentResolverComposite这个类下
   @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);
        }
    }



// 这里是拿到这个参数对应的解析器,例如是@PathVariable注解修饰的参数,那就会拿到@PathVariable的参数解析器
    @Nullable
    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;
    }





// 真正开始解析参数,这个方法是在AbstractNamedValueMethodArgumentResolver这个类里面,应该是一个公共方法,类似于模板方法的公共方法,处理公共逻辑
@Nullable
    public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 这里将注解信息封装成一个对象,例如注解里标识这个参数以什么样的名字绑定,是否是必须得,以及默认值。例如:@PathVariable(value = "id",required = false)这里面的属性
   NamedValueInfo namedValueInfo = this.getNamedValueInfo(parameter);
        MethodParameter nestedParameter = parameter.nestedIfOptional();
// 这里就是从封装对象解析出接口方法要绑定的名字,例如这个注解@PathVariable("id"),解析出来就是id这个名字
        Object resolvedName = this.resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
        if (resolvedName == null) {
            throw new IllegalArgumentException("Specified name must not resolve to null: [" + namedValueInfo.name + "]");
        } else {
// 这里开始将url上对应的值与id这个名字进行绑定,也即是对id进行赋值,这里将参数解析绑定后,下面逻辑可以不用看了
            Object arg = this.resolveName(resolvedName.toString(), nestedParameter, webRequest);
            if (arg == null) {
                if (namedValueInfo.defaultValue != null) {
                    arg = this.resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
                } else if (namedValueInfo.required && !nestedParameter.isOptional()) {
                    this.handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
                }

                arg = this.handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
            } else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
                arg = this.resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
            }

            if (binderFactory != null) {
                WebDataBinder binder = binderFactory.createBinder(webRequest, (Object)null, namedValueInfo.name);

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

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

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



// 获取参数注解信息
    private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
// 先从缓存中获取
        NamedValueInfo namedValueInfo = (NamedValueInfo)this.namedValueInfoCache.get(parameter);
        if (namedValueInfo == null) {
// 这个方法是从注解中获取注解属性信息
            namedValueInfo = this.createNamedValueInfo(parameter);
// 这里其实就是重新更新一下namedValueInfo值中的defaultValue属性。
            namedValueInfo = this.updateNamedValueInfo(parameter, namedValueInfo);
            this.namedValueInfoCache.put(parameter, namedValueInfo);
        }

        return namedValueInfo;
    }


 // 从注解中获取注解属性信息,拿PathVariable注解举例
    protected AbstractNamedValueMethodArgumentResolver.NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
// 这里获取的是一个注解
        PathVariable ann = (PathVariable)parameter.getParameterAnnotation(PathVariable.class);
        Assert.state(ann != null, "No PathVariable annotation");
// 这里会把注解传入这个对象,里面会把属性保存到这个对象中
        return new PathVariableNamedValueInfo(ann);
    }



// 把属性保存到这个对象中
    private static class PathVariableNamedValueInfo extends AbstractNamedValueMethodArgumentResolver.NamedValueInfo {
        public PathVariableNamedValueInfo(PathVariable annotation) {
// 父类NamedValueInfo
            super(annotation.name(), annotation.required(), "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n");
        }
    }




    protected static class NamedValueInfo {
        private final String name;
        private final boolean required;
        @Nullable
        private final String defaultValue;

        public NamedValueInfo(String name, boolean required, @Nullable String defaultValue) {
            this.name = name;
            this.required = required;
            this.defaultValue = defaultValue;
        }
    }





// 这里是对id进行赋值的源码,name:就是接口方法中使用注解属性的值,也就是@PathVariable("id")里面的id
    @Nullable
    protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
/**
*HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE的值为org.springframework.web.servlet.HandlerMapping.uriTemplateVariables
*request请求信息里面在org.springframework.web.servlet.HandlerMapping.uriTemplateVariables这个key下,保存了url上的请求参数
*从请求中获取到数据后,保存在map当中,其中key为:注解属性的id,value为请求携带的值,就拿@PathVariable("id"),
这个注解来说吧,key-id,value-请求请来的值。(@GetMapping("/demo/{id}/test/{name}")是从这里面取到的id、name为key)这里是由于请求一进来会有一个UrlPathHelper类里面的方法将url里面的路径变量全部解析出来
*然后提前保存到请求域当中。所以uriTemplateVars是请求域中的值,但是并未绑定到接口参数上,
*/
        Map<String, String> uriTemplateVars = (Map)request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, 0);
// 这里就是从请求域中的值里面获取到id对应的value        
   return uriTemplateVars != null ? uriTemplateVars.get(name) : null;
    }

 

三、Servlet API方式 

当我们的接口参数是WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId这些类型时,springmvc会有专门的处理器进行处理。也就是ServletRequestMethodArgumentResolver 这个处理器。主要源码如下。

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

一、判断是否支持HttpservletRequest类型参数

 

 二、解析出参数


原文地址:https://blog.csdn.net/weixin_42972832/article/details/142682891

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!