Spring提供的实用纯Java工具类合集

发布时间 2023-12-26 08:40:07作者: 残城碎梦

在Spring Framework里的spring-core核心包里面,有个org.springframework.util里面有不少非常实用的工具类。

该工具包里面的工具类虽然是被定义在Spring下面的,但是由于Spring框架目前几乎成了JavaEE实际的标准了,因此我们直接使用也是无妨的,很多时候能够大大的提高我们的生产力。

IdGenerator 唯一键生成器 UUID

UUID除了生成的字符串比较长以外,几乎没有缺点(当然用字符串做主键,也算一个小缺点吧)

Spring给我提供了接口:IdGenerator 来生成id代表唯一箭,它内置提供了三个实现:

JdkIdGenerator

JDK的工具类包util包中就为我们提供了一个很好的工具类,即UUID。UUID(Universally Unique Identifier)通用唯一识别码。

public class JdkIdGenerator implements IdGenerator {
    public JdkIdGenerator() {
    }

    public UUID generateId() {
        return UUID.randomUUID();
    }
}

底层字节调用JDK的UUID方法,因此不做过多介绍了。

AlternativeJdkIdGenerator

这是Spring提供给我们的重头戏,用它来取代JDK的UUID的生成。

它使用了SecureRandom作为种子,来替换调用UUID#randomUUID()。它提供了一个更好、更高性能的表现。

缺点是:还需要new对象才能使用,不能通过类名直接调用静态方法,当然我们可以二次封装。另外,一般输出串我们都会进一步这么处理:.toString().replace("-", "")

SimpleIdGenerator

类似于自增的Id生成器。每调用一次,自增1,一般比较少使用。

Assert 断言工具类

Assert断言工具类,通常用于数据合法性检查。Assert断言工具类,通常用于数据合法性检查。

if (message== null || message.equals("")) {  
    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的引用类型一共分四种【小家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类而已。

DigestUtils

可以对字节数组、InputStream流生成摘要。

FastByteArrayOutputStream

可以说是Spring实现的加强版的ByteArrayOutputStream。为什么加强了呢?其实底层原理是Spring采用了一个LinkedList来作为缓冲区:

private final LinkedList<byte[]> buffers = new LinkedList<>();

而ByteArrayOutputStream直接使用的字节数组。

protected byte buf[];

这样每一次扩容中分配一个数组的空间,并当该数据放入到List中。相当于批量的操作,而ByteArrayOutputStream内部实现为一个数组每一次扩容需要重新分配空间并将数据复制到新数组中。效率高下立判,推荐使用。

FileCopyUtils、FileSystemUtils、StreamUtils

都是操作文件、操作流的一些工具类。

LinkedCaseInsensitiveMap 、LinkedMultiValueMap

不区分大小写的有序map,底层代理了LinkedHashMap,因此它能保证有序。此Map的意义在于:在编写比如MaBatis这种类似的自动封装框架的时候,特别有用。

数据库本身对大小写不敏感,但是创建Table的时候,数据库里面字段都会默认小写,所以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]}
}

PropertyPlaceholderHelper

作用:将字符串里的占位符内容,用我们配置的properties里的替换。这个是一个单纯的类,没有继承没有实现,而且也没简单,没有依赖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:\\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,证明它是从内往外一层一层解析的
}

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等标签的预处理;

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
}

StringUtils

Spring提供的字符串处理类。再结合Apache提供的,绝对的够用了。因此平时code过程中,绝对禁止程序员再自定义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"));

字符串和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):使用逗号连接数组,拼成字符串;

SystemPropertyUtils 占位符解析工具类

该类依赖于上面已经说到的PropertyPlaceholderHelper来处理。本类只处理系统默认属性值

该工具类很简单,但是非常的使用。

在平时的程序开发中,我们也经常会遇到一些痛点,比如规则引擎(公式):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
}

SpringProperties

位于org.springframework.core包。我认为它是Spring给我们提供的一个便捷处理.properties文件的好帮手。

在classpath类路径下建一个文件:spring.properties(必须是类路径下,且必须叫这个文件名)

name=fangshixiang
public static void main(String[] args) {
    String name = SpringProperties.getProperty("name");
    System.out.println(name); //fangshixiang
}

就这样,非常简单的,就可以读出spring.properties这个配置文件里面指定key的值。这样若我们某些参数需要外部化配置,使用这个参数可谓是非常非常的方便。

Constants:常量获取工具

在org.springframework.core包中,在处理类的常量的时候,我们可以借助它来处理。

它只有一个构造函数,传入一个Class,通过反射的方式获取目标source类中所有的public static final的常量放入一个Map中。

常用的几个方法:通过asXX方法取出相应的值。

public class Main {

    public static final int MAX_NUM = 5;
    public static final int MIN_NUM = 2;
    public static final String NAME = "fsx";
    public static final Map<String,Object> MAP = new HashMap<>();

    public static void main(String[] args) {
        Constants constants = new Constants(Main.class);
        System.out.println(constants.asNumber("MAX_NUM").intValue()); // 5
        System.out.println(constants.asString("NAME")); //fsx

        // 自动总个数
        System.out.println(constants.getSize()); //3

        //=============它的好处是提供一些批量获取的方法,在属性很多的时候  这个特别好用==============

        // 匹配前缀 批量获取 注意:此处返回的是字段名,不是值  不区分大小写  下同
        System.out.println(constants.getNames("M")); //[MIN_NUM, MAX_NUM, MAP]
        System.out.println(constants.getNames("m")); //[MIN_NUM, MAX_NUM, MAP]
        // 后缀匹配
        System.out.println(constants.getNamesForSuffix("NUM")); //[MIN_NUM, MAX_NUM]

        // 拿到所有的值  前缀匹配
        System.out.println(constants.getValues("M")); //[{}, 2, 5]
        // 后缀匹配
        System.out.println(constants.getValuesForSuffix("E")); //[fsx]
    }
}

注意:常量必须是 public static final 修饰的,否则使用asXX方法取出的时候会抛exception

ObjectUtils

ObjectUtils工具类会尝试优雅的处理null输入,对于空输入通常不会抛出异常,每个方法都更详细的记录其行为。

另外它提供的方法:addObjectToArray()、toObjectArray()、isArray()、arrayEquals()对处理数组都特别的好用。