spring 22 对象绑定与类型转换

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

«interface»

Formatter

«interface»

Printer

«interface»

Parser

Converters

Set<GenericConverter>

«interface»

Converter

«interface»

ConversionService

FormattingConversionService

Adapter1

Adapter2

Adapter3

  • Printer 把其它类型转为 String
  • Parser 把 String 转为其它类型
  • Formatter 综合 Printer 与 Parser 功能
  • Converter 把类型 S 转为类型 T
  • Printer、Parser、Converter 经过适配转换成 GenericConverter 放入 Converters 集合
  • FormattingConversionService 利用其它们实现转换

底层第二套转换接口

«interface»

PropertyEditorRegistry

«interface»

PropertyEditor

  • PropertyEditor 把 String 与其它类型相互转换
  • PropertyEditorRegistry 可以注册多个 PropertyEditor 对象
  • 与第一套接口直接可以通过 FormatterPropertyEditorAdapter 来进行适配

高层接口与实现

«interface»

TypeConverter

SimpleTypeConverter

BeanWrapperImpl

DirectFieldAccessor

ServletRequestDataBinder

TypeConverterDelegate

«interface»

ConversionService

«interface»

PropertyEditorRegistry

  • 它们都实现了 TypeConverter 这个高层转换接口,在转换时,会用到 TypeConverter Delegate 委派ConversionService 与 PropertyEditorRegistry 真正执行转换(Facade 门面模式)
    • 首先看是否有自定义转换器, @InitBinder 添加的即属于这种 (用了适配器模式把 Formatter 转为需要的 PropertyEditor)
    • 再看有没有 ConversionService 转换
    • 再利用默认的 PropertyEditor 转换
    • 最后有一些特殊处理
  • SimpleTypeConverter 仅做类型转换
  • BeanWrapperImpl 为 bean 的属性赋值,当需要时做类型转换,走 Property
  • DirectFieldAccessor 为 bean 的属性赋值,当需要时做类型转换,走 Field
  • DataBinder 普通环境下的属性数据绑定类型转换
  • ServletRequestDataBinder 为 bean 的属性执行绑定,当需要时做类型转换,根据 directFieldAccess 选择走 Property 还是 Field,具备校验与获取校验结果功能

类型转换与数据绑定

点击查看代码
 //仅有类型转换功能
        SimpleTypeConverter converter = new SimpleTypeConverter();
        System.out.println(converter.convertIfNecessary("11", int.class));
        System.out.println(converter.convertIfNecessary("2011/01/02", Date.class));
//类型转换与数据绑定 ,根据反射,用 set 方法为 bean 属性赋值
        BeanWrapperImpl beanWrapper = new BeanWrapperImpl();
        S22Bean1 s22Bean1 = new S22Bean1();
        beanWrapper.setBeanInstance(s22Bean1);
        beanWrapper.setPropertyValue("name",13);
        beanWrapper.setPropertyValue("age","32");
        beanWrapper.setPropertyValue("date","2011/11/20");
        System.out.println(s22Bean1);
//类型转换与数据绑定, 利用反射,直接赋值给 bean 的属性,不需要 set 方法
        S22Bean2 s22Bean2 = new S22Bean2();
        DirectFieldAccessor fieldAccessor = new DirectFieldAccessor(s22Bean2);
        fieldAccessor.setPropertyValue("name",22);
        fieldAccessor.setPropertyValue("age","19");
        System.out.println(s22Bean2);
 //类型转换与数据绑定,根据 DirectFieldAccess 存不存在判断用哪种方法数据绑定  默认用 set 方法
        S22Bean2 s22Bean2 = new S22Bean2();
        DataBinder dataBinder = new DataBinder(s22Bean2);
        dataBinder.initDirectFieldAccess();
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.add("name", 34);
        propertyValues.add("age", "12");
        dataBinder.bind(propertyValues);
        System.out.println(s22Bean2);
//类型转换与数据绑定,根据 DirectFieldAccess 存不存在判断用哪种方法数据绑定  默认用 set 方法
        S22Bean2 s22Bean2 = new S22Bean2();
        DataBinder dataBinder = new ServletRequestDataBinder(s22Bean2);
        //可直接对属性进行绑定
        dataBinder.initDirectFieldAccess();
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setParameter("name","13");
        request.setParameter("age","13");
        request.setParameter("date","2011/2/2");
        ServletRequestParameterPropertyValues propertyValues = new ServletRequestParameterPropertyValues(request);
        //也可以使用此方式进行数据绑定类型转换
//        propertyValues.add("name", 34);
//        propertyValues.add("age", "12");
        dataBinder.bind(propertyValues);
        System.out.println(s22Bean2);
基本的类型转换与数据绑定用法
  • SimpleTypeConverter
  • BeanWrapperImpl
  • DirectFieldAccessor
  • ServletRequestDataBinder

数据绑定工厂

点击查看代码
 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 + '\'' +
                   '}';
        }
    }
}

ServletRequestDataBinderFactory 的用法和扩展点
  1. 可以解析控制器的 @InitBinder 标注方法作为扩展点,添加自定义转换器
    • 控制器私有范围
  2. 可以通过 ConfigurableWebBindingInitializer 配置 ConversionService 作为扩展点,添加自定义转换器
    • 公共范围
  3. 同时加了 @InitBinder 和 ConversionService 的转换优先级
    1. 优先采用 @InitBinder 的转换器
    2. 其次使用 ConversionService 的转换器
    3. 使用默认转换器
    4. 特殊处理(例如有参构造)

获取泛型参数

点击查看代码
 // 小技巧
        // 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());
  
1. java api 获取泛型参数 2. spring api 获取泛型参数
posted @   xy7112  阅读(94)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示