Spring自带工具类使用学习

我们项目大多数都是基于Spring架构,Spring自身包含了许多实用的工具类,学习这些工具类的使用不仅能让我们达到事半功倍的效果,而且还能减少不必要的额外的工具类的引入。查看这些工具类的源码时发现它们都是abstract类型的,这是因为工具类的方法一般都是static静态方法,静态方法和类绑定,类加载后就能使用了,无需实例化(刚好abstract类不能直接实例化,并且可以定义非抽象方法),所以工具类定义为abstract类型再合适不过。

本文print方法为System.out.println的封装:
1
2
3
private static void print(Object value) {
System.out.println(value);
}

ClassUtils

org.springframework.util.classUtils包含一些和java.lang.Class相关的实用方法。

getDefaultClassLoader

ClassLoader getDefaultClassLoader()获取当前线程上下文的类加载器:

1
print(ClassUtils.getDefaultClassLoader());

1
sun.misc.Launcher$AppClassLoader@18b4aac2

overrideThreadContextClassLoader

ClassLoader overrideThreadContextClassLoader(@Nullable ClassLoader classLoaderToUse)用特定的类加载器覆盖当前线程上下文的类加载器:

1
2
3
print(ClassUtils.getDefaultClassLoader());
ClassUtils.overrideThreadContextClassLoader(ClassLoader.getSystemClassLoader().getParent());
print(ClassUtils.getDefaultClassLoader());

1
2
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@3feba861

forName

forName(String name, @Nullable ClassLoader classLoader)通过类名返回类实例,类似于Class.forName(),但功能更强,可以用于原始类型,内部类等:

1
2
3
4
ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
print(ClassUtils.forName("int", classLoader));
print(ClassUtils.forName("java.lang.String[]", classLoader));
print(ClassUtils.forName("java.lang.Thread$State", classLoader));

1
2
3
int
class [Ljava.lang.String;
class java.lang.Thread$State

isPresent

boolean isPresent(String className, @Nullable ClassLoader classLoader)判断当前classLoader是否包含目标类型(包括它的所有父类和接口):

1
2
3
ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
print(ClassUtils.isPresent("int", classLoader));
print(ClassUtils.isPresent("intt", classLoader));

1
2
true
false

resolvePrimitiveClassName

Class<?> resolvePrimitiveClassName(@Nullable String name)通过给定类名获取原始类:

1
2
print(ClassUtils.resolvePrimitiveClassName("int"));
print(ClassUtils.resolvePrimitiveClassName("java.lang.Integer"));

1
2
int
null

isPrimitiveWrapper

boolean isPrimitiveWrapper(Class<?> clazz)判断给定类是否为包装类,如Boolean, Byte, Character, Short, Integer, Long, Float, Double 或者 Void:

1
2
3
4
print(ClassUtils.isPrimitiveWrapper(Integer.class));
print(ClassUtils.isPrimitiveWrapper(Character.class));
print(ClassUtils.isPrimitiveWrapper(Void.class));
print(ClassUtils.isPrimitiveWrapper(String.class));

1
2
3
4
true
true
true
false

类似的方法还有isPrimitiveOrWrapper判断是否为原始类或者包装类、isPrimitiveWrapperArray判断是否为包装类数组、isPrimitiveArray判断是否为原始类数组。

resolvePrimitiveIfNecessary

Class<?> resolvePrimitiveIfNecessary(Class<?> clazz)如果给定类是原始类,则返回对应包装类,否则直接返回给定类:

1
2
print(ClassUtils.resolvePrimitiveIfNecessary(int.class));
print(ClassUtils.resolvePrimitiveIfNecessary(Object.class));

1
2
class java.lang.Integer
class java.lang.Object

isAssignable

boolean isAssignable(Class<?> lhsType, Class<?> rhsType)通过反射检查,是否可以将rhsType赋值给lhsType(注意,包装类型可以赋值给相应的原始类型,自动拆装箱机制):

1
2
3
4
5
print(ClassUtils.isAssignable(Integer.class, int.class));
print(ClassUtils.isAssignable(Object.class, String.class));
print(ClassUtils.isAssignable(BeanPostProcessor.class, InstantiationAwareBeanPostProcessor.class));
print(ClassUtils.isAssignable(double.class, Double.class)); // consider this
print(ClassUtils.isAssignable(Integer.class, Long.class));

1
2
3
4
5
true
true
true
true
false

isAssignableValue

boolean isAssignableValue(Class<?> type, @Nullable Object value)判断给定的值是否符合给定的类型:

1
2
3
4
5
print(ClassUtils.isAssignableValue(Integer.class, 1));
print(ClassUtils.isAssignableValue(Integer.class, 1L));
print(ClassUtils.isAssignableValue(int.class, Integer.valueOf(1)));
print(ClassUtils.isAssignableValue(Object.class,1));
print(ClassUtils.isAssignableValue(String.class,1));

1
2
3
4
5
true
false
true
true
false

convertResourcePathToClassName

String convertResourcePathToClassName(String resourcePath)将类路径转换为全限定类名:

1
print(ClassUtils.convertResourcePathToClassName("java/lang/String"));

1
java.lang.String

实际上就是将/替换为.convertClassNameToResourcePath方法功能相反。

classNamesToString

String classNamesToString(Class<?>... classes)直接看演示不解释:

1
print(ClassUtils.classNamesToString(String.class, Integer.class, BeanPostProcessor.class));

1
[java.lang.String, java.lang.Integer, org.springframework.beans.factory.config.BeanPostProcessor]

getAllInterfaces

Class<?>[] getAllInterfaces(Object instance)返回给定实例对象所实现接口类型集合:

1
2
3
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(processor);
Arrays.stream(allInterfaces).forEach(System.out::println);

1
2
3
4
interface org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor
interface org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor
interface org.springframework.core.PriorityOrdered
interface org.springframework.beans.factory.BeanFactoryAware

类似的方法还有getAllInterfacesForClassgetAllInterfacesAsSetgetAllInterfacesForClassAsSet

determineCommonAncestor

Class<?> determineCommonAncestor(@Nullable Class<?> clazz1, @Nullable Class<?> clazz2)寻找给定类型的共同祖先(所谓共同祖先指的是给定类型调用class.getSuperclass获得的共同类型,如果给定类型是Object.class,接口,原始类型或者Void,直接返回null):

1
2
3
4
// 它两都是接口
print(ClassUtils.determineCommonAncestor(AutowireCapableBeanFactory.class, ListableBeanFactory.class));
print(ClassUtils.determineCommonAncestor(Long.class, Integer.class));
print(ClassUtils.determineCommonAncestor(String.class, Integer.class));
1
2
3
null
class java.lang.Number
null

isInnerClass

boolean isInnerClass(Class<?> clazz)判断给定类型是否为内部类(非静态):

1
2
3
4
5
6
class A {
class B {

}
}
print(ClassUtils.isInnerClass(A.B.class)); // true

1
2
3
4
5
6
static class A {
class B {

}
}
print(ClassUtils.isInnerClass(A.B.class)); // true
1
2
3
4
5
6
static class A {
static class B {

}
}
print(ClassUtils.isInnerClass(A.B.class)); // false

isCglibProxy

boolean isCglibProxy(Object object)是否为Cglib代理对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SpringBootApplication
public class AopApplication {

@Configuration
static class MyConfigure {

}

public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(AopApplication.class, args);
MyConfigure myConfigure = context.getBean(MyConfigure.class);
System.out.println(ClassUtils.isCglibProxy(myConfigure));
}
}

1
true

配置类不由Cglib代理的话,返回为false:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SpringBootApplication
public class AopApplication {

@Configuration(proxyBeanMethods = false) // 注意这里
static class MyConfigure {

}

public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(AopApplication.class, args);
MyConfigure myConfigure = context.getBean(MyConfigure.class);
System.out.println(ClassUtils.isCglibProxy(myConfigure));
}
}

1
false

不过这个方法废弃了,建议使用org.springframework.aop.support.AopUtils.isCglibProxy(Object)方法。

getUserClass

Class<?> getUserClass(Object instance)返回给定实例对应的类型,如果实例是Cglib代理后的对象,则返回代理的目标对象类型:

1
print(ClassUtils.getUserClass("Hello")); // class java.lang.String

Cglib代理例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@SpringBootApplication
public class AopApplication {

@Configuration
static class MyConfigure {

}

public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(AopApplication.class, args);
MyConfigure myConfigure = context.getBean(MyConfigure.class);
// 注意它们的区别
System.out.println(myConfigure.getClass());
System.out.println(ClassUtils.getUserClass(myConfigure));
}
}

1
2
class cc.mrbird.aop.AopApplication$MyConfigure$$EnhancerBySpringCGLIB$$e51ce45
class cc.mrbird.aop.AopApplication$MyConfigure

matchesTypeName

boolean matchesTypeName(Class<?> clazz, @Nullable String typeName)判断给定class和类型名称是否匹配:

1
print(ClassUtils.matchesTypeName(String.class, "java.lang.String")); // true

getShortName

String getShortName(Class<?> clazz)返回类名:

1
print(ClassUtils.getShortName(String.class)); // String

getShortNameAsProperty

String getShortNameAsProperty(Class<?> clazz)返回首字母小写的类名,如果是内部类的话,则去掉外部类名:

1
print(ClassUtils.getShortNameAsProperty(String.class)); // string

1
2
3
4
5
class A {
class B {
}
}
print(ClassUtils.getShortNameAsProperty(String.class)); // b

getClassFileName

String getClassFileName(Class<?> clazz)返回类名+.class:

1
print(ClassUtils.getShortNameAsProperty(String.class)); // String.class

getPackageName

String getPackageName(Class<?> clazz)返回包名:

1
print(ClassUtils.getShortNameAsProperty(String.class)); // java.lang

getQualifiedName

String getQualifiedName(Class<?> clazz)返回全限定类名,如果是数组类型则末尾加[]:

1
2
print(ClassUtils.getQualifiedName(String.class));
print(ClassUtils.getQualifiedName(String[].class));

1
2
java.lang.String
java.lang.String[]

getQualifiedMethodName

String getQualifiedMethodName(Method method)获取方法的全限定名:

1
2
3
print(ClassUtils.getQualifiedMethodName(
ClassUtils.class.getDeclaredMethod("getQualifiedMethodName", Method.class
)));

1
org.springframework.util.ClassUtils.getQualifiedMethodName

hasConstructor

boolean hasConstructor(Class<?> clazz, Class<?>... paramTypes)判断给定类型是否有给定类型参数构造器:

1
2
print(ClassUtils.hasConstructor(String.class, String.class));
print(ClassUtils.hasConstructor(String.class, Object.class));

1
2
true
false

getConstructorIfAvailable

<T> Constructor<T> getConstructorIfAvailable(Class<T> clazz, Class<?>... paramTypes)返回给定类型的给定参数类型构造器,没有的话返回null:

1
2
3
Constructor<String> constructorIfAvailable = ClassUtils.getConstructorIfAvailable(String.class, String.class);
print(constructorIfAvailable != null);
print(constructorIfAvailable.toString());

1
2
true
public java.lang.String(java.lang.String)

hasMethod

boolean hasMethod(Class<?> clazz, Method method)判断给定类型是否有指定的方法:

1
2
Method hasMethod = ClassUtils.class.getDeclaredMethod("hasMethod", Class.class, Method.class);
print(ClassUtils.hasMethod(ClassUtils.class, hasMethod)); // true

重载方法boolean hasMethod(Class<?> clazz, String methodName, Class<?>... paramTypes)

getMethod

Method getMethod(Class<?> clazz, String methodName, @Nullable Class<?>... paramTypes)从指定类型中找指定方法,没找到抛IllegalStateException异常:

1
ClassUtils.getMethod(ClassUtils.class,"hello", String.class);

1
java.lang.IllegalStateException: Expected method not found: java.lang.NoSuchMethodException: org.springframework.util.ClassUtils.hello(java.lang.String)

如果希望没找到返回null,而非抛异常,可以用getMethodIfAvailable方法。

getMethodCountForName

int getMethodCountForName(Class<?> clazz, String methodName)从指定类型中通过方法名称查找该方法个数(重写、重载、非public的都算):

1
print(ClassUtils.getMethodCountForName(ClassUtils.class,"hasMethod")); // 2

类似的方法还有hasAtLeastOneMethodWithName,至少得有一个。

getStaticMethod

Method getStaticMethod(Class<?> clazz, String methodName, Class<?>... args)获取给定类型的静态方法,如果该方法不是静态的或者没有这个方法,则返回null:

1
2
3
Method method = ClassUtils.getStaticMethod(ClassUtils.class, "getDefaultClassLoader");
print(method != null);
print(method.getReturnType());

1
2
true
class java.lang.ClassLoader

FileSystemUtils

文件系统实用工具类

deleteRecursively

boolean deleteRecursively(@Nullable File root)递归删除指定文件或目录,删除成功返回true,失败返回false,不会抛出异常。

新建一个多层级目录:

QQ20201223-160010@2x

实用File的delete目录尝试删除a目录:

1
2
File file = new File("a");
print(file.delete()); // false

因为a目录包含子目录(文件),所以应该使用递归删除:

1
2
File file = new File("a");
print(FileSystemUtils.deleteRecursively(file)); // true

重载方法boolean deleteRecursively(@Nullable Path root)和该方法功能相似,但该方法可能会抛出IO异常。

copyRecursively

void copyRecursively(File src, File dest)递归复制src文件到dest(目标路径不存在则自动创建):

新建一个多层级目录:

QQ20201223-160010@2x

1
2
3
File src = new File("a");
File dest = new File("aa");
FileSystemUtils.copyRecursively(src, dest);

QQ20201223-161119@2x

重载方法void copyRecursively(Path src, Path dest)

StreamUtils

包含一些文件流的实用方法默认的缓冲区大小为4096bytes。

注意:该工具类的所有方法都不会对流进行关闭处理!

未完待续,慢慢记录😴

请作者喝瓶肥宅水🥤

0