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


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

    1. @GetMapping("obj")
    2. public void obj(DataDto dataDto){
    3. System.out.println(dataDto.getName()+"-"+dataDto.getValue());
    4. }
    5. http://localhost:9999/obj?name=1&value=2
    6. 1-2

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

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

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

    1. resolvers.add(new ServletModelAttributeMethodProcessor(false));
    2. resolvers.add(new RequestResponseBodyMethodProcessor(this.getMessageConverters(), this.requestResponseBodyAdvice));
    3. resolvers.add(new RequestPartMethodArgumentResolver(this.getMessageConverters(), this.requestResponseBodyAdvice));
    4. ....
    5. ....
    6. resolvers.add(new ServletModelAttributeMethodProcessor(true));

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

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

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

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

    1. public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor {
    2. public ServletModelAttributeMethodProcessor(boolean annotationNotRequired) {
    3. super(annotationNotRequired);
    4. }
    5. //父类构造,传入 true表示 方法参数 没有 类似 @RequestParam、@PathVariable 的注解
    6. public ModelAttributeMethodProcessor(boolean annotationNotRequired) {
    7. this.annotationNotRequired = annotationNotRequired;
    8. }
    9. public boolean supportsParameter(MethodParameter parameter) {
    10. return parameter.hasParameterAnnotation(ModelAttribute.class) || this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType());
    11. }

    所以ServletModelAttributeMethodProcessor的解析处理

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

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

    1. public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    2. Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
    3. Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
    4. String name = ModelFactory.getNameForParameter(parameter);
    5. ModelAttribute ann = (ModelAttribute)parameter.getParameterAnnotation(ModelAttribute.class);
    6. if (ann != null) {
    7. mavContainer.setBinding(name, ann.binding());
    8. }
    9. Object attribute = null;
    10. BindingResult bindingResult = null;
    11. if (mavContainer.containsAttribute(name)) {
    12. attribute = mavContainer.getModel().get(name);
    13. } else {
    14. try {
    15. attribute = this.createAttribute(name, parameter, binderFactory, webRequest);
    16. } catch (BindException var10) {
    17. if (this.isBindExceptionRequired(parameter)) {
    18. throw var10;
    19. }
    20. if (parameter.getParameterType() == Optional.class) {
    21. attribute = Optional.empty();
    22. } else {
    23. attribute = var10.getTarget();
    24. }
    25. bindingResult = var10.getBindingResult();
    26. }
    27. }
    28. if (bindingResult == null) {
    29. //根据api的对象入参 生成 databinder
    30. //其中的属性target就是 controller中 api的入参对象,等待后续属性填充
    31. WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
    32. if (binder.getTarget() != null) {
    33. if (!mavContainer.isBindingDisabled(name)) {
    34. //将request中的querystring 与 binder中的 target 入参对象的属性进行绑定
    35. //这个方法调用完成后,其实binder中的target属性 已经填充完毕了
    36. this.bindRequestParameters(binder, webRequest);
    37. }
    38. this.validateIfApplicable(binder, parameter);
    39. if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) {
    40. throw new BindException(binder.getBindingResult());
    41. }
    42. }
    43. if (!parameter.getParameterType().isInstance(attribute)) {
    44. attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
    45. }
    46. //通过binder生成 bindingresult,其中包含了 target入参对象等
    47. bindingResult = binder.getBindingResult();
    48. }
    49. //获取target对象,生成map,key=target对象类名,value=target对象
    50. Map bindingResultModel = bindingResult.getModel();
    51. mavContainer.removeAttributes(bindingResultModel);
    52. mavContainer.addAllAttributes(bindingResultModel);
    53. return attribute;
    54. }

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

  • 相关阅读:
    Redis命令手册
    Meta&伯克利基于池化自注意力机制提出通用多尺度视觉Transformer,在ImageNet分类准确率达88.8%!开源...
    java中的DTO
    Vue框架--Vue条件渲染
    Apache反向代理的功能和設置
    ts随记:面向对象、泛型
    成为比开发更硬气的测试人,我都经历了什么?
    js修改函数this指向的三种常用方法总结
    吃透 Spring 系列—AOP部分
    什么c++流行造轮子而不是调包侠?
  • 原文地址:https://blog.csdn.net/z69183787/article/details/126425735