zoukankan      html  css  js  c++  java
  • 1. 揭秘Spring类型转换

    仰不愧天,俯不愧人,内不愧心。关注公众号【BAT的乌托邦】,有Spring技术栈、MyBatis、JVM、中间件等小而美的原创专栏供以免费学习。分享、成长,拒绝浅尝辄止。本文已被 https://www.yourbatman.cn 收录。

    ✍前言

    你好,我是YourBatman。

    Spring Framework是一个现代化的框架,俨然已发展成为Java开发的基石。随着高度封装、高度智能化的Spring Boot的普及,发现团队内越来越少的人知道其深层次机制,哪怕只有一点点。这是让Spirng团队开心,但却是让使用的团队比较担忧的现象。


    若运行一个完全黑箱程序无疑像抱着一个定时炸弹,总是如履薄冰、战战兢兢。团队内需要这样的同学来为它保驾护航,惊爆之时方可泰然自诺。所以,你愿意pick吗?

    本系列将讨论Spring Framework里贯穿其上下文,具有举足轻重地位的一个模块:类型转换(也可叫数据转换)。

    ✍正文

    Java是个多类型且强类型语言,类型转换这个概念对它来说并不陌生。比如:

    • 自动类型转换(隐式):小类型 -> 大类型。eg:int a = 10; double b = a;
    • 强制类型转换(显式):大类型 -> 小类型。eg:double a = 10.123; int b = (int)a;
      • 说明:强转有可能产生精度丢失
    • 调用API类型转换:常见的是字符串和其它类型的互转。eg:parseInt(String); parseBoolean(String); JSON.toJSONString(Obj); LocalDate.parse(String)
      • 说明:API可能来自于JDK提供、一方库、二方库、三方库提供

    在企业级开发环境中,会遇到更为复杂的数据转换场景,譬如说:

    1. 输入/传入一个规格字符串(如1,2,3,4),转换为一个数组
    2. 输入/传入一个JSON串(如{"name":"YourBatman","age":18}),转换为一个Person对象
    3. 输入/传入一个URL串(如:C:/myfile.txt、classpath:myfile.txt),转换为一个org.springframework.core.io.Resource对象

    虽说数据输入/传入绝大部分都会是字符串(如Http请求信息、XML配置信息),但结构可以千差万别,那么这就必然会涉及到大量的数据类型、结构转换的逻辑。倘若这都需要程序员自己手动编码做转换处理,那会让人望而生畏甚至怯步。

    还好我们有Spring。从本文起,A哥就帮你解密Spring Framework它是如何帮你接管类型转换,实现“自动化”的。有了此部分知识的储备,后续再讨论自动化数据绑定、自动化数据校验、Spring Boot松散绑定等,一切都变得容易接受得多。

    说明:类型转换其实每个框架都会存在,其中Java领域以Spring的实现最为经典,学会后便可举一反三

    Spring类型转换

    Spring的类型转换也并非一步到位。完全掌握Spring的类型转换并非易事,需要有一定的脉络按步骤进行。本文作为类型转换系列第一篇文章,将绘制目录大纲,将从以下几个方面逐步展开讨论。

    早期类型转换之PropertyEditor

    早期的Spirng(3.0之前)类型转换是基于Java Beans接口java.beans.PropertyEditor来实现的(全部继承自PropertyEditorSupport):

    public interface PropertyEditor {
    	...
    	// String -> Object
    	void setAsText(String text) throws java.lang.IllegalArgumentException;
    	// Object -> String
    	String getAsText();
    	...
    }
    

    这类实现举例有:

    • StringArrayPropertyEditor,分隔的字符串和String[]类型互转
    • PropertiesEditor:键值对字符串和Properties类型互转
    • IntegerEditor:字符串和Integer类型互转
    • ...

    基于PropertyEditor的类型转换作为一种古老的、遗留下来的方式,是具有一些设计缺陷的,如:职责不单一,类型不安全,只能实现String类型的转换等。虽然自Spring 3.0起提供了现代化的类型转换接口,但是此部分机制一直得以保留,保证了向下兼容性。

    说明:Spring 3.0之前在Java领域还未完全站稳脚跟,因此良好的向下兼容显得尤为重要

    这块内容将在本系列后面具体篇章中得到专题详解,敬请关注。

    新一代类型转换接口Converter、GenericConverter

    为了解决PropertyEditor作为类型转换方式的设计缺陷,Spring 3.0版本重新设计了一套类型转换接口,其中主要包括:

    • Converter<S, T>:Source -> Target类型转换接口,适用于1:1转换
      • StringToPropertiesConverter:将String类型转换为Properties
      • StringToBooleanConverter:将String类型转换为Boolean
      • EnumToIntegerConverter:将Enum类型转换为Integer
    • ConverterFactory<S, R>:Source -> R类型转换接口,适用于1:N转换
      • StringToEnumConverterFactory:将String类型转任意Enum
      • StringToNumberConverterFactory:将String类型转为任意数字(可以是int、long、double等等)
      • NumberToNumberConverterFactory:数字类型转为数字类型(如int到long,long到double等等)
    • GenericConverter:更为通用的类型转换接口,适用于N:N转换
      • ObjectToCollectionConverter:任意集合类型转为任意集合类型(如List<String>转为List<Integer> / Set<Integer>都使用此转换器)
      • CollectionToArrayConverter:解释基本同上
      • MapToMapConverter:解释基本同上
    • ConditionalConverter:条件转换接口。可跟上面3个接口组合使用,提供前置条件判断验证

    重新设计的这套接口,解决了PropertyEditor做类型转换存在的所有缺陷,且具有非常高的灵活性和可扩展性。但是,每个接口独立来看均具有一定的局限性,只有使用组合拳方才有最大威力。当然喽,这也造成学习曲线变得陡峭。据我了解,很少有同学搞得清楚新的这套类型转换机制,特别容易混淆。倘若你掌握了是不是自己价值又提升了呢?不信你细品?

    这块内容将在本系列后面具体篇章中得到专题详解,敬请关注。

    新一代转换服务接口:ConversionService

    从上一小节我们知道,新的这套接口中,Converter、ConverterFactory、GenericConverter它们三都着力于完成类型转换。对于使用者而言,如果做个类型转换需要了解到这三套体系无疑成本太高,因此就有了ConversionService用于整合它们三,统一化接口操作。

    此接口也是Spring 3.0新增,用于统一化 底层类型转换实现的差异,对外提供统一服务,所以它也被称作类型转换的门面接口,从接口名称xxxService也能看出来其设计思路。它主要有两大实现:

    1. GenericConversionService:提供模版实现,如转换器的注册、删除、匹配查找等,但并不内置转换器实现
    2. DefaultConversionService:继承自GenericConversionService。在它基础上默认注册了非常多的内建的转换器实现,从而能够实现绝大部分的类型转换需求

    ConversionService转换服务它贯穿于Spring上下文ApplicationContext的多项功能,包括但不限于:BeanWrapper处理Bean属性、DataBinder数据绑定、PropertySource外部化属性处理等等。因此想要进一步深入了解的话,ConversionService是你绕不过去的坎。

    说明:很多小伙伴问WebConversionService是什么场景下使用?我说:它并非Spirng Framework的API,而属于Spring Boot提供的增强,且起始于2.x版本,这点需引起注意

    这块内容将在本系列后面具体篇章中得到专题详解,敬请关注。

    类型转换整合格式化器Formatter

    Spring 3.0还新增了一个Formatter<T>接口,作用为:将Object格式化为类型T。从语义上理解它也具有类型转换(数据转换的作用),相较于Converter<S,T>它强调的是格式化,因此一般用于时间/日期、数字(小数、分数、科学计数法等等)、货币等场景,举例它的实现:

    • DurationFormatter:字符串和Duration类型的互转
    • CurrencyUnitFormatter:字符串和javax.money.CurrencyUnit货币类型互转
    • DateFormatter:字符串和java.util.Date类型互转。这个就使用得太多了,它默认支持什么格式?支持哪些输出方式,这将在后文详细描述
    • ......

    为了和类型转换服务ConversionService完成整合,对外只提供统一的API。Spring提供了FormattingConversionService专门用于整合Converter和Formatter,从而使得两者具有一致的编程体验,对开发者更加友好。

    这块内容将在本系列后面具体篇章中得到专题详解,敬请关注。

    类型转换底层接口TypeConvert

    定义类型转换方法的接口,它在Spring 2.0就已经存在。在还没有ConversionService之前,它的类型转换动作均委托给已注册的PropertyEditor来完成。但自3.0之后,这个转换动作可能被PropertyEditor来做,也可能交给ConversionService处理。

    它一共提供三个重载方法:

    // @since 2.0
    public interface TypeConverter {
    
     	// value:待转换的source源数据
     	// requiredType:目标类型targetType
     	// methodParam:转换的目标方法参数,主要为了分析泛型类型,可能为null
     	// field:目标的反射字段,为了泛型,可能为null
    	<T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException;
    	<T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException;
    	<T> T convertIfNecessary(Object value, Class<T> requiredType, Field field) throws TypeMismatchException;
    
    }
    

    它是Spring内部使用类型转换的入口,最终委托给PropertyEditor或者注册到ConversionService里的转换器去完成。它的主要实现有:

    • TypeConverterSupport:@since 3.2。继承自PropertyEditorRegistrySupport,它主要是为子类BeanWrapperImpl提供功能支撑。作用有如下两方面:
      1. 提供对默认编辑器(支持JDK内置类型的转换如:Charset、Class、Class[]、Properties、Collection等等)和自定义编辑器的管理(PropertyEditorRegistry#registerCustomEditor)
      2. 提供get/set方法,把ConversionService管理上(可选依赖,可为null)
    • 数据绑定相关:因为数据绑定强依赖于类型转换,因此数据绑定涉及到的属性访问操作将会依赖于此组件,不管是直接访问属性的DirectFieldAccessor还是功能更强大的BeanWrapperImpl均是如此

    总的来说,TypeConverter能把类型的各种实现、API收口于此,Spring把类型转换的能力都转嫁到TypeConverter这个API里面去了。虽然方便了使用,但其内部实现原理稍显复杂,同样的这块内容将在本系列后面具体篇章中得到专题详解,敬请关注。

    Spring Boot使用增强

    在传统Spring Framework场景下,若想使用ConversionService还得手动档去配置,这对于不太了解其运行机制的同学无疑是有使用门槛的。而在Spring Boot场景下这一切都会变得简单许多,可谓使用起来愈发方便了。

    另外,Spring Boot在内建转换器的基础上额外扩展了不少实用转换器,形如:

    • StringToFileConverter:String -> File
    • NumberToDurationConverter
    • DelimitedStringToCollectionConverter
    • ......

    ✍总结

    基于配置来控制程序运行总比你修改程序代码来得更优雅、更富弹性,但这是需要依赖于数据绑定、数据校验等功能的,而它们又依赖于类型转换。

    虽说几乎所有的框架都会有类型转换的功能模块,但Spring的可能是最为通用、最为经典的存在。因此本系列专题讲解Spring Framework的类型转换,旨在能够帮你你撬开通往跃升的大门,节节攀高。


    ✔推荐阅读:
  • 相关阅读:
    pgspider sqlite mysql docker 镜像
    pgspider docker 镜像
    pgspider基于pg 的高性能数据可视化sql 集群引擎
    diesel rust orm 框架试用
    golang 条件编译
    Performance Profiling Zeebe
    bazel 学习一 简单java 项目运行
    一个好用node http keeplive agnet
    gox 简单灵活的golang 跨平台编译工具
    mailhog 作为smtp server mock工具
  • 原文地址:https://www.cnblogs.com/yourbatman/p/14068006.html
Copyright © 2011-2022 走看看