# Spring 框架中自带的工具类

# Spring 中最常用的四个工具类

  • ObjectUtils
  • StringUtils
  • CollectionUtils
  • Assert

# PatternMatchUtils

PatternMatchUtils 类来自 org.springframework.util 包 。

它用于进行简单地正则匹配。

判断规则有:xxx*, *xxx, *xxx*xxx*yyy* 通配任意个字符。

注意,正则规则字符串在前。

// 判断字符串是否符合规则。
static boolean simpleMatch(String pattern, String str) 

// 判断字符串是否同时满足多个规则。
static boolean simpleMatch(String[] patterns, String str) 

# StreamUtils

StreamUtils 类位于 org.springframework.util 包下 。

# 输出

void copy(byte[] in, OutputStream out)

int copy(InputStream in, OutputStream out)

void copy(String in, Charset charset, OutputStream out)

long copyRange(InputStream in, OutputStream out, long start, long end)

# 输入

byte[] copyToByteArray(InputStream in)

String copyToString(InputStream in, Charset charset)

// 舍弃输入流中的内容
int drain(InputStream in) 

# 实例化

InputStream emptyInput()

// 所生成的 输入流 close() 方法无效
InputStream nonClosing(InputStream in) 

// 所生成的 输出流 close() 方法无效
OutputStream nonClosing(OutputStream out) 

# FileCopyUtils

FileCopyUtils 类来自 org.springframework.util 包。它用于文件的拷贝处理工具。

以下方法结尾 static 方法,故不再显示 static 关键字。

# 输出

// 从字节数组到文件
void copy(byte[] in, File out)

// 从文件到文件
int copy(File in, File out)

// 从字节数组到输出流
void copy(byte[] in, OutputStream out) 

// 从输入流到输出流
int copy(InputStream in, OutputStream out) 

// 从输入流到输出流
int copy(Reader in, Writer out)

// 从字符串到输出流
void copy(String in, Writer out)

# 输入

// 从文件中读入到字节数组中
byte[] copyToByteArray(File in)

// 从输入流中读入到字节数组中
byte[] copyToByteArray(InputStream in)

// 从输入流中读入到字符串中
String copyToString(Reader in)

# 资源(Resource)及相关

# URI 和 URL

在原生 JDK 中,提供了 java.net.URIjava.net.URL 类,来标识资源的定位。

# 说明
URI 统一资源标志符(Uniform Resource Identifier
URL 统一资源定位符(uniform Resource Location

就像经纬度一样可以表示你在世界的哪个角落。URI 和 URL 代表着资源的位置信息。例如,数据库的 JDBC 连接是一个 URI/URL:

dbc:mysql://127.0.0.1:3306/scott?useSSL=false

它表示这一个资源(scott 数据库)的位置信息。

URI 更宽泛、更高级也更抽象,而 URL 则更具体。在概念上,URI 是 URL 的超集,URL 是 URI 的子集。类似于父类和子类的关系。

URL 并未 URI 唯一的 “子类” 。有一些标识资源的方式,它是 URI ,但不是 URL 。例如 urn:isbn:0-486-27557-4,这个是一本书的 ISBN ,可以唯一标识这本书,它是 URI 但不是 URL 。

# Resource 及其实现类

在日常项目开发过程中,从 classpath 下加载一个配置文件(并从中读取配置信息)是一个常见需求。

Spring 提炼出 Resource(资源)的概念,并围绕它提供了一系列的工具类,能方便地实现上述功能(及其它功能)

Resource
├── FileSystemResource
├── FileUrlResource
├── ClassPathResource
├── UrlResource
└── InputStreamResource
    └── EncodedResource

InputStreamResource 是 Resource 接口的子接口,其实现类 EncodedResource 支持通过 InputStream 生成 Resource 对象,并且可以设置编码。(其他 Resource 都是使用 ISO-8859-1 编码)

Resource 接口定义了『资源』的概念,其中常用方法有:

实例方法 说明
boolean exists() 判断资源是否存在
File getFile() 从资源中获得 File 对象
URI getURI() 从资源中获得 URI 对象
URL getURL() 从资源中获得 URI 对象
InputStream getInputStream() 获得资源的 InputStream
String getDescription() 获得资源的描述信息

Resource 接口的各个实现类中除了上述方法外,还有自己的一些方法。

# Resource 的 getFile() 方法的一个问题

Resource 的 getFile() 方法需要 Resource 对象所代表的那个文件 在文件系统中是独立存在的!

因此,你在 Eclipse/Idea 源码运行中运行,或者是一个 .war 的项目在 tomcat 下运行,.getFile() 方法都是没有任何问题,能正常起作用:获得 File 对象。

但是,在 Spring Boot 中,.getFile() 方法无法获得 File 对象。因为,Spring Boot 的 .jar 包不像一般 Java Web 的 .war 那样会被 tomcat 解压开!

因此,Spring Boot 项目中的资源文件都是 内嵌.jar 包中,而非独立存在的。不满足 .getFile() 的那个前提要求。

Spring Boot 的这种场景下,需要 使用 .getInputStream() 方法来替代 .getFile() 方法

简而言之,Resource 的 .getInputStream() 方法比 .getFile() 方法具有更好的普适性。

# ResourceUtils

对于常见的 “new Resource() 对象 + 操作 Resource 对象” 的常见使用方式,Spring 提供了一个 ResourceUtils 类来简化这样的代码。

ResourceUtils 类位于 org.springframework.util 包。它用于处理表达资源字符串前缀描述资源的工具. 如: classpath:file: 等。

不过,ResourceUtils 类也有上面所说的那个 .getFile() 的问题。因此,如果是 Spring Boot 项目,还是少用/不用 ResourceUtils 的 .getFile() 方法。

ResourceUtils 的常用方法:

// 判断字符串是否是一个合法的 URL 字符串。
static boolean isUrl(String resourceLocation) 
static URL getURL(String resourceLocation) 
static File	getFile(String resourceLocation)

static URI toURI(URL url) 
static File	getFile(URI resourceUri)
static File	getFile(URL resourceUrl)

# PropertiesLoaderUtils

.properties 文件是最常见的资源文件。对此,Spring 框架提供了 PropertiesLoaderUtils 工具类来简化加载 .properties 配置类的工作。

PropertiesLoaderUtils 位于 org.springframework.core.io.support 包下。

以下方法都是 static 方法

// 查找资源,并生成 Properties 对象。使用 ISO-8859-1 编码。
Properties loadProperties(Resource resource) 

// 从 classpath 中查找资源,并生成 Properties 对象。使用 ISO-8859-1 编码。
Properties loadAllProperties(String resourceName) 

// 查找资源,并生成 Properties 对象。
Properties loadProperties(EncodedResource resource) 

// 查找资源,填充参数对象。
void fillProperties(Properties props, Resource resource)

// 查找资源,填充参数对象。
void fillProperties(Properties props, EncodedResource resource) 

# Spring ResolvableType

提示

ResolveableType 与 Java 的 Type 类型体系密切相关。请先了解另一篇笔记《虚拟机中的泛型类型信息》 的内容。

# 类型擦除

由于类型擦除,当你在实例化泛型对象时,泛型的信息会受影响,被擦除(替换为 Object 等)。例如:

List<String> stringList = new ArrayList<String>();

上述代码中的 stringList 的泛型类型 String 会被擦除,变量 stringList 的实际类型会变为 ArrayList,而非 ArrayList<String>

但是好消息是,类型擦除只影响被实例化的类型参数,如果你使用在类定义中,泛型信息会被保留,在运行时可用。例如:

class StringList extends ArrayList<String> {
    ...
}

StringList stringList = new StringList();

当你去使用 StringList 类型时,这里的 String 信息你是可以通过反射机制获取到的。

# ResolvableType 简介

ResolvableType 是 Spring 4.0 的 feature 之一。

ResolvableType 为 java Type 体系中的所有类型提供了统一的数据结构以及 API 。

逻辑上,ResolvableType 就是一种特殊的容器,它可以用来包装 Type 类型(Class、ParameterizedType、TypeVariable、WildcardType、GenericArrayType)的数据类型的数据。

这样,你是直接操作 ResolvableType,而间接操作 Class、ParameterizedType、TypeVariable 等类型的数据。好处是无论 ResovableType 中封装的是何种 Type 类型, ResolvableType 有统一 API,且更简便好用。

# 简单使用

以 Class 类型为例,用 ResolvableType 包装 Class 类型的常见方式有:

// 其实本质上是一样的
ResolvableType type1 = ResolvableType.forClass(String.class);
ResolvableType type2 = ResolvableType.forClass("hello".getClass());
ResolvableType type3 = ResolvableType.forClass(Integer.class);
ResolvableType type4 = ResolvableType.forClass(ArrayList.class);

于此同时,ResolvableType 还提供了一个 .resolve() 方法,用于反向从 type 对象再反向求出其封装的 Class 对象。

Class clazz1 = type1.resolve(); // class java.lang.String
Class clazz2 = type2.resolve(); // class java.lang.String
Class clazz3 = type3.resolve(); // class java.lang.Integer
Class clazz4 = type4.resolve(); // class java.util.ArrayList

借助于这个 ResolvabType 对象,可以很方便地求出它String.class的直接父类类型、接口类型、泛型参数类型等类型有关信息。

System.out.println(type1.getSuperType());
System.out.println(Arrays.toString(type1.getInterfaces()));

需要注意的是这些方法的返回值的类型,仍然是 ResolvableType 类型(或 ResolvableType 类型的数组)

包装域的类型
forField(Field field);
包装方法形参的类型
forMethodParameter(Method method, int parameterIndex)
包装方法的返回值类型
forMethodReturnType(Method method);
等等

# 在泛型中的使用

还是曾经的一个例子:

public class StringArrayList extends ArrayList<String> {
  ...
}

在泛型和反射中,我们最常见的需求就是要去【】出某个类的父类的泛型实参(ActualTypeArguments)

通过 ResolvableType 实现这个需求就非常容易了:

ResolvableType resolvableType = ResolvableType.forClass(StringArrayList.class);

System.out.println( resolvableType.getSuperType().getGenerics()[0].resolve() );

# Base64Utils

Base64Utils 类来自 org.springframework.util 包。

Base64 是网络上最常见的用于传输 8 Bit 字节码的编码方式之一,Base64 就是一种基于 64 个可打印字符来表示二进制数据的方法。

Base64 编码是从二进制到字符的过程,可用于在 HTTP 环境下传递较长的标识信息。

采用 Base64 编码具有不可读性,需要解码后才能阅读。

  • 编码方法

    static byte[]	encode(byte[] src)
    static String	encodeToString(byte[] src)
    
    static byte[]	encodeUrlSafe(byte[] src)
    static String	encodeToUrlSafeString(byte[] src)
    
  • 解码方法

    static byte[]	decode(byte[] src)
    static byte[]	decodeFromString(String src)
    
    static byte[]	decodeUrlSafe(byte[] src)
    static byte[]	decodeFromUrlSafeString(String src)
    

BASE64-字母表

字符 十进制 十六进制 字符 十进制 十六进制 字符 十进制 十六进制
A 0 00 W 22 16 s 44 2C
B 1 01 X 23 17 t 45 2D
C 2 02 Y 24 18 u 46 2E
D 3 03 Z 25 19 v 47 2F
E 4 04 a 26 1A w 48 30
F 5 05 b 27 1B x 49 31
G 6 06 c 28 1C y 50 32
H 7 07 d 29 1D z 51 33
I 8 08 e 30 1E 0 52 34
J 9 09 f 31 1F 1 53 35
K 10 0A g 32 20 2 54 36
L 11 0B h 33 21 3 55 37
M 12 0C i 34 22 4 56 38
N 13 0D j 35 23 5 57 39
O 14 0E k 36 24 6 58 3A
P 15 0F l 37 25 7 59 3B
Q 16 10 m 38 26 8 60 3C
R 17 11 n 39 27 9 61 3D
S 18 12 o 40 28 + 62 3E
T 19 13 p 41 29 / 63 3F
U 20 14 q 42 2A
V 21 15 r 43 2B = (pad) (pad)

URL and Filename Safe 字母表中,+ 号会被替换成 - 号,进行编解码。同理,/ 号会被替换成 _

# SerializationUtils

SerializationUtils 类来自 org.springframework.util 包,它用于 java 的序列化与反序列化。

// 序列化方法
static byte[] serialize(Object object)  

// 反序列化方法
static Object deserialize(byte[] bytes)