Springboot @GetMapping 自动接收对象参数源码分析

Posted OkidoGreen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Springboot @GetMapping 自动接收对象参数源码分析相关的知识,希望对你有一定的参考价值。

偶然发现这样也可以接收对象类型的入参,代码如下

 @GetMapping("obj")
    public void obj(DataDto dataDto)
        System.out.println(dataDto.getName()+"-"+dataDto.getValue());
    

http://localhost:9999/obj?name=1&value=2

1-2

springboot中会在启动时 加载许多 argumentResolver 和 returnValueHandler ,可以自行查看

RequestMappingHandlerAdapter # getDefaultArgumentResolvers 、getDefaultReturnValueHandlers方法,这里不再赘述,核心问题是 到底是哪个HandlerMethodArgumentResolver处理了上述代码中的自动填充逻辑;

根据debug调试最终发现在 getDefaultArgumentResolvers  的最后的兜底策略中进行了处理:

resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(this.getMessageConverters(), this.requestResponseBodyAdvice));
        resolvers.add(new RequestPartMethodArgumentResolver(this.getMessageConverters(), this.requestResponseBodyAdvice));
....
....
resolvers.add(new ServletModelAttributeMethodProcessor(true));

看一下这个类的源码,首先是他的构造函数,对比上面的DefaultArgumentResolvers,可以明

显地看到 ServletModelAttributeMethodProcessor 其实被添加了2次,同时结合 supportsParameter  方法,可以明显的发现:

第一次构造参数是false,专门用来处理@ModelAttribute注解

第二次是true,专门用来处理 入参 前面没有 注解的情况:

public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor 
    public ServletModelAttributeMethodProcessor(boolean annotationNotRequired) 
        super(annotationNotRequired);
    

//父类构造,传入 true表示 方法参数 没有 类似 @RequestParam、@PathVariable 的注解
public ModelAttributeMethodProcessor(boolean annotationNotRequired) 
        this.annotationNotRequired = annotationNotRequired;
    

    public boolean supportsParameter(MethodParameter parameter) 
        return parameter.hasParameterAnnotation(ModelAttribute.class) || this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType());
    

所以ServletModelAttributeMethodProcessor的解析处理

其实都是依靠父类 ModelAttributeMethodProcessor 来解决的,

接下来看核心的  resolveArgument 方法,看一下如何转换为 我们 Controller 中 API的入参对象:

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();
                 else 
                    attribute = var10.getTarget();
                

                bindingResult = var10.getBindingResult();
            
        

        if (bindingResult == null) 
            //根据api的对象入参 生成 databinder
            //其中的属性target就是 controller中 api的入参对象,等待后续属性填充
            WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
            if (binder.getTarget() != null) 
                if (!mavContainer.isBindingDisabled(name)) 
                    //将request中的querystring 与 binder中的 target 入参对象的属性进行绑定
                    //这个方法调用完成后,其实binder中的target属性 已经填充完毕了
                    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);
            

            //通过binder生成 bindingresult,其中包含了 target入参对象等
            bindingResult = binder.getBindingResult();
        

        //获取target对象,生成map,key=target对象类名,value=target对象
        Map<String, Object> bindingResultModel = bindingResult.getModel();
        mavContainer.removeAttributes(bindingResultModel);
        mavContainer.addAllAttributes(bindingResultModel);
        return attribute;
    

至此,request请求中的querystring参数已通过绑定装配 变成了 API 的入参对象,后续进行实际的方法调用

以上是关于Springboot @GetMapping 自动接收对象参数源码分析的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot中@GetMapping参数接收理解

SpringBoot的映射(@RequestMapping@GetMapping与@PostMapping)

SpringBoot 中常用注解@PathVaribale/@RequestParam/@GetMapping介绍

Spring Boot:以 Pageable 作为请求参数的 @GetMapping 无法按预期工作

利用注解@getmapping怎样传递数据url中的值

Spring Boot - GetMapping 到具有不同路径的相同方法的更简单方法