spring原理(第十五天)

对象绑定与类型转换

底层第一套转换接口与实现

  • Printer 把其它类型转为 String

  • Parser 把 String 转为其它类型

  • Formatter 综合 Printer 与 Parser 功能

  • Converter 把类型 S 转为类型 T

  • Printer、Parser、Converter 经过适配转换成 GenericConverter 放入 Converters 集合

  • FormattingConversionService 利用其它们实现转换

底层第二套转换接口

  • PropertyEditor 把 String 与其它类型相互转换

  • PropertyEditorRegistry 可以注册多个 PropertyEditor 对象

  • 与第一套接口直接可以通过 FormatterPropertyEditorAdapter 来进行适配

高层接口与实现

  • 它们都实现了 TypeConverter 这个高层转换接口,在转换时,会用到 TypeConverter Delegate 委派ConversionService 与 PropertyEditorRegistry 真正执行转换(Facade 门面模式)

    • 首先看是否有自定义转换器, @InitBinder 添加的即属于这种 (用了适配器模式把 Formatter 转为需要的 PropertyEditor)

    • 再看有没有 ConversionService 转换

    • 再利用默认的 PropertyEditor 转换

    • 最后有一些特殊处理

  • SimpleTypeConverter 仅做类型转换

  • BeanWrapperImpl 为 bean 的属性赋值,当需要时做类型转换,走 Property

  • DirectFieldAccessor 为 bean 的属性赋值,当需要时做类型转换,走 Field

  • ServletRequestDataBinder 为 bean 的属性执行绑定,当需要时做类型转换,根据 directFieldAccess 选择走 Property 还是 Field,具备校验与获取校验结果功能

类型转换与数据绑定

基本的类型转换与数据绑定用法

  • SimpleTypeConverter

    public class TestSimpleConverter {
    public static void main(String[] args) {
    // 仅有类型转换的功能
    SimpleTypeConverter typeConverter = new SimpleTypeConverter();
    Integer number = typeConverter.convertIfNecessary("13", int.class);
    Date date = typeConverter.convertIfNecessary("1999/03/04", Date.class);
    System.out.println(number);
    System.out.println(date);
    }
    }

  • BeanWrapperImpl(通过set,get方法来给属性赋值)

    public class TestBeanWrapper {
    public static void main(String[] args) {
    // 利用反射原理, 为 bean 的属性赋值
    MyBean target = new MyBean();
    BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
    wrapper.setPropertyValue("a", "10");
    wrapper.setPropertyValue("b", "hello");
    wrapper.setPropertyValue("c", "1999/03/04");
    System.out.println(target);
    }

    复制代码
      static class MyBean {
          private int a;
          private String b;
          private Date c;
    
          public int getA() {
              return a;
          }
    
          public void setA(int a) {
              this.a = a;
          }
    
          public String getB() {
              return b;
          }
    
          public void setB(String b) {
              this.b = b;
          }
    
          public Date getC() {
              return c;
          }
    
          public void setC(Date c) {
              this.c = c;
          }
    
          @Override
          public String toString() {
              return "MyBean{" +
                     "a=" + a +
                     ", b='" + b + '\'' +
                     ", c=" + c +
                     '}';
          }
      }

    }

  • DirectFieldAccessor(直接通过属性赋值)

    public class TestFieldAccessor {
    public static void main(String[] args) {
    // 利用反射原理, 为 bean 的属性赋值
    MyBean target = new MyBean();
    DirectFieldAccessor accessor = new DirectFieldAccessor(target);
    accessor.setPropertyValue("a", "10");
    accessor.setPropertyValue("b", "hello");
    accessor.setPropertyValue("c", "1999/03/04");
    System.out.println(target);
    }

    复制代码
      static class MyBean {
          private int a;
          private String b;
          private Date c;
          @Override
          public String toString() {
              return "MyBean{" +
                     "a=" + a +
                     ", b='" + b + '\'' +
                     ", c=" + c +
                     '}';
          }
      }

    }

  • ServletRequestDataBinder

    public class TestDataBinder {

    复制代码
      public static void main(String[] args) {
          // 执行数据绑定
          MyBean target = new MyBean();
          DataBinder dataBinder = new DataBinder(target);
          dataBinder.initDirectFieldAccess();
          MutablePropertyValues pvs = new MutablePropertyValues();
          pvs.add("a", "10");
          pvs.add("b", "hello");
          pvs.add("c", "1999/03/04");
          dataBinder.bind(pvs);
          System.out.println(target);
      }
    
      static class MyBean {
          private int a;
          private String b;
          private Date c;
    
          @Override
          public String toString() {
              return "MyBean{" +
                     "a=" + a +
                     ", b='" + b + '\'' +
                     ", c=" + c +
                     '}';
          }
      }

    }

数据绑定工厂

ServletRequestDataBinderFactory 的用法和扩展点

  1. 可以解析控制器的 @InitBinder 标注方法作为扩展点,添加自定义转换器

    • 控制器私有范围
  2. 可以通过 ConfigurableWebBindingInitializer 配置 ConversionService 作为扩展点,添加自定义转换器

    • 公共范围
  3. 同时加了 @InitBinder 和 ConversionService 的转换优先级

    1. 优先采用 @InitBinder 的转换器

    2. 其次使用 ConversionService 的转换器

    3. 使用默认转换器

    4. 特殊处理(例如有参构造)

    public class TestServletDataBinderFactory {
    public static void main(String[] args) throws Exception {
    MockHttpServletRequest request = new MockHttpServletRequest();
    request.setParameter("birthday", "1999|01|02");
    request.setParameter("address.name", "西安");

    复制代码
         User target = new User();
         // "1. 用工厂, 无转换功能"

    // ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
    // "2. 用 @InitBinder 转换" PropertyEditorRegistry PropertyEditor
    // InvocableHandlerMethod method = new InvocableHandlerMethod(new MyController(), MyController.class.getMethod("aaa", WebDataBinder.class));
    // ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(List.of(method), null);
    // "3. 用 ConversionService 转换" ConversionService Formatter
    // FormattingConversionService service = new FormattingConversionService();
    // service.addFormatter(new MyDateFormatter("用 ConversionService 方式扩展转换功能"));
    // ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
    // initializer.setConversionService(service);
    // ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, initializer);
    // "4. 同时加了 @InitBinder 和 ConversionService"
    // InvocableHandlerMethod method = new InvocableHandlerMethod(new MyController(), MyController.class.getMethod("aaa", WebDataBinder.class));
    //
    // FormattingConversionService service = new FormattingConversionService();
    // service.addFormatter(new MyDateFormatter("用 ConversionService 方式扩展转换功能"));
    // ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
    // initializer.setConversionService(service);
    //
    // ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(List.of(method), initializer);
    // "5. 使用默认 ConversionService 转换"
    ApplicationConversionService service = new ApplicationConversionService();
    ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
    initializer.setConversionService(service);

    复制代码
         ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, initializer);
    
         WebDataBinder dataBinder = factory.createBinder(new ServletWebRequest(request), target, "user");
         dataBinder.bind(new ServletRequestParameterPropertyValues(request));
         System.out.println(target);
     }
    
     static class MyController {
         @InitBinder
         public void aaa(WebDataBinder dataBinder) {
             // 扩展 dataBinder 的转换器
             dataBinder.addCustomFormatter(new MyDateFormatter("用 @InitBinder 方式扩展的"));
         }
     }
    
     public static class User {
         @DateTimeFormat(pattern = "yyyy|MM|dd")
         private Date birthday;
         private Address address;
    
         public Address getAddress() {
             return address;
         }
    
         public void setAddress(Address address) {
             this.address = address;
         }
    
         public Date getBirthday() {
             return birthday;
         }
    
         public void setBirthday(Date birthday) {
             this.birthday = birthday;
         }
    
         @Override
         public String toString() {
             return "User{" +
                    "birthday=" + birthday +
                    ", address=" + address +
                    '}';
         }
     }
    
     public static class Address {
         private String name;
    
         public String getName() {
             return name;
         }
    
         public void setName(String name) {
             this.name = name;
         }
    
         @Override
         public String toString() {
             return "Address{" +
                    "name='" + name + '\'' +
                    '}';
         }
     }

    }

在spring中使用ApplicationConversionService来对类型进行转换,无法识别的日期参数加注解 @DateTimeFormat,在jdk中使用DefaultConversionService,他也需要配合注解进行使用,

在这些默认的转换其中,并不需要 @InitBinder 转换ConversionService 转换。

获取泛型参数

  1. java api 获取泛型参数

  2. spring api 获取泛型参数

    public class TestGenericType {
    public static void main(String[] args) {
    // 小技巧
    // 1. java api
    System.out.println(">>>>>>>>>>>>>>>>>>>>>>>");
    Type type = TeacherDao.class.getGenericSuperclass();
    System.out.println(type);

    复制代码
         if (type instanceof ParameterizedType parameterizedType) {
             System.out.println(parameterizedType.getActualTypeArguments()[0]);
         }
    
         // 2. spring api 1
         System.out.println(">>>>>>>>>>>>>>>>>>>>>>>");
         Class<?> t = GenericTypeResolver.resolveTypeArgument(TeacherDao.class, BaseDao.class);
         System.out.println(t);
    
         // 3. spring api 2
         System.out.println(">>>>>>>>>>>>>>>>>>>>>>>");
         System.out.println(ResolvableType.forClass(TeacherDao.class).getSuperType().getGeneric().resolve());
     }

    }

相关推荐
哈哈哈笑什么3 分钟前
蜜雪冰城1分钱奶茶秒杀活动下,使用分片锁替代分布式锁去做秒杀系统
redis·分布式·后端
Query*11 分钟前
杭州2024.08 Java开发岗面试题分类整理【附面试技巧】
java·开发语言·面试
WZTTMoon18 分钟前
Spring Boot 4.0 迁移核心注意点总结
java·spring boot·后端
寻kiki19 分钟前
scala 函数类?
后端
疯狂的程序猴29 分钟前
iOS App 混淆的真实世界指南,从构建到成品 IPA 的安全链路重塑
后端
旷野说36 分钟前
为什么 MyBatis 原生二级缓存“难以修复”?
java·java-ee·mybatis
8***235540 分钟前
【wiki知识库】07.用户管理后端SpringBoot部分
java
bcbnb41 分钟前
iOS 性能测试的工程化方法,构建从底层诊断到真机监控的多工具测试体系
后端
开心就好202544 分钟前
iOS 上架 TestFlight 的真实流程复盘 从构建、上传到审核的团队协作方式
后端
小周在成长1 小时前
Java 泛型支持的类型
后端