工具类(util class)是指用java编写的可独立使用的静态方法。本文借助探讨Spring Framework中工具类的命名规范和组织方式,帮助开发者深入理解工具类的设计思想。
使用spring多年,知道其中有许多好的工具类。本着程序员不重复造轮子的理念,有好的工具就应该直接来用。
平常没刻意的去记忆这些工具的名字,而是在想要用的时候去一个个翻找。这就产生一个问题,总不能一次性的找到想要调用的方法,而是总在几个近似的类中翻找想用调用的方法,浪费了时间。
遂对这些工具类的起名产生好奇,它们的名字到底和我推理出应该的名字之间有什么差异。
一、盘点spring中有哪些util class
StringUtil
org.springframework.util.StringUtils
处理与字符串(String
or
CharSequence
)相关的操作。其入参是或返回值是字符串。
其中最常用的是 hasText
方法,用来判断入参字符串是否含有可见文本(非空白)。它的逻辑和常见的isEmpty恰好是相反的。在string的判空上还要注意null、空字符串、空白字符串的细微区别。
另外注意的是这里面的split方法,指的是将一个字符串分割成两部分。
而其他更常见的分割字符串数在这里是tokenizeToStringArray。
CollectionUtils
o.s.u.CollectionUtils
是与集合有关的操作。如判空(null)、包含,第一个,最后一个元素等等。
FileCopyUtils
o.s.u.FileCopyUtils
是与File拷贝有关的操作。复制文件,以及File
和byte
array、string、reader、writer、stream之间转换。主要关系到文件内容的读写。
FileSystemUtils
o.s.util.FileSystemUtils
是文件系统操作。目前只有递归文件夹复制和删除两个操作。
SocketUtils
o.s.util.SocketUtils
是与socket相关的操作。主要用来检索可用端口,不涉及socket连接。
MimeTypeUtils
o.s.u.MimeTypeUtils
和o.s.u.MimeType
是对Mime字符串解析和模式匹配的封装,并且静态定义了很多常用mime。可以将mime字符串解析成结构化,极大方便了子类型的匹配判断。
MIME的介绍请看这篇:Media Type 媒体类型(MIME Type、Content Type)
StreamUtils
o.s.u.StreamUtils
是和stream类有关的操作。提取为string或byte
array,写到输出流、吃掉输入、再包装。
TypeUtils
o.s.u.TypeUtils
处理与java.lang.reflect.Type
相关的操作。判断类型之间可否转换,以及对泛型的查询(WildcardType
、ParameterizedType
)
ClassUtils
o.s.u.ClassUtils
处理与java.lang.Class
相关的操作。查找,比较,加载,检索成员,入参是class或返回值是class。
ObjectUtils
o.s.u.ObjectUtils
处理抽象到object层次的通用操作。例如isCheckedException
,
isArray
,isEmpty
,nullSafeHashCode
,nullSafeToString
,它的入参是object。
ReflectionUtils
o.s.u.ReflectionUtils
处理与反射,动态调用有关的操作和过程中的异常处理。支持类
方法的查找,调用,异常处理。与直接使用java reflect多了unchecked
exception包装。
BeanUtils
o.s.beans.BeanUtils
处理与 java bean 规范(即getter setter那种类,参看:JavaBean- 廖雪峰的官方网站)关联的操作。
Bean有属性(property),因此方法多为对属性元信息的访问和对属性的读写。
AnnotationUtils
o.s.core.annotation.AnnotationUtils
是与Annotation
有关的操作,特别是
findAnnotation
,可以在类的继承链上查找注解。
另外还有一个o.s.c.a.AnnotatedElementUtils
,算是升级版。这两个的差异主要在于AnnotationUtils
入参是class,是老式的,但AnnotatedElementUtils
的入参AnnotatedElement
是新式的并且功能更多。
二、为什么不容易精准找到要用的方法
起名很准确。Spring对工具类的整理是非常严格的。class、method起名非常准确,务必减少歧义。例如split
、tokenizeToStringArray
、find×××
、
get×××
这些不会让使用的人产生歧义,保持了spring一贯的编程风格。
我们日常编程时起名的准确性会弱化,以实用、能在使用场景中产生区分即可——这样的习惯导致产生了认知上的偏差。
归类很细致。Spring的工具围绕其内在的场景而分类,不会因为只是相近就合并到一个class中。为了升级时的兼容性,新工具旧工具也会分开保存。
我们日常编程用不到那么细致的分类。许多相似、相近的工具可能就合并到一个class中进行管理了。比如对JDK中class、reflect、annotation等的解析和处理通常就会放在一个ClassUtil里面。只要对这个分类体系的认知有偏差也会出现查找困难。
那么有没有方法来帮助查找呢?一种是借助开发工具的代码搜索功能,跳过分类直接搜方法名。
还有一种就是自己新设计一个util
class,包装并引用spring中的工具方法,将名称改换成自己的认知体系。
三、工具类一般起名时怎么起的
对工具类的起名生出好奇心,大家都是怎样对工具类起名字的呢?于是做了一个简单的调查,选取一批常用的类库,搜索其中的工具类,分析起名习惯。
调查范围:以org.springframework.boot:spring-boot-starter-parent:2.2.9.RELEASE
为基线,选择所有进入spring-boot-starter-\*
中的那些类库。搜索名字中含util
的类,分析其特征。
如此,搜索共得到1256个符合要求的调查对象。(1309个数据中有42个class带有实例method,不符合要求,另外排除内部类11个)
名字特点
以什么结尾 | class数量 | 占比(%) |
---|---|---|
Utils | 829 | 66 |
Util | 404 | 32 |
Utilities | 11 | 1 |
Utility | 11 | 1 |
总计 | 1256 | - |
-
Utils
+Util
占据主流,合计占比98%。
2.以s结尾是将每method视为一个util,class是util的集合(utils);而不带s,则是将整个class视为一个util。
哪些package 偏好Util呢
不同的工具对util 或 utils有没有偏好?来看看TOP10 Util
package name | Utils | Util |
---|---|---|
org.apache | 161 | 84 |
com.couchbase | 21 | 46 |
io.netty | 10 | 39 |
org.neo4j | 20 | 29 |
ch.qos | 0 | 26 |
org.springframework | 283 | 18 |
org.unbescape | 1 | 13 |
com.sun | 7 | 11 |
org.attoparser | 1 | 10 |
org.eclipse | 6 | 9 |
除了spring外,其他工具中两种用法有普遍存在,相信是开发者个人的因素在起作用。
在声明util class时习惯用哪些关键词来限定:
可见性 | 无abstract | abstract | 总计 | ||
---|---|---|---|---|---|
无final | final | 小计 | 无final | ||
package | 43 | 153 | 196 | 32 | 228 |
public | 421 | 429 | 850 | 178 | 1028 |
总计 | 464 | 582 | 1046 | 210 | 1256 |
可见性与abstract、final交叉透视(单位:class个数)
使用final或abstrac修订工具类,防止无意义的extends是一个好习惯,占到60%。
将类声明为final 或 abstract 可以用来避免不小心 extends。
而为避免不小心new class,可以类中声明 private construct(下图)。
有private 构造函数 | 无 | 总计 | |
---|---|---|---|
abstract | 25 | 185 | 210 |
其他 | 641 | 405 | 1046 |
总计 | 666 | 590 | 1256 |
有超过一半(53%)的类设置了private 构造函数来避免工具类实例化。
为了贯彻这两条原则,可以在自动化的代码检查环节启动类似的检查规则。例如sonarqube的SPEC-1118规则。
2024-05-16 修订语句不通顺的地方和冗余文字