• 【第二十三讲】对象绑定与类型转换


    【第二十三讲】对象绑定与类型转换

    1. 两套底层转换接口,一套高层转换接口
    2. 类型转换和数据绑定示例
    3. 类型转换绑定与绑定器工厂
    4. @DataTimeFormat 注解谁来解析
    5. Spring 提供的泛型操作技巧

    1.两套底层转换接口,一套高层转换接口

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

    «interface»
    Formatter
    «interface»
    Printer
    «interface»
    Parser
    Converters
    Set
    «interface»
    Converter
    «interface»
    ConversionService
    FormattingConversionService
    Adapter1
    Adapter2
    Adapter3
    • Printer 把其它类型转为 String
    • ParserString 转为其它类型
    • Formatter 综合 PrinterParser 功能
    • 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
    • ServletRequestDataBinder 为 bean 的属性执行绑定,当需要时做类型转换,根据 directFieldAccess 选择走 Property 还是 Field,具备校验与获取校验结果功能

    2.类型转换和数据绑定示例

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    BeanWrapperImpl

    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);
        }
    	
        @Data
        static class MyBean {
            private int a;
            private String b;
            private Date c;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    DirectFieldAccessor

    不需要get set 方法

    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 +
                       '}';
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    ServletRequestDataBinder

    directFieldAccess 选择走 Property 还是 Field

    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 +
                    '}';
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    3.类型转换绑定与绑定器工厂

    绑定器工厂

    日期绑定不成功

    public class TestServletDataBinderFactory {
        public static void main(String[] args) {
            MockHttpServletRequest request = new MockHttpServletRequest();
            request.setParameter("birthday", "1999|01|02");
            request.setParameter("address.name", "xi.an");
    
            User target = new User();
            ServletRequestDataBinder dataBinder = new ServletRequestDataBinder(target);
            dataBinder.bind(new ServletRequestParameterPropertyValues(request));
            System.out.println("--"+target);
        }
    
        @Data
        public static class User {
            private Date birthday;
            private Address address;
    
        }
        @Data
        public static class Address {
            private String name;
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    1.使用工厂,无转化功能

    ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
    WebDataBinder dataBinder = factory.createBinder(new ServletWebRequest(request), target, "user");
    
    • 1
    • 2

    1. 用 @InitBinder 转换

    public static void main(String[] args) throws Exception {
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setParameter("birthday", "1999|01|02");
        request.setParameter("address.name", "xi.an");
    
        User target = new User();
        // "2. 用 @InitBinder 转换"          PropertyEditorRegistry PropertyEditor
        InvocableHandlerMethod method = new InvocableHandlerMethod(new MyController(), MyController.class.getMethod("aaa", WebDataBinder.class));
        ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(Arrays.asList(method), null);
    
        WebDataBinder dataBinder = factory.createBinder(new ServletWebRequest(request), target, "user");
        dataBinder.bind(new ServletRequestParameterPropertyValues(request));
    
    
        System.out.println("--"+target);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    MyController

    static class MyController {
        @InitBinder
        public void aaa(WebDataBinder dataBinder) {
            // 扩展 dataBinder 的转换器
            dataBinder.addCustomFormatter(new MyDateFormatter("用 @InitBinder 方式扩展的"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    自定义MyDateFormatter

    @Slf4j
    public class MyDateFormatter implements Formatter<Date> {
    
        private final String desc;
    
        public MyDateFormatter(String desc) {
            this.desc = desc;
        }
    
        @Override
        public String print(Date date, Locale locale) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
            return sdf.format(date);
        }
    
        @Override
        public Date parse(String text, Locale locale) throws ParseException {
            log.debug(">>>>>> 进入了: {}", desc);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
            return sdf.parse(text);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    2.用 ConversionService 转换"

    //"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);
    WebDataBinder dataBinder = factory.createBinder(new ServletWebRequest(request), target, "user");
    dataBinder.bind(new ServletRequestParameterPropertyValues(request));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3.同时加了 @InitBinder 和 ConversionService"

    // "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(Arrays.asList(method), initializer);
    
    WebDataBinder dataBinder = factory.createBinder(new ServletWebRequest(request), target, "user");
    dataBinder.bind(new ServletRequestParameterPropertyValues(request));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    优先使用InitBinder

    在这里插入图片描述

    4.使用默认 ConversionService 转换"

    在这里插入图片描述

    5.Spring 提供的泛型操作技巧

    1.jdk api

    2.Spring api

    BaseDao

    class BaseDao<T> {
        T findOne() {
            return null;
        }
    }
    --------------------------------------------
    class StudentDao extends BaseDao<Student> {
    }
    
    --------------------------------------------
    class Student {
    }
    ----------------------------------------------
    class TeacherDao extends BaseDao<Teacher> {
    }
    ---------------------------------------------
    class EmployeeDao extends BaseDao {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    获取泛型参数

    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(
                    .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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    总结

    ServletRequestDataBinderFactory 的用法和扩展点

    1. 可以解析控制器的 @InitBinder 标注方法作为扩展点,添加自定义转换器
      • 控制器私有范围
    2. 可以通过 ConfigurableWebBindingInitializer 配置 ConversionService 作为扩展点,添加自定义转换器
      • 公共范围
    3. 同时加了 @InitBinder 和 ConversionService 的转换优先级
      1. 优先采用 @InitBinder 的转换器
      2. 其次使用 ConversionService 的转换器
      3. 使用默认转换器
      4. 特殊处理(例如有参构造)
  • 相关阅读:
    国内镜像源网址
    计组--存储系统
    Linux启动流程及systemd服务详解
    基于HTML+CSS+JavaScript+Bootstarp响应式健身网站(web前端期末大作业)
    程序员缓解工作压力的一些方法
    JAVA中如何实现代码优化
    用HTML+CSS做一个漂亮简单大学生校园班级网页
    在将对象 => JSON格式时,无法序列化部分属性
    python爬虫:同步模式和异步模式的区别
    vue、react中虚拟的dom
  • 原文地址:https://blog.csdn.net/qq_25614773/article/details/126576236