偶然发现这样也可以接收对象类型的入参,代码如下
- @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
bindingResultModel = bindingResult.getModel(); - mavContainer.removeAttributes(bindingResultModel);
- mavContainer.addAllAttributes(bindingResultModel);
- return attribute;
- }
至此,request请求中的querystring参数已通过绑定装配 变成了 API 的入参对象,后续进行实际的方法调用