Spring 自帶工具類使用學習

語言: CN / TW / HK

theme: vuepress highlight: atom-one-dark


我們專案大多數都是基於Spring架構,Spring自身包含了許多實用的工具類,學習這些工具類的使用不僅能讓我們達到事半功倍的效果,而且還能減少不必要的額外的工具類的引入。檢視這些工具類的原始碼時發現它們都是abstract型別的,這是因為工具類的方法一般都是static靜態方法,靜態方法和類繫結,類載入後就能使用了,無需例項化(剛好abstract類不能直接例項化,並且可以定義非抽象方法),所以工具類定義為abstract型別再合適不過。

本文print方法為System.out.println的封裝:

java private static void print(Object value) { System.out.println(value); }

ClassUtils

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

getDefaultClassLoader

ClassLoader getDefaultClassLoader()獲取當前執行緒上下文的類載入器:

java print(ClassUtils.getDefaultClassLoader());

java sun.misc.Launcher$AppClassLoader@18b4aac2

overrideThreadContextClassLoader

ClassLoader overrideThreadContextClassLoader(@Nullable ClassLoader classLoaderToUse)用特定的類載入器覆蓋當前執行緒上下文的類載入器:

java print(ClassUtils.getDefaultClassLoader()); ClassUtils.overrideThreadContextClassLoader(ClassLoader.getSystemClassLoader().getParent()); print(ClassUtils.getDefaultClassLoader());

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

forName

forName(String name, @Nullable ClassLoader classLoader)通過類名返回類例項,類似於Class.forName(),但功能更強,可以用於原始型別,內部類等:

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

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

isPresent

boolean isPresent(String className, @Nullable ClassLoader classLoader)判斷當前classLoader是否包含目標型別(包括它的所有父類和介面):

java ClassLoader classLoader = ClassUtils.getDefaultClassLoader(); print(ClassUtils.isPresent("int", classLoader)); print(ClassUtils.isPresent("intt", classLoader));

true false

resolvePrimitiveClassName

Class<?> resolvePrimitiveClassName(@Nullable String name)通過給定類名獲取原始類:

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

int null

isPrimitiveWrapper

boolean isPrimitiveWrapper(Class<?> clazz)判斷給定類是否為包裝類,如Boolean, Byte, Character, Short, Integer, Long, Float, Double 或者 Void:

java print(ClassUtils.isPrimitiveWrapper(Integer.class)); print(ClassUtils.isPrimitiveWrapper(Character.class)); print(ClassUtils.isPrimitiveWrapper(Void.class)); print(ClassUtils.isPrimitiveWrapper(String.class));

true true true false

類似的方法還有isPrimitiveOrWrapper判斷是否為原始類或者包裝類、isPrimitiveWrapperArray判斷是否為包裝類陣列、isPrimitiveArray判斷是否為原始類陣列。

resolvePrimitiveIfNecessary

Class<?> resolvePrimitiveIfNecessary(Class<?> clazz)如果給定類是原始類,則返回對應包裝類,否則直接返回給定類:

java print(ClassUtils.resolvePrimitiveIfNecessary(int.class)); print(ClassUtils.resolvePrimitiveIfNecessary(Object.class));

java class java.lang.Integer class java.lang.Object

isAssignable

boolean isAssignable(Class<?> lhsType, Class<?> rhsType)通過反射檢查,是否可以將rhsType賦值給lhsType(注意,包裝型別可以賦值給相應的原始型別,自動拆裝箱機制):

java 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));

java true true true true false

isAssignableValue

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

java 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));

true false true true false

convertResourcePathToClassName

String convertResourcePathToClassName(String resourcePath)將類路徑轉換為全限定類名:

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

java java.lang.String

實際上就是將/替換為.convertClassNameToResourcePath方法功能相反。

classNamesToString

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

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

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

getAllInterfaces

Class<?>[] getAllInterfaces(Object instance)返回給定例項物件所實現介面型別集合:

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

java 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):

java // 它兩都是介面 print(ClassUtils.determineCommonAncestor(AutowireCapableBeanFactory.class, ListableBeanFactory.class)); print(ClassUtils.determineCommonAncestor(Long.class, Integer.class)); print(ClassUtils.determineCommonAncestor(String.class, Integer.class)); null class java.lang.Number null

isInnerClass

boolean isInnerClass(Class<?> clazz)判斷給定型別是否為內部類(非靜態):

```java class A { class B {

}

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

```java static class A { class B {

}

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

}

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

isCglibProxy

boolean isCglibProxy(Object object)是否為Cglib代理物件:

```java @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));
}

} ```

true

配置類不由Cglib代理的話,返回為false:

```java @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));
}

} ```

false

不過這個方法廢棄了,建議使用org.springframework.aop.support.AopUtils.isCglibProxy(Object)方法。

getUserClass

Class<?> getUserClass(Object instance)返回給定例項對應的型別,如果例項是Cglib代理後的物件,則返回代理的目標物件型別:

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

Cglib代理例子:

```java @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));
}

} ```

java class cc.mrbird.aop.AopApplication$MyConfigure$$EnhancerBySpringCGLIB$$e51ce45 class cc.mrbird.aop.AopApplication$MyConfigure

matchesTypeName

boolean matchesTypeName(Class<?> clazz, @Nullable String typeName)判斷給定class和型別名稱是否匹配:

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

getShortName

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

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

getShortNameAsProperty

String getShortNameAsProperty(Class<?> clazz)返回首字母小寫的類名,如果是內部類的話,則去掉外部類名:

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

java class A { class B { } } print(ClassUtils.getShortNameAsProperty(String.class)); // b

getClassFileName

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

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

getPackageName

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

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

getQualifiedName

String getQualifiedName(Class<?> clazz)返回全限定類名,如果是陣列型別則末尾加[]:

java print(ClassUtils.getQualifiedName(String.class)); print(ClassUtils.getQualifiedName(String[].class));

java java.lang.String java.lang.String[]

getQualifiedMethodName

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

java print(ClassUtils.getQualifiedMethodName( ClassUtils.class.getDeclaredMethod("getQualifiedMethodName", Method.class )));

java org.springframework.util.ClassUtils.getQualifiedMethodName

hasConstructor

boolean hasConstructor(Class<?> clazz, Class<?>... paramTypes)判斷給定型別是否有給定型別引數構造器:

java print(ClassUtils.hasConstructor(String.class, String.class)); print(ClassUtils.hasConstructor(String.class, Object.class));

true false

getConstructorIfAvailable

<T> Constructor<T> getConstructorIfAvailable(Class<T> clazz, Class<?>... paramTypes)返回給定型別的給定引數型別構造器,沒有的話返回null:

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

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

hasMethod

boolean hasMethod(Class<?> clazz, Method method)判斷給定型別是否有指定的方法:

java 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異常:

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

java 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的都算):

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

類似的方法還有hasAtLeastOneMethodWithName,至少得有一個。

getStaticMethod

Method getStaticMethod(Class<?> clazz, String methodName, Class<?>... args)獲取給定型別的靜態方法,如果該方法不是靜態的或者沒有這個方法,則返回null:

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

java true class java.lang.ClassLoader

FileSystemUtils

檔案系統實用工具類

deleteRecursively

boolean deleteRecursively(@Nullable File root)遞迴刪除指定檔案或目錄,刪除成功返回true,失敗返回false,不會丟擲異常。

實用File的delete目錄嘗試刪除a目錄:

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

因為a目錄包含子目錄(檔案),所以應該使用遞迴刪除:

java 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(目標路徑不存在則自動建立):

java File src = new File("a"); File dest = new File("aa"); FileSystemUtils.copyRecursively(src, dest);

過載方法void copyRecursively(Path src, Path dest)

StreamUtils

包含一些檔案流的實用方法預設的緩衝區大小為4096bytes。

注意:該工具類的所有方法都不會對流進行關閉處理!