spring实体类(POJO)参数的赋值(form表单)原理

10、实体类(POJO)参数的赋值(form表单)原理

10.1、原理解析

  1. 测试用例

    • 准备好两个实体类
    public class Person {
    private String name;
    private Integer age;
    private Pet pet;
    }
    public class Pet {
    private String name;
    private Integer age;
    }
    • html的form表单

      注意这个 宠物Pet对象的name不能乱写 必须要和 person中定义的名称一样 才可以

    <form action="/person" method="post">
    <input type="text" name="name" value="水三丫">
    <input type="text" name="age" value="18">
    <input type="text" name="pet.name" value="阿猫">
    <input type="text" name="pet.age" value="10">
    <input type="submit" value="Person">
    </form>
    • Controller请求代码
    @PostMapping("/person")
    public Map person(Person person){
    Map<String, Person> map = new HashMap<>();
    map.put("person",person);
    return map;
    }
  2. 源码Debug

    • 从DispatchServlet 的 doDispatch方法开始

      DispatchServlet类中的
      doDispatch方法
      //RequestMappingHandlerAdapter这个处理器
      HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    • 获取请求参数的处理器

      HandlerMethodArgumentResolverComposite类中的
      getArgumentResolver方法
      //总共27个处理器
      ServletModelAttributeMethodProcessor这个处理Pojo实体类的参数
    • 创建实体类构造器和赋值

      • 创造构造器
      ModelAttributeMethodProcessor类中的
      resolveArgument方法
      // Create attribute instance
      try {
      attribute = createAttribute(name, parameter,
      binderFactory, webRequest);
      }
      createAttribute方法 //反射获取空参构造器
      Constructor<?> ctor = BeanUtils.getResolvableConstructor(clazz);
      Constructor<?> ctor = BeanUtils.getResolvableConstructor(clazz);
      Object attribute = constructAttribute(ctor, attributeName, parameter,
      binderFactory, webRequest);

      • 属性绑定和验证
      ModelAttributeMethodProcessor类中的
      resolveArgument方法
      // Bean property binding and validation;
      // skipped in case of binding failure on construction.
      WebDataBinder binder = binderFactory.createBinder(webRequest, attribute,
      name);

      • 获取请求的参数
      ServletModelAttributeMethodProcessor类中订单
      bindRequestParameters方法
      servletBinder.bind(servletRequest);
      ServletRequestDataBinder类中的
      bind方法
      MutablePropertyValues mpvs = new
      ServletRequestParameterPropertyValues(request);
      WebUtils方法类中
      getParametersStartingWith方法
      Enumeration<String> paramNames = request.getParameterNames();
      Map<String, Object> params = new TreeMap<>();

      • 准备开始数据绑定

        ServletRequestDataBinder类中的
        bind方法
        doBind(mpvs);
        WebDataBinder类中的
        doBind方法
        super.doBind(mpvs);
        DataBinder类中的
        doBind方法
        applyPropertyValues(mpvs);
        AbstractPropertyAccessor类中的
        setPropertyValues方法
        try {
        setPropertyValue(pv);
        }

        第一步获取数据的绑定格式转换的处理器(因为浏览器以JSOn穿过了的都是字符串)需要格式转换

        TypeConverterDelegate类中的
        convertIfNecessary方法
        try {
        //传入的是请求的参数值 参数类型 需要转换的类型
        return (T) conversionService.convert(newValue, sourceTypeDesc,
        typeDescriptor);
        }
        GenericConversionService类中的
        convert方法
        GenericConverter converter = getConverter(sourceType, targetType);
        getConverter方法
        ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
        GenericConverter converter = this.converterCache.get(key);

        需要124个类型转换中寻找这次需要的

        需要的是:

        获取的转换器

        第二步转换数据

        GenericConversionService类中的
        convert方法
        Object result = ConversionUtils.invokeConverter(converter, source,
        sourceType,targetType);

        第三步开始赋值

        AbstractNestablePropertyAccessor类中的
        processLocalProperty方法
        ph.setValue(valueToApply);

        获取set方法

        BeanWrapperImpl类中的
        setValue方法
        Method writeMethod = (this.pd instanceof
        GenericTypeAwarePropertyDescriptor ?
        ((GenericTypeAwarePropertyDescriptor)
        this.pd).getWriteMethodForActualAccess() :
        this.pd.getWriteMethod());

        开始反射赋值

        BeanWrapperImpl类中的
        setValue方法
        writeMethod.invoke(getWrappedInstance(), value);

        赋值完成

10.2、定制化属性转换器

  1. 编写自定义配置转换规则

    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
    return new WebMvcConfigurer() {
    @Override
    public void addFormatters(FormatterRegistry registry) {
    registry.addConverter(new Converter<String, Pet>() {//增加一个转换器
    @Override
    public Pet convert(String source) {//实现这个接口的方法
    if(!StringUtils.isEmpty(source)){
    Pet pet = new Pet();
    String[] split = source.split(",");//以逗号为分隔符
    pet.setName(split[0]);
    pet.setAge(Integer.parseInt(split[1]));
    return pet;
    }
    return null;
    }
    });
    }
    };
    }

    lambda表达式写法

    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
    @Override
    public void addFormatters(FormatterRegistry registry) {
    registry.addConverter(String.class,Pet.class, source -> {
    if(!StringUtils.isEmpty(source)){
    Pet pet = new Pet();
    String[] split = source.split(",");
    pet.setName(split[0]);
    pet.setAge(Integer.parseInt(split[1]));
    return pet;
    }
    return null;
    });
    }
    };
    }
  2. 测试用例

    html页面form表单代码

    <form action="/person1" method="post">
    <input type="text" name="name" value="水三丫">
    <input type="text" name="age" value="18">
    <input type="text" name="pet" value="阿猫,10">
    <input type="submit" value="Person1">
    </form>
  3. Debug测试

    测试的时候就会在全部转换器中找到我们定制的哪一个

posted @   水三丫  阅读(646)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示