Spring框架提供的好用的Java工具类
在Spring Framework里的spring-core核心包里面,有个org.springframework.util包,里面有不少非常实用的工具类。
该工具包里面的工具类虽然是被定义在Spring下面的,但是由于Spring框架目前几乎成了JavaEE实际的标准了,因此我们直接使用也是无妨的,很多时候能够大大的提高我们的生产力。
此处提到的工具类为纯工具类。与Spring的Bean没有关系,是最为共用的工具类
IdGenerator 唯一键生成器 UUID
UUID除了生成的字符串比较长以外,几乎没有缺点(当然用字符串做主键,也算一个小缺点吧)
Spring给我提供了接口:IdGenerator 来生成id代表唯一键,它内置提供了三个实现:
JdkIdGenerator
JDK的工具类包util包中就为我们提供了一个很好的工具类,即UUID。UUID(Universally Unique Identifier)通用唯一识别码。
@Override
public UUID generateId() {
return UUID.randomUUID();
}
底层字节调用JDK的UUID方法。
AlternativeJdkIdGenerator
这是Spring提供给我们的重头戏,用它来取代JDK的UUID的生成。
* An {@link IdGenerator} that uses {@link SecureRandom} for the initial seed and
* {@link Random} thereafter, instead of calling {@link UUID#randomUUID()} every
* time as {@link org.springframework.util.JdkIdGenerator JdkIdGenerator} does.
* This provides a better balance between securely random ids and performance.
它使用了SecureRandom作为种子,来替换调用UUID#randomUUID()。它提供了一个更好、更高性能的表现。
SimpleIdGenerator
类似于自增的Id生成器。每调用一次,自增1(一般比较少使用 知道就行了)
三者性能比较
public static void main(String[] args) {
JdkIdGenerator jdkIdGenerator = new JdkIdGenerator();
AlternativeJdkIdGenerator alternativeJdkIdGenerator = new AlternativeJdkIdGenerator();
SimpleIdGenerator simpleIdGenerator = new SimpleIdGenerator();
Instant start;
Instant end;
int count = 1000000;
//jdkIdGenerator
start = Instant.now();
for (int i = 0; i < count; i++) {
jdkIdGenerator.generateId();
}
end = Instant.now();
System.out.println("jdkIdGenerator循环" + count + "次耗时:" + Duration.between(start, end).toMillis() + "ms");
//alternativeJdkIdGenerator
start = Instant.now();
for (int i = 0; i < count; i++) {
alternativeJdkIdGenerator.generateId();
}
end = Instant.now();
System.out.println("alternativeJdkIdGenerator循环" + count + "次耗时:" + Duration.between(start, end).toMillis() + "ms");
//simpleIdGenerator
start = Instant.now();
for (int i = 0; i < count; i++) {
simpleIdGenerator.generateId();
}
end = Instant.now();
System.out.println("simpleIdGenerator循环" + count + "次耗时:" + Duration.between(start, end).toMillis() + "ms");
}
发现仅仅循环生成100万次,Spring提供的算法性能远远高于JDK的。因此建议大家以后使用AlternativeJdkIdGenerator去生成UUID,性能会更好一点。
缺点是:还需要new对象才能使用,不能通过类名直接调用静态方法,当然我们可以二次封装。另外,一般输出串我们都会进一步这么处理:.toString().replace("-", "")
Assert 断言工具类
Assert断言工具类,通常用于数据合法性检查。
if (message== null || message.equls("")) {
throw new IllegalArgumentException("输入信息错误!");
}
用Assert工具类上面的代码可以简化为:Assert.hasText((message, "输入信息错误!");
下面介绍常用的断言方法的使用:
Assert.notNull(Object object, "object is required") - 对象非空
Assert.isTrue(Object object, "object must be true") - 对象必须为true
Assert.notEmpty(Collection collection, "collection must not be empty") - 集合非空
Assert.hasLength(String text, "text must be specified") - 字符不为null且字符长度不为0
Assert.hasText(String text, "text must not be empty") - text 不为null且必须至少包含一个非空格的字符
Assert.isInstanceOf(Class clazz, Object obj, "clazz must be of type [clazz]") - obj必须能被正确造型成为clazz 指定的类
junit也提供断言工具类,但是我们只能在单元测试中使用,而Spring提供的这个,哪儿都能使用,还是比较方便的。
PathMatcher 路径匹配器
Spring提供的实现:AntPathMatcher Ant路径匹配规则
(1) SpringMVC的路径匹配规则是依照Ant的来的,实际上不只是SpringMVC,整个Spring框架的路径解析都是按照Ant的风格来的;
(2) AntPathMatcher不仅可以匹配Spring的@RequestMapping路径,也可以用来匹配各种字符串,包括文件路径等。
你是否曾经在你们的Filter里看过类似下面的代码?
这种所谓的白名单URL这样来匹配,可谓非常的不优雅,而且通过穷举法的扩展性不可为不差。因此下面举几个例子来介绍此匹配器的用法,以后建议使用它吧~
PathMatcher pathMatcher = new AntPathMatcher();
//这是我们的请求路径 需要被匹配(理解成匹配controller吧 就很容易理解了)
String requestPath = "/user/list.htm?username=aaa&departmentid=2&pageNumber=1&pageSize=20";//请求路径
//路径匹配模版
String patternPath = "/user/list.htm**";
assertTrue(pathMatcher.match(patternPath, requestPath));
ANT方式的通配符有三种:
- ?(匹配任何单字符)
- *(匹配0或者任意数量的字符)
- **(匹配0或者更多的目录)
url路径匹配规则说明:
举一些常用案例:
@Test
public void fun1() {
PathMatcher pathMatcher = new AntPathMatcher();
// 精确匹配
assertTrue(pathMatcher.match("/test", "/test"));
assertFalse(pathMatcher.match("test", "/test"));
//测试通配符?
assertTrue(pathMatcher.match("t?st", "test"));
assertTrue(pathMatcher.match("te??", "test"));
assertFalse(pathMatcher.match("tes?", "tes"));
assertFalse(pathMatcher.match("tes?", "testt"));
//测试通配符*
assertTrue(pathMatcher.match("*", "test"));
assertTrue(pathMatcher.match("test*", "test"));
assertTrue(pathMatcher.match("test/*", "test/Test"));
assertTrue(pathMatcher.match("*.*", "test."));
assertTrue(pathMatcher.match("*.*", "test.test.test"));
assertFalse(pathMatcher.match("test*", "test/")); //注意这里是false 因为路径不能用*匹配
assertFalse(pathMatcher.match("test*", "test/t")); //这同理
assertFalse(pathMatcher.match("test*aaa", "testblaaab")); //这个是false 因为最后一个b无法匹配了 前面都是能匹配成功的
//测试通配符** 匹配多级URL
assertTrue(pathMatcher.match("/*/**", "/testing/testing"));
assertTrue(pathMatcher.match("/**/*", "/testing/testing"));
assertTrue(pathMatcher.match("/bla/**/bla", "/bla/testing/testing/bla/bla")); //这里也是true哦
assertFalse(pathMatcher.match("/bla*bla/test", "/blaXXXbl/test"));
assertFalse(pathMatcher.match("/????", "/bala/bla"));
assertFalse(pathMatcher.match("/**/*bla", "/bla/bla/bla/bbb"));
assertTrue(pathMatcher.match("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing/"));
assertTrue(pathMatcher.match("/*bla*/**/bla/*", "/XXXblaXXXX/testing/testing/bla/testing"));
assertTrue(pathMatcher.match("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing"));
assertTrue(pathMatcher.match("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing.jpg"));
assertTrue(pathMatcher.match("/foo/bar/**", "/foo/bar"));
//这个需要特别注意:{}里面的相当于Spring MVC里接受一个参数一样,所以任何东西都会匹配的
assertTrue(pathMatcher.match("/{bla}.*", "/testing.html"));
assertFalse(pathMatcher.match("/{bla}.htm", "/testing.html")); //这样就是false了
}
注意事项:
(1) AntPathMatcher不仅可以匹配URL路径,也可以匹配文件路径。但是需要注意AntPathMatcher也有有参构造,传递路径分隔符参数pathSeparator(若不传,默认值为/),对于文件路径的匹配来说,则需要根据不同的操作系统来传递各自的文件分隔符,以此防止匹配文件路径错误。
AntPathMatcher默认路径分隔符为“/”,而在匹配文件路径时,需要注意Windows下路径分隔符为“\”,Linux下为“/”。靠谱写法如下两种方式:
AntPathMatcher matcher = new AntPathMatcher(File.separator);
AntPathMatcher matcher = new AntPathMatcher(System.getProperty("file.separator"));
(2) 最长匹配规则(has more characters),即越精确的模式越会被优先匹配到。例如,URL请求/app/dir/file.jsp,现在存在两个路径匹配模式/**/.jsp和/app/dir/.jsp,那么会根据模式/app/dir/*.jsp来匹配。
ConcurrentReferenceHashMap
ConcurrentReferenceHashMap是自spring3.2后增加的一个同步的软(虚)引用Map
这个工具类厉害了。我们知道java的引用类型一共分四种引用类型(强引用、软引用、弱引用、虚引用),JDK也为我们提供了WeakHashMap来使用。但是它并不是线程安全的,因此刚好Spring给我们功提供这个工具类:ConcurrentReferenceHashMap满足了我们对线程安全的弱、软引用的需求。下面通过一个示例体验一把:
@Test
public void fun1() throws InterruptedException {
String key = new String("key");
String value = new String("val");
Map<String, String> map = new ConcurrentReferenceHashMap<>(8, ConcurrentReferenceHashMap.ReferenceType.WEAK);
map.put(key, value);
System.out.println(map); //{key=val}
key = null;
System.gc();
//等待一会 确保GC能够过来
TimeUnit.SECONDS.sleep(5);
System.out.println(map); //{}
}
我们发现当我们把key置为null后,垃圾回收器会回收掉这部分内存。这就是弱、虚引用的作用,主要用来防止OOM。
查看ConcurrentReferenceHashMap源码发现起底层实现依赖的是RefrenceQueue完成自动移除操作。
DefaultPropertiesPersister
这个类本身没有什么特别的,就是代理了JDK的Properties类而已。但写到此处是觉得Spring优秀就优秀在它强大的对扩展开放的原则体现。
比如如果我们需要对配置文件进行解密(比如数据库连接密码不能明文),这些操作通过复写这些扩展类的某些方法来做,将特别的优雅。
DigestUtils
可以对字节数组、InputStream流生成摘要。10进制或者16进制都行。
FastByteArrayOutputStream
可以说是Spring实现的加强版的ByteArrayOutputStream。为什么加强了呢?其实底层原理是Spring采用了一个LinkedList来作为缓冲区:
// The buffers used to store the content bytes
private final LinkedList<byte[]> buffers = new LinkedList<>();
而ByteArrayOutputStream直接使用的字节数组。
protected byte buf[];
这样每一次扩容中分配一个数组的空间,并当该数据放入到List中。相当于批量的操作,而ByteArrayOutputStream内部实现为一个数组每一次扩容需要重新分配空间并将数据复制到新数组中。效率高下立判了~因此推荐使用。
FileCopyUtils、FileSystemUtils、StreamUtils
都是操作文件、操作流的一些工具类。
LinkedCaseInsensitiveMap 、LinkedMultiValueMap
不区分大小写的有序map;底层代理了LinkedHashMap,因此它能保证有序。此Map的意义在于:在编写比如MyBatis这种类似的自动封装框架的时候,特备有用。
数据库本身对大小写不敏感(我使用的是mysql),但是创建表格的时候,数据库里面字段都会默认小写,所以MyBatis映射的时候,key也会映射成小写,可以用LinkedCaseInsensitiveMap(key值不区分大小写的LinkedMap)来处理。
LinkedCaseInsensitiveMap的key一定是String
现在我随手写个Demo,感受一下吧:
@Test
public void fun1() {
Map<String, Object> map = new LinkedCaseInsensitiveMap<>();
map.put("a", 1);
map.put("A", 1);
System.out.println(map); //{A=1}
System.out.println(map.get("a")); //1 map里面key是小写的a,通过大写的A也能get出来结果
}
LinkedMultiValueMap:见名之意,一个key对应多个value。
@Test
public void fun1() {
//用Map接的时候 请注意第二个泛型 是个List哦
//Map<String, List<Integer>> map = new LinkedMultiValueMap<>();
LinkedMultiValueMap<String, Integer> map = new LinkedMultiValueMap<>();
//此处务必注意,如果你还是用put方法 那是没有效果的 同一个key还是会覆盖
//map.put("a", Arrays.asList(1));
//map.put("a", Arrays.asList(1));
//map.put("a", Arrays.asList(1));
//System.out.println(map); //{a=[1]}
//请用add方法
map.add("a", 1);
map.add("a", 1);
map.add("a", 1);
System.out.println(map); //{a=[1, 1, 1]}
}
个人感觉没有Apache Common提供的好用。但是一般来说也足够用了~
PropertyPlaceholderHelper
作用:将字符串里的占位符内容,用我们配置的properties里的替换。这个是一个单纯的类,没有继承没有实现,而且也没有依赖Spring框架其他的任何类。
StringValueResolver是一个转化String类型数据的接口,真正更新属性的api实现竟然是在PropertyPlaceholderHelper#parseStringValue
因此现在知道此类的重要性了,下面演示一下Demo,知道怎么使用即可(其实在Spring容器环境下,这些都对调用者是透明的,除非自己要书写框架性的东西):
配置文件如下:
name=wangzha
age=18
sex=man
name18man=love
public static void main(String[] args) throws Exception {
String a = "{name}{age}{sex}";
String b = "{name{age}{sex}}";
PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper("{", "}");
InputStream in = new BufferedInputStream(new FileInputStream(new File("D:\\work\\remotegitcheckoutproject\\myprojects\\java\\boot2-demo1\\src\\main\\resources\\application.properties")));
Properties properties = new Properties();
properties.load(in);
//==============开始解析此字符串==============
System.out.println("替换前:" + a); //替换前:{name}{age}{sex}
System.out.println("替换后:" + propertyPlaceholderHelper.replacePlaceholders(a, properties)); //替换后:wangzha18man
System.out.println("====================================================");
System.out.println("替换前:" + b); //替换前:{name{age}{sex}}
System.out.println("替换后:" + properties); //替换后:love 最后输出love,证明它是从内往外一层一层解析的
}
很显然@Value注解或者其余spel表达式的东西,都得依赖于这个来做。
这个掌握了,让我们自定义支持的表达式的时候,我们也可以高大上的说,我的格式支持spel表达式SpelExpressionParser。更加的灵活了。
SpringVersion、SpringBootVersion 获取当前Spring版本号
public static void main(String[] args) {
System.out.println(SpringVersion.getVersion()); //5.0.6.RELEASE
System.out.println(SpringBootVersion.getVersion()); //2.0.2.RELEASE
}
ReflectionUtils 反射工具类
反射在容器中使用是非常频繁的了,这些方法一般在Spring框架内部使用。当然现在Spring都成为实际的规范了,所以我们也可以直接拿来使用。
public static Field findField(Class<?> clazz, String name);
public static Field findField(Class<?> clazz, @Nullable String name, @Nullable Class<?> type);
public static void setField(Field field, @Nullable Object target, @Nullable Object value);
该方法是从类里面找到字段对象。(private以及父类的都会找哟,但是static的就不会啦)
public static Object getField(Field field, @Nullable Object target);
这个很明显,就是拿到该对象指定字段的值,示例如下:
public static void main(String[] args) {
Person person = new Person("fsx", 18);
Field field = ReflectionUtils.findField(Person.class, "name");
field.setAccessible(true); //注意,如果是private的属性,请加上这一句,否则抛出异常:can not access a member of class com.fsx.boot2demo1.bean.Person with modifiers "private"
System.out.println(ReflectionUtils.getField(field, person)); //fsx
}
public static Method findMethod(Class<?> clazz, String name);
public static Method findMethod(Class<?> clazz, String name, @Nullable Class<?>... paramTypes);
public static Object invokeMethod(Method method, @Nullable Object target); //这个不需要自己处理异常哦
public static Object invokeMethod(Method method, @Nullable Object target, @Nullable Object... args);
示例:
public static void main(String[] args) {
Person person = new Person("fsx", 18);
System.out.println(ReflectionUtils.findMethod(Person.class, "clone")); //protected native java.lang.Object java.lang.Object.clone() throws java.lang.CloneNotSupportedException
System.out.println(ReflectionUtils.findMethod(Person.class, "getName")); //public java.lang.String com.fsx.boot2demo1.bean.Person.getName()
System.out.println(ReflectionUtils.findMethod(Person.class, "setName", String.class)); //public void com.fsx.boot2demo1.bean.Person.setName(java.lang.String)
System.out.println(ReflectionUtils.findMethod(Person.class, "privateMethod")); //private void com.fsx.boot2demo1.bean.Person.privateMethod()
}
也是一样非常的强大。private、父类方法都能获取到。
所有的反射相关的异常,其实都可以交给下面来处理:
public static void handleReflectionException(Exception ex);
public static void rethrowRuntimeException(Throwable ex);
boolean declaresException(Method method, Class<?> exceptionType); //判断一个方法上是否声明了指定类型的异常
boolean isPublicStaticFinal(Field field); //判断字段是否是public static final的
boolean isEqualsMethod(Method method); //判断该方法是否是equals方法
boolean isHashCodeMethod(Method method);
boolean isToStringMethod(Method method);
boolean isObjectMethod(Method method); //判断该方法是否是Object类上的方法
public static void makeAccessible(Field field); //将一个字段设置为可读写,主要针对private字段
void makeAccessible(Method method);
void makeAccessible(Constructor<?> ctor);
在AopUtils中也有这几个isXXX方法,其实AopUtils中的isXXX方法就是调用的ReflectionUtils的这几个方法的;所以可见此工具类的强大。
public static Method[] getAllDeclaredMethods(Class<?> leafClass);
public static Method[] getUniqueDeclaredMethods(Class<?> leafClass);
看个例子:
public static void main(String[] args) {
Person person = new Person("fsx", 18);
Method[] allDeclaredMethods = ReflectionUtils.getAllDeclaredMethods(Person.class);
//我们发现 这个方法可以把所有的申明的方法都打印出来。包含private和父类的 备注:重复方法都会拿出来。比如此处的toString方法 子类父类的都有
for (Method method : allDeclaredMethods) {
System.out.println(method);
}
System.out.println("------------------------------------");
//针对于上面的结果过滤。只会保留一个同名的方法(保留子类的)
Method[] uniqueDeclaredMethods = ReflectionUtils.getUniqueDeclaredMethods(Person.class);
for (Method method : uniqueDeclaredMethods) {
System.out.println(method);
}
}
最后是这几个方法:
针对指定类型上的所有方法,依次调用MethodCallback回调;看个源码就知道这个方法的作用:
public static void doWithLocalMethods(Class<?> clazz, MethodCallback mc) {
Method[] methods = getDeclaredMethods(clazz);
for (Method method : methods) {
try {
mc.doWith(method);
}catch (IllegalAccessException ex) {
throw new IllegalStateException("...");
}
}
}
其实实现很简单,就是得到类上的所有方法,然后执行回调接口;这个方法在Spring针对bean的方法上的标签处理时大量使用,比如@Init,@Resource,@Autowire
等标签的预处理;
Spring处理的源码如下:
ResourceUtils
Spring 提供了一个 ResourceUtils 工具类,它支持“classpath:”和“file:”的地址前缀,它能够从指定的地址加载文件资源。(其实还支持“jar:和war:前缀”)
public abstract class ResourceUtils {
public static final String CLASSPATH_URL_PREFIX = "classpath:";
public static final String FILE_URL_PREFIX = "file:";
public static final String JAR_URL_PREFIX = "jar:";
public static final String WAR_URL_PREFIX = "war:";
...
// 是否是一个URL
public static boolean isUrl(@Nullable String resourceLocation) {
if (resourceLocation == null) {
return false;
}
// 对classpath:进行了特殊的照顾
if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
return true;
}
try {
new URL(resourceLocation);
return true;
}
catch (MalformedURLException ex) {
return false;
}
}
public static URL getURL(String resourceLocation) throws FileNotFoundException {...}
public static File getFile(String resourceLocation) throws FileNotFoundException {...}
public static File getFile(URL resourceUrl) throws FileNotFoundException {...}
public static File getFile(URI resourceUri) throws FileNotFoundException {...}
public static File getFile(URI resourceUri, String description) throws FileNotFoundException {...}
// 判断
public static boolean isFileURL(URL url) {
// 均是和protocol 这个协议有关的~~~
String protocol = url.getProtocol();
return (URL_PROTOCOL_FILE.equals(protocol) || URL_PROTOCOL_VFSFILE.equals(protocol) ||
URL_PROTOCOL_VFS.equals(protocol));
}
public static boolean isJarURL(URL url) {...}
// @since 4.1
public static boolean isJarFileURL(URL url) {...}
// URL和URI的转换
public static URI toURI(URL url) throws URISyntaxException {
return toURI(url.toString());
}
public static URI toURI(String location) throws URISyntaxException {
return new URI(StringUtils.replace(location, " ", "%20"));
}
}
示例:
public static void main(String[] args) throws FileNotFoundException {
File file = ResourceUtils.getFile("classpath:application.properties");
System.out.println(file); //D:\work\remotegitcheckoutproject\myprojects\java\boot2-demo1\target\classes\application.properties
System.out.println(ResourceUtils.isUrl("classpath:application.properties")); //true
//注意此处输出的路径为正斜杠 ‘/’的 上面直接输出File是反斜杠的(和操作系统相关)
System.out.println(ResourceUtils.getURL("classpath:application.properties")); //file:/D:/work/remotegitcheckoutproject/myprojects/java/boot2-demo1/target/classes/application.properties
}
备注:若你在使用过程中,发现ResourceUtils.getFile()死活都找不到文件的话,那我提供一个建议:是否是在jar包内使用了此工具类,一般不建议在jar包内使用。另外,提供一个代替方案:可解决大多数问题:
public static void main(String[] args) {
ClassPathResource resource = new ClassPathResource("application.properties");
System.out.println(resource); //class path resource [application.properties]
}
SerializationUtils
这个工具就不做过多解释了。提供了两个方法:对象<–>二进制的相互转化。(基于源生JDK的序列化方式)
public static byte[] serialize(@Nullable Object object);
public static Object deserialize(@Nullable byte[] bytes);
请注意,对象需要实现Serializable接口哦。
SocketUtils
提供给我们去系统找可用的Tcp、Udp端口来使用。有的时候确实还蛮好用的,毕竟端口有时候不用写死了,提高灵活性。
public static void main(String[] args) {
System.out.println(SocketUtils.PORT_RANGE_MAX); //65535 最大端口号
System.out.println(SocketUtils.findAvailableTcpPort()); //45569 随便找一个可用的Tcp端口 每次执行值都不一样哦
System.out.println(SocketUtils.findAvailableTcpPort(1000, 2000)); //1325 从指定范围内随便找一个端口
//找一堆端口出来 并且是排好序的
System.out.println(SocketUtils.findAvailableTcpPorts(10, 1000, 2000)); //[1007, 1034, 1287, 1483, 1494, 1553, 1577, 1740, 1963, 1981]
//UDP端口的找寻 同上
System.out.println(SocketUtils.findAvailableUdpPort()); //12007
}
其长度都是16个bit,所以端口号范围是0到(2^16-1),即 0到 65535。其中0到1023是IANA规定的系统端口,即系统保留窗口。例如HTTP为80端口,DNS服务为53端口。
下面列出常用tcp和udp重要协议端口号,供以参考:
另外说明:TCP、UDP可以绑定同一端口来进行通信。每个端口都拥有一个叫端口号(port number)的整数型标识符,用于区别不同端口。由于TCP/IP传输层的两个协议TCP和UDP是完全独立的两个软件模块,因此各自的端口号也相互独立,如TCP有一个255号端口,UDP也可以有一个255号端口,二者并不冲突。
StringUtils
Spring提供的字符串处理类。再结合Apache提供的,绝对的够用了。因此平时code过程中,绝对禁止程序员再自定义StringUtils工具类。
Spring为了最依赖原则,自己实现了一个StringUtils,这里分类进行介绍:
判断类
属于该类别的方法都是在对字符串进行一些判定操作:
//判断类:
// boolean isEmpty(Object str):字符串是否为空或者空字符串:""
// boolean hasLength(CharSequence str):字符串是否为空,或者长度为0
// boolean hasText(String str):字符串是否有内容(不为空,且不全为空格)
assertFalse(StringUtils.hasText(" "));
// boolean containsWhitespace(String str):字符串是否包含空格
assertTrue(StringUtils.containsWhitespace("a b"));
字符串头尾操作
都是对字符串前,或者字符串后的内容进行判定或者操作:
//字符串头尾操作
// String trimWhitespace(String str):去掉字符串前后的空格
assertEquals("abc", StringUtils.trimWhitespace(" abc "));
// String trimAllWhitespace(String str):去掉字符串中所有的空格
assertEquals("abc", StringUtils.trimAllWhitespace(" a b c "));
// String trimLeadingWhitespace(String str):去掉字符串开头的空格
// String trimTrailingWhitespace(String str):去掉字符串结束的空格
// String trimLeadingCharacter(String str, char leadingCharacter):去掉字符串开头的指定字符;
// String trimTrailingCharacter(String str, char trailingCharacter):去掉字符串结尾的指定字符;
// boolean startsWithIgnoreCase(String str, String prefix):
// 判断字符串是否以指定字符串开头,忽略大小写
assertTrue(StringUtils.startsWithIgnoreCase("abcd", "AB"));
// boolean endsWithIgnoreCase(String str, String suffix):
// 判断字符串是否以指定字符串结尾,忽略大小写
文件路径名称相关操作(非常重要)
文件路径名称相关操作,是针对文件名,文件路径,文件后缀等常见文件操作中需要用到的方法进行封装;
// String unqualify(String qualifiedName):
// 得到以.分割的最后一个值,可以非常方便的获取类似类名或者文件后缀
assertEquals("java", StringUtils.unqualify("cn.wolfcode.java"));
assertEquals("java", StringUtils.unqualify("cn/wolfcode/Hello.java"));
// String unqualify(String qualifiedName, char separator):
// 得到以给定字符分割的最后一个值,可以非常方便的获取类似文件名
assertEquals("Hello.java", StringUtils.unqualify("cn/wolfcode/Hello.java", File.separatorChar));
// String capitalize(String str):首字母大写
assertEquals("Wolfcode", StringUtils.capitalize("wolfcode"));
// String uncapitalize(String str):取消首字母大写(首字母小写)
assertEquals("java", StringUtils.uncapitalize("Java"));
// String getFilename(String path):获取文件名,就不需要再使用FilenameUtils
assertEquals("myfile.txt",StringUtils.getFilename("mypath/myfile.txt"));
// String getFilenameExtension(String path):获取文件后缀名
assertEquals("txt",StringUtils.getFilenameExtension("mypath/myfile.txt"));
// String stripFilenameExtension(String path):截取掉文件路径后缀
assertEquals("mypath/myfile", StringUtils.stripFilenameExtension("mypath/myfile.txt"));
// String applyRelativePath(String path, String relativePath):
// 找到给定的文件,和另一个相对路径的文件,返回第二个文件的全路径
// 打印:d:/java/wolfcode/other/Some.java
System.out.println(StringUtils.applyRelativePath("d:/java/wolfcode/Test.java", "other/Some.java"));
// 但是不支持重新定位绝对路径和上级目录等复杂一些的相对路径写法:
// 仍然打印:d:/java/wolfcode/../other/Some.java
System.out.println(StringUtils.applyRelativePath("d:/java/wolfcode/Test.java", "../other/Some.java"));
// String cleanPath(String path): =====这个方法非常的重要
// 清理文件路径,这个方法配合applyRelativePath就可以计算一些简单的相对路径了
// 打印:d:/java/other/Some.java
System.out.println(StringUtils.cleanPath("d:/java/wolfcode/../other/Some.java"));
// 需求:获取d:/java/wolfcode/Test.java相对路径为../../other/Some.java的文件全路径:
// 打印:d:/other/Some.java
System.out.println(StringUtils.cleanPath(StringUtils.applyRelativePath( "d:/java/wolfcode/Test.java", "../../other/Some.java")));
// boolean pathEquals(String path1, String path2):
// 判断两个文件路径是否相同,会先执行cleanPath之后再比较
assertTrue(StringUtils.pathEquals("d:/wolfcode.txt","d:/somefile/../wolfcode.txt"));
字符串和子串的操作
该组方法中主要是提供了字符串和字符串子串的操作,比如子串的匹配,子串的替换;子串的删除等等操作;
// boolean substringMatch(CharSequence str, int index, CharSequence
// substring):判断从指定索引开始,是否匹配子字符串
assertTrue(StringUtils.substringMatch("aabbccdd", 1, "abb"));
// int countOccurrencesOf(String str, String sub):判断子字符串在字符串中出现的次数
assertEquals(4, StringUtils.countOccurrencesOf("ababaabab", "ab"));
// String replace(String inString, String oldPattern, String
// newPattern):在字符串中使用子字符串替换
assertEquals("cdcdacdcd", StringUtils.replace("ababaabab", "ab", "cd"));
// String delete(String inString, String pattern):删除所有匹配的子字符串;
assertEquals("a", StringUtils.delete("ababaabab", "ab"));
// String deleteAny(String inString, String charsToDelete):删除子字符串中任意出现的字符
assertEquals("", StringUtils.deleteAny("ababaabab", "bar"));
// String quote(String str) :在字符串前后增加单引号,比较适合在日志时候使用;
assertEquals("'hello'", StringUtils.quote("hello"));
和Locale相关的一些字符串操作
// Locale parseLocaleString(String localeString):
// 从本地化字符串中解析出本地化信息,相当于Locale.toString()的逆向方法
assertEquals(Locale.CHINA, StringUtils.parseLocaleString("zh_CN"));
// @deprecated as of 5.0.4, in favor of {@link Locale#toLanguageTag()}
// String toLanguageTag(Locale locale):把Locale转化成HTTP中Accept-Language能接受的本地化标准;
// 比如标准的本地化字符串为:zh_CN,更改为zh-CN
System.out.println(StringUtils.toLanguageTag(StringUtils.parseLocaleString("zh_CN")));
字符串和Properties
把字符串和Properties对象之间的相互转化抽取出的一些常用方法
//Properties splitArrayElementsIntoProperties(String[] array, String delimiter):
// 把字符串数组中的每一个字符串按照给定的分隔符装配到一个Properties中
String[] strs=new String[]{"key:value","key2:中文"};
Properties ps=StringUtils.splitArrayElementsIntoProperties(strs, ":");
//打印输出:{key=value, key2=中文}
System.out.println(ps);
//Properties splitArrayElementsIntoProperties(String[] array, String delimiter, String charsToDelete)
//把字符串数组中的每一个字符串按照给定的分隔符装配到一个Properties中,并删除指定字符串,比如括号之类的;
字符串和数组之间的基本操作(重要)
该组方法主要是完成字符串和字符串数组之间的基本操作,比如追加,删除,排序等
// String[] addStringToArray(String[] array, String str):把一个字符串添加到一个字符串数组中
// 打印:[a, b, c, d]
System.out.println(Arrays.toString(StringUtils
.addStringToArray(new String[] { "a", "b", "c" }, "d")));
// String[] concatenateStringArrays(String[] array1, String[]array2):连接两个字符串数组
//打印:[a, b, c, a, b, c, d]
System.out.println(Arrays.toString(StringUtils.concatenateStringArrays(
new String[] { "a", "b", "c" },
new String[] { "a", "b", "c","d" })));
//String[] mergeStringArrays(String[] array1, String[] array2):连接两个字符串数组,去掉重复元素
//打印:[a, b, c, d]
System.out.println(Arrays.toString(StringUtils.mergeStringArrays(
new String[] { "a", "b", "c" },
new String[] { "a", "b", "c","d" })));
//String[] sortStringArray(String[] array):字符串数组排序
//打印:[a, b, c, d]
System.out.println(Arrays.toString(StringUtils.sortStringArray(new String[]{"d","c","b","a"})));
//String[] toStringArray(Collection<String> collection):把字符串集合变成字符串数组
//String[] toStringArray(Enumeration<String> enumeration):把字符串枚举类型变成字符串数组
//String[] trimArrayElements(String[] array):把字符串数组中所有字符串执行trim功能;
//String[] removeDuplicateStrings(String[] array):去掉给定字符串数组中重复的元素,能保持原顺序;
另外还有关于字符串和数组还有更多的方法:
//String[] split(String toSplit, String delimiter):按照指定字符串分割字符串;
assertArrayEquals(new String[]{"wolfcode","cn"}, StringUtils.split("wolfcode.cn", "."));
//只分割第一次,打印:[www, wolfcode.cn]
System.out.println(Arrays.toString(StringUtils.split("www.wolfcode.cn", ".")));
//String[] tokenizeToStringArray(String str, String delimiters)
//会对每一个元素执行trim操作,并去掉空字符串
//使用的是StringTokenizer完成,
//打印[b, c, d]
System.out.println(Arrays.toString(StringUtils.tokenizeToStringArray("aa,ba,ca,da", "a,")));
//String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens)
//后面两个参数在限定是否对每一个元素执行trim操作,是否去掉空字符串
//String[] delimitedListToStringArray(String str, String delimiter):分割字符串,会把delimiter作为整体分隔符
//打印:[a, b, c, da]
System.out.println(Arrays.toString(StringUtils.delimitedListToStringArray("aa,ba,ca,da", "a,")));
//String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete)
//分割字符串,会把delimiter作为整体分隔符,增加一个要从分割字符串中删除的字符;
//String[] commaDelimitedListToStringArray(String str):使用逗号分割字符串
//是delimitedListToStringArray(str, ",")的简单方法
//Set<String> commaDelimitedListToSet(String str):使用逗号分割字符串,并放到set中去重
//使用LinkedHashSet;
//String collectionToDelimitedString(Collection<?> coll, String delim, String prefix, String suffix)
//将一个集合中的元素,使用前缀,后缀,分隔符拼装一个字符串,前缀后后缀是针对每一个字符串的
String[] arrs=new String[]{"aa","bb","cc","dd"};
assertEquals("{aa},{bb},{cc},{dd}", StringUtils.collectionToDelimitedString(Arrays.asList(arrs),",","{","}"));
//String collectionToDelimitedString(Collection<?> coll, String delim):集合变成指定字符串连接的字符串;
//是collectionToDelimitedString(coll, delim, "", "")的简写;
//String collectionToCommaDelimitedString(Collection<?> coll):集合变成逗号连接的字符串;
//是collectionToDelimitedString(coll, ",");的简写;
//String arrayToDelimitedString(Object[] arr, String delim):数组使用指定字符串连接;
//String arrayToCommaDelimitedString(Object[] arr):使用逗号连接数组,拼成字符串;
StringUtils类中的方法其实真的还是很多,可能平时我们用的比较多的还是一些普通的方法,其实类似文件路径,文件名等相关操作,以前还会专门引入common-io的FilenameUtils等额外的工具类,原来在StringUtils中都有,而且根据其设计的这些方法,我们也能大概的猜出一些方法在Spring中哪些地方可能有用;最后,其中有些方法,还是非常常见的面试题,比如替换字符串,查询子串个数等。
SystemPropertyUtils 占位符解析工具类
该类依赖于上面已经说到的PropertyPlaceholderHelper来处理。本类只处理系统默认属性值哦~
该工具类很简单,但是非常的使用。可以很好的利用起来,Spring内部也有非常之多的使用。
在平时的程序开发中,我们也经常会遇到一些痛点,比如规则引擎(公式):A=${B}+${C}+1,这样我们只需要得到B和C的值,然后再放入公式计算即可
占位符在Spring、Tomcat、Maven里都有大量的使用。下面用简单的例子体验一把:
public static String resolvePlaceholders(String text);
//如果标志位为true,则没有默认值的无法解析的占位符将保留原样不被解析 默认为false
public static String resolvePlaceholders(String text, boolean ignoreUnresolvablePlaceholders);
例子:
public static void main(String[] args) {
System.out.println(SystemPropertyUtils.resolvePlaceholders("${os.name}/logs/app.log")); //Windows 10/logs/app.log
//备注:这里如果不传true,如果找不到app.root这个key就会报错哦。传true后找不到也原样输出
System.out.println(SystemPropertyUtils.resolvePlaceholders("${app.root}/logs/app.log", true)); //${app.root}/logs/app.log
}
另外,org.springframework.core.io.support.PropertiesLoaderUtils也是Spring提供的加载.properties配置文件的重要工具类之一。
工具类附录
UrlResource
ClassPathResource
FileSystemResource
ServletContextResource
InputStreamResource
ByteArrayResource
EncodedResource 也就是Resource加上encoding, 可以认为是有编码的资源
VfsResource(在jboss里经常用到, 相应还有 工具类 VfsUtils)
org.springframework.util.xml.ResourceUtils 用于处理表达资源字符串前缀描述资源的工具
org.springframework.util.xml.DomUtils
org.springframework.web.util.CookieGenerator
org.springframework.web.util.HtmlCharacterEntityDecoder
org.springframework.web.util.HtmlCharacterEntityReferences
org.springframework.web.util.HtmlUtils
org.springframework.web.util.HttpUrlTemplate 这个类用于用字符串模板构建url, 它会自动处理url里的汉字及其它相关的编码. 在读取别人提供的url资源时, 应该经常用
org.springframework.web.util.JavaScriptUtils
org.springframework.web.util.Log4jConfigListener 用listener的方式来配制log4j在web环境下的初始化
org.springframework.web.util.UriTemplate
org.springframework.web.util.UriComponentsBuilder
org.springframework.web.util.UriComponents
org.springframework.web.util.UrlPathHelper
org.springframework.web.util.UriUtils 处理uri里特殊字符的编码
org.springframework.web.util.WebUtils
org.springframework.util.xml.ClassUtils 用于Class的处理
org.springframework.util.xml.Assert 断言,在我们的参数判断时应该经常用
org.springframework.util.xml.DigestUtils 摘要处理, 这里有用于md5处理信息的
org.springframework.util.xml.FileCopyUtils 文件的拷贝处理, 结合Resource的概念一起来处理, 真的是很方便
org.springframework.util.xml.Log4jConfigurer 一个log4j的启动加载指定配制文件的工具类
org.springframework.util.xml.NumberUtils 处理数字的工具类, 有parseNumber 可以把字符串处理成我们指定的数字格式, 还支持format格式, convertNumberToTargetClass 可以实现Number类型的转化
org.springframework.util.xml.ObjectUtils 有很多处理null object的方法. 如nullSafeHashCode, nullSafeEquals, isArray, containsElement, addObjectToArray, 等有用的方法
org.springframework.util.xml.PropertyPlaceholderHelper 用于处理占位符的替换
org.springframework.util.xml.ReflectionUtils 反映常用工具方法。有 findField, setField, getField, findMethod, invokeMethod等有用的方法
org.springframework.util.xml.SerializationUtils 用于java的序列化与反序列化. serialize与deserialize方法
org.springframework.util.xml.StopWatch 一个很好的用于记录执行时间的工具类, 且可以用于任务分阶段的测试时间。最后支持一个很好看的打印格式,这个类应该经常用
org.springframework.util.xml.StringUtils
org.springframework.util.xml.SystemPropertyUtils
org.springframework.util.xml.TypeUtils 用于类型相容的判断
参考: |