SpringMvc 源码分析 (类型绑定和对象转换) (十一)

本文详细介绍了Spring中的类型转换接口,包括底层的Printer、Parser、Formatter和Converter,以及高层的TypeConverter接口。文章通过示例展示了SimpleTypeConverter、BeanWrapperImpl、DataBinder和DirectFieldAccessor在不同类型转换中的应用。同时,解释了Spring如何处理泛型参数,并提供了获取泛型类型的实际参数的方法。此外,还探讨了ConversionService的扩展和@InitBinder的使用,包括自定义Formatter和数据绑定过程中的转换规则定制。

1.底层的转换接口

Spring提供的接口
在这里插入图片描述

  • Printer: 其他类型转为String
  • Parser: String转为其他类型
  • Formatter: Printer + Parser
  • Converter: 类型S转为类型T
  • Printer, Parser, Converter通过适配转换为GenericConverter, 放入Converters 集合中

Jdk提供的接口

在这里插入图片描述

  • PropertyEditor: 把String和其他类型的转换
  • PropertyEditorRegistry: 可以注册多个PropertyEditor对象
  • 与前面一套接口可以通过FormatterPropertyEditorAdapter进行适配

2.高层的转换接口

在这里插入图片描述

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

3.演示高级接口类型转化

  • TestSimpleConverter: SimpleTypeConverter
public class TestSimpleConverter {
    public static void main(String[] args) {
        // 类型转换
        SimpleTypeConverter typeConverter = new SimpleTypeConverter();
        Integer integer = typeConverter.convertIfNecessary("13", int.class);
        Date date = typeConverter.convertIfNecessary("1111/11/11", Date.class);
        System.out.println(date);
        System.out.println(integer);
    }
}
  • TestBeanWrapper: BeanWrapper
    BeanWrapperImpl 为 bean 的属性赋值,当需要时做类型转换,走 Property, 利用反射
public class TestBeanWrapper {
    public static void main(String[] args) {
        // 反射
        MyBean myBean = new MyBean();
        BeanWrapperImpl wrapper = new BeanWrapperImpl(myBean);
        wrapper.setPropertyValue("a", "10");
        wrapper.setPropertyValue("b", "hello");
        wrapper.setPropertyValue("c", "1111/11/11");
        System.out.println(myBean);
    }
    @Data
    @ToString
    static class MyBean{
        private int a;
        private String b;
        private Date c;

    }
}
  • TestDataBinder: DataBinder
    默认走set方法
public class TestDataBinder {
    public static void main(String[] args){
        // 默认走set方法
        MyBean bean = new MyBean();
        DataBinder dataBinder = new DataBinder(bean);
        MutablePropertyValues pvs = new MutablePropertyValues();
        pvs.addPropertyValue("a", "1");
        pvs.addPropertyValue("b", "a");
        pvs.addPropertyValue("c", "1111/11/11");
        dataBinder.bind(pvs);
        System.out.println(bean);
    }
    @Data
    static class MyBean{
        private int a;
        private String b;
        private Date c;


    }
}
  • TestServletDataBinder: Web环境下的DataBinder
public class TestServletDataBinder {
    public static void main(String[] args) {
        // web 条件下的数据绑定
        MyBean bean = new MyBean();
        ServletRequestDataBinder dataBinder = new ServletRequestDataBinder(bean);
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setParameter("a", "1");
        request.setParameter("b", "A");
        request.setParameter("c", "1111/11/11");
        dataBinder.bind(new ServletRequestParameterPropertyValues(request));
        System.out.println(bean);
    }

    @Data
    static class MyBean{
        private int a;
        private String b;
        private Date c;


    }
}
  • TestFileAccessor: DirectFieldAccessor 为 bean 的属性赋值,当需要时做类型转换,走 Field
public class TestFileAccessor {
    public static void main(String[] args) {
        // 反射, 走的不是get/set方法而是成员变量
        MyBean myBean = new MyBean();
        DirectFieldAccessor accessor = new DirectFieldAccessor(myBean);
        accessor.setPropertyValue("a", "10");
        accessor.setPropertyValue("b", "hello");
        accessor.setPropertyValue("c", "1111/11/11");
        System.out.println(myBean);
    }
    @ToString
    static class MyBean{
        private int a;
        private String b;
        private Date c;

    }
}

4.Spring中是如何操作泛型参数的

框架代码中, 父类有泛型的时候, 如何获取到泛型参数的类型的

public class Student {
}
public class BaseDao<T> {
}
public class StudentDao extends BaseDao<Student> {

}
  • TestGenericType
public class TestGenericType {
    public static void main(String[] args){
        // 如何获取继承参数
        // jdk
        Type type = StudentDao.class.getGenericSuperclass();
        System.out.println(type);

        ParameterizedType parameterizedType = (ParameterizedType)type;
        System.out.println(parameterizedType.getActualTypeArguments()[0]);

        // spring
        Class<?> aClass = GenericTypeResolver.resolveTypeArgument(StudentDao.class, BaseDao.class);
        System.out.println(aClass);
    }
}

5.ConversionService扩展和@InitBinder扩展

转换类型的功能扩展: 比如日期的形式可以改为 “xxxx|xx|xx” 本来是 “xxxx/xx/xx”

  • @InitBinder
  • ConversionService

MyDataFormatter

public class MyDataFormatter implements Formatter<Date> {
    @Override
    public Date parse(String text, Locale locale) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
        return sdf.parse(text);
    }

    @Override
    public String print(Date object, Locale locale) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
        return sdf.format(object);
    }
}

InitBinder

public class TestServletDataBinderFactory {
    public static void main(String[] args) throws Exception{
        // 1.工厂, 无需转换
        // 2.@InitBinder 转换
        // 3.ConversionService 转换 Formatter
        // 4.默认的ConversionService
        User user = new User();
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setParameter("date", "1111|11|11");
        request.setParameter("address.name", "1");

        // @InitBinder
        InvocableHandlerMethod method = new InvocableHandlerMethod(new Controller(), Controller.class.getMethod("aaa", WebDataBinder.class));
        ArrayList<InvocableHandlerMethod> list = new ArrayList<>();
        list.add(method);
        ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(list, null);
        WebDataBinder webDataBinder = factory.createBinder(new ServletWebRequest(request), user, "user");

        webDataBinder.bind(new ServletRequestParameterPropertyValues(request));
        System.out.println(user);

    }

    static class Controller{
        @InitBinder
        public void aaa(WebDataBinder webDataBinder){
            webDataBinder.addCustomFormatter(new MyDataFormatter());
        }
    }
    @Data
    public static class User{
        @DateTimeFormat(pattern = "yyyy||MM|dd")
        private Date date;
        private Address address;
    }
    @Data
    public static class Address{
        private String name;
    }
}

在这里插入图片描述

ConversionService

public class TestServletDataBinderFactory {
    public static void main(String[] args) throws Exception{
        // 1.工厂, 无需转换
        // 2.@InitBinder 转换
        // 3.ConversionService 转换 Formatter
        // 4.默认的ConversionService
        User user = new User();
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setParameter("date", "1111|11|11");
        request.setParameter("address.name", "1");


        // ConversionService
        FormattingConversionService service = new FormattingConversionService();
        service.addFormatter(new MyDataFormatter());
        ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
        initializer.setConversionService(service);
        
        ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, initializer);
       WebDataBinder webDataBinder = factory.createBinder(new ServletWebRequest(request), user, "user");

        webDataBinder.bind(new ServletRequestParameterPropertyValues(request));
        System.out.println(user);

    }

    static class Controller{
        @InitBinder
        public void aaa(WebDataBinder webDataBinder){
            webDataBinder.addCustomFormatter(new MyDataFormatter());
        }
    }
    @Data
    public static class User{
        @DateTimeFormat(pattern = "yyyy||MM|dd")
        private Date date;
        private Address address;
    }
    @Data
    public static class Address{
        private String name;
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值