java proertyutils_2. Spring早期类型转换,基于PropertyEditor实现

青年时种下什么,老年时就收获什么。关注公众号【BAT的乌托邦】,有Spring技术栈、MyBatis、JVM、中间件等小而美的原创专栏供以避免费学习。分享、成长,拒绝浅尝辄止。本文已被 https://www.yourbatman.cn 收录。html

064da4a436e7acc3886b66d530081ec2.png

✍前言

你好,我是YourBatman。java

Spring早在1.0(2004年发布,2003年孵化中)的时候,就有了类型转换功能模块。此模块存在的必要性没必要多说,相信每一个同窗均可理解。最初,Spring作类型转换器是基于Java标准的java.beans.PropertyEditor这个API去扩展实现的,直到Spring 3.0后才得以出现更好替代方案(Spring 3.0发布于2009 年12月)。程序员

提示:文章末尾附有Spring主要版本的发布时间和以及主要特性,感兴趣者可文末查看web

虽然说Spring自3.0就提出了更为灵活、优秀的类型转换接口/服务,可是早期基于PropertyEditor实现的转换器并未废弃且还在发挥余热中,所以本文就针对其早期类型转换实现作出专文讲解。spring

版本约定

Spring Framework:5.3.1

Spring Boot:2.4.0

说明:版本均于2020-11发布,且版本号均不带有.RELEASE后缀,这和以前是不同的。具体缘由请参考:Spring改变版本号命名规则:此举对非英语国家很友好数据库

✍正文

若你用当下的眼光去看Spring基于PropertyEditor的类型转换实现,会发现这么搞是存在一些设计缺陷的。固然并不能这么去看,毕竟如今都2020年了,那会才哪跟哪呢。既然Spring里的PropertyEditor现现在依然健在,那咱就会会它呗。json

PropertyEditor是什么?

PropertyEditor位于java.beans包中,要知道这个包里面的类都是设计为Java GUI程序(AWT)服务的,因此你看官方javadoc对PropertyEditor的介绍也无出其右:数组

A PropertyEditor class provides support for GUIs that want to allow users to edit a property value of a given type.

为GUI程序提供支持,容许你对给定的value进行编辑,做用相似于一个转换器:GUI上你能够输入、编辑某个属性而后通过它转换成合适的类型。

539e5486daa45075f413b48c5f5d226b.png缓存

此接口提供的方法挺多的,和本文类型转换有关的最多只有4个:安全

void setValue(Object value):设置属性值

Object getValue():获取属性值

String getAsText():输出。把属性值转换成String输出

void setAsText(String text):输入。将String转换为属性值类型输入

JDK对PropertyEditor接口提供了一个默认实现java.beans.PropertyEditorSupport,所以咱们若需扩展此接口,仅需继承此类,根据须要复写getAsText/setAsText这两个方法便可,Spring无一例外都是这么作的。

PropertyEditor做为一个JDK原生接口,内置了一些基本实现来服务于GUI程序,如:

BooleanEditor:将true/false字符串转换为Boolean类型

IntegerEditor:将字符串转换为Integer类型

同类别的还有LongEditor、FloatEditor...

JDK内置的实现比较少(如上),功能简陋,但对于服务GUI程序来讲已经够用,毕竟界面输入的只多是字符串,而且还均是基础类型。但这对于复杂的Spring环境、以及富文本的web环境来讲就不够用了,因此Spring在此基础上有所扩展,所以才有了本文来讨论。

注意:PropertyEditorSupport线程不安全

PropertyEditor实现的是双向类型转换:String和Object互转。调用setValue()方法后,须要先“缓存”起来后续才可以使用(输出)。PropertyEditorSupport为此提供了一个成员属性来作:

PropertyEditorSupport:

// 调用setValue()方法对此属性赋值 getValue()方法取值

private Object value;

这么一来PropertyEditorSupport就是有状态的了,所以是线程不安全的。在使用过程当中须要特别注意,避免出现并发风险。

说明:Support类里还看到属性监听器PropertyChangeListener,因它属于GUI程序使用的组件,与咱们无关,因此后续丝绝不会说起哦

Spring内置的全部扩展均是基于PropertyEditorSupport来实现的,所以也都是线程不安全的哦~

Spring为什么基于它扩展?

官方的javadoc都说得很清楚:PropertyEditor设计是为GUI程序服务的,那么Spring为什么看上它了呢?

试想一下:那会的Spring只能支持xml方式配置,而XML属于文本类型配置,所以在给某个属性设定值的时候,书写上去的100%是个字符串,可是此属性对应的类型却不必定是字符串,多是任意类型。你思考下,这种场景是否是跟GUI程序(AWT)一毛同样:输入字符串,对应任意类型。

为了实现这种需求,在PropertyEditorSupport的基础上只须要复写setAsText和getAsText这两个方法就行,而后Spring就这么干了。我我的yy一下,当初Spring选择这么干而没本身另起炉灶的缘由可能有这么几个:

本着不重复发明轮子的原则,有得用就直接用呗,何况是100%知足要求的

示好Java EE技术。毕竟那会Spring地位还并不稳,有大腿就先榜上

2003年左右,Java GUI程序还并未退出历史舞台,Spring为了通用性就选择基于它扩展喽

说明:那会的通用性可能和如今通用性含义上是不同的,须要稍做区别

Spring内建扩展实现有哪些?

Spring为了扩展自身功能,提升配置灵活性,扩展出了很是很是多的PropertyEditor实现,共计40余个,部分截图以下:

6018ace576f3d55411f606d7fd58a3f9.png

PropertyEditor

功能

举例

ZoneIdEditor

转为java.time.ZoneId

Asia/Shanghai

URLEditor

转为URL,支持传统方式file:,http:,也支持Spring风格:classpath:,context上下文相对路径等等

StringTrimmerEditor

trim()字符串,也可删除指定字符char

任意字符串

StringArrayPropertyEditor

转为字符串数组

A,B,C

PropertiesEditor

转为Properties

name = YourBatman

PatternEditor

转为Pattern

(\D)(\d+)(.)

PathEditor

转为java.nio.file.Path。支持传统URL和Spring风格的url

classpath:xxx

ClassEditor

转为Class

全类名

CustomBooleanEditor

转为Boolean

见示例

CharsetEditor

转为Charset

见示例

CustomDateEditor

转为java.util.Date

见示例

Spring把实现基本(大多数)都放在org.springframework.beans.propertyeditors包下,接下来我挑选几个表明性API举例说明。

标准实现示例

CustomBooleanEditor:

@Test

public void test1() {

PropertyEditor editor = new CustomBooleanEditor(true);

// 这些都是true,不区分大小写

editor.setAsText("trUe");

System.out.println(editor.getAsText());

editor.setAsText("on");

System.out.println(editor.getAsText());

editor.setAsText("yes");

System.out.println(editor.getAsText());

editor.setAsText("1");

System.out.println(editor.getAsText());

// 这些都是false(注意:null并不会输出为false,而是输出空串)

editor.setAsText(null);

System.out.println(editor.getAsText());

editor.setAsText("fAlse");

System.out.println(editor.getAsText());

editor.setAsText("off");

System.out.println(editor.getAsText());

editor.setAsText("no");

System.out.println(editor.getAsText());

editor.setAsText("0");

System.out.println(editor.getAsText());

// 报错

editor.setAsText("2");

System.out.println(editor.getAsText());

}

关注点:对于Spring来讲,传入的true、on、yes、1等都会被“翻译”成true(字母不区分大小写),大大提升兼容性。

如今知道为啥你用Postman传个1,用Bool值也能正常封装进去了吧,就是它的功劳。此效果等同于转换器StringToBooleanConverter,将在后面进行讲述对比

CharsetEditor:

@Test

public void test2() {

// 虽然都行,但建议你规范书写:UTF-8

PropertyEditor editor = new CharsetEditor();

editor.setAsText("UtF-8");

System.out.println(editor.getAsText()); // UTF-8

editor.setAsText("utF8");

System.out.println(editor.getAsText()); // UTF-8

}

关注点:utf-8中间的横杠可要可不要,但建议加上使用标准写法,另外字母也是不区分大小写的。

CustomDateEditor:

@Test

public void test3() {

PropertyEditor editor = new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"),true);

editor.setAsText("2020-11-30 09:10:10");

System.out.println(editor.getAsText()); // 2020-11-30 09:10:10

// null输出空串

editor.setAsText(null);

System.out.println(editor.getAsText());

// 报错

editor.setAsText("2020-11-30");

System.out.println(editor.getAsText());

}

关注点:这个时间/日期转换器很很差用,构造时就必须指定一个SimpleDateFormat格式化器。在实际应用中,Spring并无使用到它,而是用后面会说到的替代方案。

特殊实现

把没有放在org.springframework.beans.propertyeditors包下的实现称做特殊实现(前者称为标准实现)。

MediaTypeEditor:位于org.springframework.http。依赖的核心API是MediaType.parseMediaType(text),能够把诸如text/html、application/json转为MediaType对象

显然它属于spring-web包,使用在web环境下

FormatterPropertyEditorAdapter:位于org.springframework.format.support。将3.0新增的Formatter接口适配为一个PropertyEditor:setAsText这种转换操做委托给formatter.parse()去完成,反向委托给formatter.print()去完成。

此适配器在DataBinder#addCustomFormatter()获得应用

PropertyValuesEditor:位于org.springframework.beans。将k-v字符串(Properties格式)转换为一个PropertyValues,从而方便放进Environment里

ResourceEditor:位于org.springframework.core.io。此转换器将String转换为Resource资源,特别实用。做为基础设施,普遍被用到Spring的不少地方

像什么标准的FileEditor、InputSourceEditor、InputStreamEditor、URLEditor等等与资源相关的转换器,均依赖它而实现

@Test

public void test4() {

// 支持标准URL如file:C:/myfile.txt,也支持classpath:myfile.txt

// 同时还支持占位符形式

PropertyEditor editor = new ResourceEditor(new DefaultResourceLoader(), new StandardEnvironment(), true);

// file:形式本处略

// editor.setAsText("file:...");

// System.out.println(editor.getAsText());

// classpath形式(注意:若文件不存在不会报错,而是输出null)

editor.setAsText("classpath:app.properties");

System.out.println(editor.getAsText()); // 输出带盘符的全路径

System.setProperty("myFile.name", "app.properties");

editor.setAsText("classpath:${myFile.name}");

System.out.println(editor.getAsText()); // 结果同上

}

关注点:Spring扩展出来的Resource不只自持常规file:资源协议,还支持平时使用最多的classpath:协议,可谓很是好用。

ConvertingPropertyEditorAdapter:位于org.springframework.core.convert.support。将3.0新增的ConversionService转换服务适配为一个PropertyEditor,内部转换动做都委托给前者去完成。

AbstractPropertyBindingResult#findEditor()为属性寻找合适PropertyEditor的时候,若ConversionService能支持就包装为ConvertingPropertyEditorAdapter供以使用,这是适配器模式的典型应用场景。

“谁”在使用ProertyEditor

PropertyEditor自动发现机制

PropertyEditor存在的缺陷

考虑到阅读的温馨性,单篇文章不宜太长,所以涉及到PropertyEditor的这几个问题,放在下篇文章单独列出。这个几个问题会明显比本文更深刻,欢迎保持持续关注,peace!

✍总结

本文主要介绍了三点内容:

PropertyEditor是什么?

Spring为什么选择基于PropertyEditor?

Spring内建的那些PropertyEditor都有哪些,各自什么做用?

PropertyEditor虽然已经很古老,不适合当下复杂环境。但不能否认它依旧有存在的价值,Spring内部也大量的仍在使用,所以不容忽视。下篇文章将深度探讨Spring内部是如何使用PropertyEditor的,赋予了它哪些机制,以及最终为什么仍是决定本身另起炉灶搞一套呢?欢迎对本系列保持持续关注~

附:Spring主要版本发布时间和特性

2002-02,开始开发孵化此项目,项目名叫:interface21,它便就是Spring的前身

2004-03,1.0版发布。进入迅速发展期

2006-10,2.0版发布。支持可扩展的xml配置功能、支持Java五、支持动态语言、支持更多扩展点

2007-11,2.5版发布。项目名今后改成Spring Source,支持Java 6,支持注解配置(部分)

2009-12,3.0版发布。这是很是很是重要的一个版本,支持了模块驱动、支持SpEL、支持Java Bean配置、支持嵌入式数据库......

2011和2012,这两年发布了很是多的3.x系列小版本,带来了不少惊喜,同时也让Spring更加扎实

2013-12,4.0版发布。这是有一次进步,提供了对Java 8的全面支持,支持Java EE 七、支持websocket、泛型依赖注入......

2017-09,5.0版发布。最低JDK版本要求是Java 8,同时支持Servlet 3.1。固然最为重要的是引入了全新模块:WebFlux

截止到当前,Spring Framework的最新版本是5.3.x版本,此版本是5.x的最后一个主要功能分支,下个版本将是6.x喽,我们拭目以待。

✔推荐阅读:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值