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

 @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 的入参对象,后续进行实际的方法调用

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐