Spring雜談 | 你真的瞭解泛型嗎?從java的Type到Spring的ResolvableType

關於泛型的基本知識在本文中不會過多提及,本文主要解決的是如何處理泛型,以及java中Type接口下對泛型的一套處理機制,進而分析Spring中的ResolvableType。

Type

簡介

Type是Java 編程語言中所有類型的公共高級接口(官方解釋),也就是Java中所有類型的“爹”;其中,“所有類型”的描述尤爲值得關注。它並不是我們平常工作中經常使用的 int、String、List、Map等數據類型,而是從Java語言角度來說,對基本類型、引用類型向上的抽象;

Type體系中類型的包括:原始類型(Class)、參數化類型(ParameterizedType)、數組類型(GenericArrayType)、類型變量(TypeVariable)、基本類型(Class);

原始類型,不僅僅包含我們平常所指的類,還包括枚舉、數組、註解等;

參數化類型,就是我們平常所用到的泛型List、Map<String,Integer>這種;

數組類型,並不是我們工作中所使用的數組String[] 、byte[],而是帶有泛型的數組,即T[] ;

基本類型,也就是我們所說的java的基本類型,即int,float,double等

Type體系的出現主要是爲了解決泛型的一系列問題。

接口定義

public interface Type {
   // 返回這個類型的名稱
    default String getTypeName() {
        return toString();
    }
}

可以看到Type接口內只定義了一個方法,這個方法會返回該類型的名稱

UML類圖

在這裏插入圖片描述
在上面的圖中對於Class我相信大家都已經很瞭解了。我們主要對其餘四個子接口進行測試分析

ParameterizedType

簡介

參數化類型,也就是我們所說的泛型。像List就是一個參數化類型,但是List並不是,因爲沒有使用泛型。

接口定義
public interface ParameterizedType extends Type {
	// 對於一個參數化類型而言,必定是帶有泛型的,所有這裏是爲了獲取到其中的泛型的具體類型,也就是<>中的內容
    // 返回一個數組是因爲,有時候會定義多個泛型,比如Map<String,String>
    Type[] getActualTypeArguments();

	// 獲取原始類型,這裏不帶泛型,就是class
    Type getRawType();

	// 獲取這個類所在類的類型,這裏可能比較拗口,舉個例子,假如當前這個ParameterizedType的類型爲
    // O<T>.I<U>,那麼調用這個方法所返回的就是一個O<T>類型
    Type getOwnerType();
}
使用示例
public class Main extends OwnerTypeDemo<String> {

    private List<String> stringList;

    private Map<String, String> stringStringMap;

    private Map.Entry<String, ?> entry;

    private OwnerTypeDemo<String>.Test<String> testOwnerType;

    private List list;

    private Map map;

    public void test(List<String> stringList, List list) {

    }

    public static void main(String[] args) {
        Class<Main> mainClass = Main.class;
        Field[] fields = mainClass.getDeclaredFields();
        for (Field field : fields) {
            Type genericType = field.getGenericType();
            String typeName = genericType.getTypeName();
            String name = field.getName();
            if (genericType instanceof ParameterizedType) {
                System.out.println(name + "是一個參數化類型,類型名稱爲:" + typeName);
                ParameterizedType parameterizedType = (ParameterizedType) genericType;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                System.out.println(name + "的actualTypeArguments:" + Arrays.toString(actualTypeArguments));
                Type ownerType = parameterizedType.getOwnerType();
                System.out.println(name + "的ownerType:" + ownerType);
                Type rawType = parameterizedType.getRawType();
                System.out.println(name + "的rawType:" + rawType);
            } else {
                System.out.println(name + "不是一個參數化類型,類型名稱爲:" + typeName);
            }
        }
        System.out.println("===================開始測試方法中的參數=========================");
        Method[] declaredMethods = mainClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            String methodName = declaredMethod.getName();
            Type[] genericParameterTypes = declaredMethod.getGenericParameterTypes();
            for (int i = 0; i < genericParameterTypes.length; i++) {
                Type parameterType = genericParameterTypes[i];
                String typeName = parameterType.getTypeName();
                System.out.println("打印" + methodName + "方法的參數," + "第" + (i + 1) + "個參數爲:" + parameterType);
                if (parameterType instanceof ParameterizedType) {
                    System.out.println("第" + (i + 1) + "個參數是一個參數化類型, 類型名稱爲 : " + typeName);
                } else {
                    System.out.println("第" + (i + 1) + "個參數不是一個參數化類型, 類型名稱爲 : " + typeName);
                }
            }
        }
        System.out.println("===================開始測試父類中的泛型=========================");
        // 獲取帶有泛型的父類
        Type genericSuperclass = mainClass.getGenericSuperclass();
        if (genericSuperclass instanceof ParameterizedType) {
            System.out.println("父類是一個參數化類型,類型名稱爲:" + genericSuperclass.getTypeName());
        }

    }
}

class OwnerTypeDemo<T> {
    class Test<T> {

    }
}

程序會做如下輸出:

stringList是一個參數化類型,類型名稱爲:java.util.List<java.lang.String>
stringList的actualTypeArguments:[class java.lang.String]
stringList的ownerType:null
stringList的rawType:interface java.util.List
stringStringMap是一個參數化類型,類型名稱爲:java.util.Map<java.lang.String, java.lang.String>
stringStringMap的actualTypeArguments:[class java.lang.String, class java.lang.String]
stringStringMap的ownerType:null
stringStringMap的rawType:interface java.util.Map
entry是一個參數化類型,類型名稱爲:java.util.Map$Entry<java.lang.String, ?>
entry的actualTypeArguments:[class java.lang.String, ?]
entry的ownerType:interface java.util.Map
entry的rawType:interface java.util.Map$Entry
testOwnerType是一個參數化類型,類型名稱爲:main.java.OwnerTypeDemo<java.lang.String>$Test<java.lang.String>
testOwnerType的actualTypeArguments:[class java.lang.String]
testOwnerType的ownerType:main.java.OwnerTypeDemo<java.lang.String>
testOwnerType的rawType:class main.java.OwnerTypeDemo$Test
list不是一個參數化類型,類型名稱爲:java.util.List
map不是一個參數化類型,類型名稱爲:java.util.Map
===================開始測試方法中的參數=========================
打印main方法的參數,第1個參數爲:class [Ljava.lang.String;1個參數不是一個參數化類型, 類型名稱爲 : java.lang.String[]
打印test方法的參數,第1個參數爲:java.util.List<java.lang.String>1個參數是一個參數化類型, 類型名稱爲 : java.util.List<java.lang.String>
打印test方法的參數,第2個參數爲:interface java.util.List2個參數不是一個參數化類型, 類型名稱爲 : java.util.List
===================開始測試父類中的泛型=========================
父類是一個參數化類型,類型名稱爲:main.java.OwnerTypeDemo<java.lang.String>

通過上面的例子可以看出,ParameterizedType可以讓我們明確字段或者方法參數上是否使用了泛型,並獲取到泛型的具體類型。那是不是依賴ParameterizedType就能解決所有的泛型問題了呢?答案顯然是不是的,我們看一個特殊的例子:

public class SpecialDemo<T extends Type> {

    T t;

    public static void main(String[] args) {
        Class<SpecialDemo> specialDemoClass = SpecialDemo.class;
        Field[] declaredFields = specialDemoClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            Type genericType = declaredField.getGenericType();
            if (genericType instanceof ParameterizedType) {
                System.out.println("t是一個參數化類型");
            } else {
                System.out.println("t不是一個參數化類型");
            }
        }
    }
    // 程序輸出:t不是一個參數化類型
}

運行上面的程序,會發現字段t不是一個參數化類型,這就意味着沒辦法通過ParameterizedType來解決這一類泛型問題。我們分析<T extends Type>,會發現其實T類似於一個變量,我們可以在使用時可以傳入具體的類,比如我們可以這樣:

SpecialDemo<ParameterizedType> specialDemo = new SpecialDemo<>();

同時這個基於這個<T extends Type>表達式,我們知道這個變量是具有屬性的,最直觀的就是T是有上界的,所有的T都繼承了Type。基於這種情況,Java對其進行了抽象,得到了一個新的類型TypeVariable

TypeVariable

簡介

類型變量,或者也可以叫泛型變量。具體就是指我們在申明泛型時定義的T,K,U這種變量。在之前的例子中,SpecialDemo<T extends Type>,T就是一個類型變量。

接口定義
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
    // 獲取泛型的邊界
    Type[] getBounds();
	// 獲取申明所在的具體對象
    D getGenericDeclaration();
	// 獲取具體類型變量的名稱
    String getName();
	// 獲取類型變量邊界上添加的註解
    AnnotatedType[] getAnnotatedBounds();
}

可以看到,TypeVariable本身也使用了泛型,並且泛型的上界爲GenericDeclaration。在瞭解TypeVariable之前,有必要先對GenericDeclaration做一個簡單的說明。GenericDeclaration這個接口主要限定了哪些地方可以定義TypeVariable,換言之,也就是定義了哪些地方可以申明泛型。這個接口只有3個實現類(忽略Executable抽象類)。如下:

在這裏插入圖片描述

從這裏我們也能看到,我們只能在方法(包括普通方法跟構造方法)以及類上申明泛型。

這裏需要對接口定義的方法做進一步的說明:

  1. getBounds()會返回泛型的邊界,但是這裏的邊界跟我們在參數化類型中定義的邊界不同,這裏的邊界只有上界。即我們不通通過super關鍵字來申明一個泛型,例如下面這種:
class A<T super classA>{}

在申明泛型時,我們要明確一點,申明是爲了使用,而在上面的例子中,我們不能使用T來幹任何事情,因爲我們不能確定T中的任何方法(只能確定它是一個Object,但是這沒有任何意義)。所以對於泛型變量來說,只存在上界,也就是隻能使用extends關鍵字進行申明

  1. getGenericDeclaration(),返回泛型申明時所在的類或者方法
  2. 返回泛型變量的名稱,也就是我們定義泛型時採用的T,K,U這一類的名稱
  3. getAnnotatedBounds(),此方法返回一個AnnotatedType類型的數組,獲取的是我們在類型變量的上界。不同於getBounds()方法的是,這個方法可以獲取到邊界上添加的註解
使用示例
public class TypeVariableMain<T, K extends @TypeAnnotation Integer & Type> {

    public <U extends Long, V> void testTypeVariable(Map<U, V> map) {

    }

    public static void main(String[] args) {
        Class<TypeVariableMain> typeVariableMainClass = TypeVariableMain.class;
        TypeVariable<Class<TypeVariableMain>>[] typeParameters = typeVariableMainClass.getTypeParameters();
        for (int i = 0; i < typeParameters.length; i++) {
            TypeVariable<Class<TypeVariableMain>> typeParameter = typeParameters[i];
            Type[] bounds = typeParameter.getBounds();
            String name = typeParameter.getName();
            AnnotatedType[] annotatedBounds = typeParameter.getAnnotatedBounds();
            Class<TypeVariableMain> genericDeclaration = typeParameter.getGenericDeclaration();
            System.out.println("第" + (i + 1) + "個類型變量的名稱爲:" + name);
            System.out.println("通過getBounds方法獲取到,第" + (i + 1) + "個類型變量的邊界爲:" + Arrays.toString(bounds));
            System.out.println("第" + (i + 1) + "個類型變量的申明的位置爲:" + genericDeclaration);
            System.out.println("通過getAnnotatedBounds方法獲取到,第" + (i + 1) + "個類型變量的邊界爲:"
                    + Arrays.stream(annotatedBounds).map(AnnotatedType::getType).collect(Collectors.toList()));
            for (AnnotatedType annotatedType : annotatedBounds) {
                Annotation[] annotations = annotatedType.getAnnotations();
                if (annotations.length > 0) {
                    System.out.println("第" + (i + 1) + "個類型變量的上界上添加了註解,註解爲" + annotations[0]);
                }
            }
        }
        System.out.println("===================基於方法獲取類型變量====================");
        Method[] declaredMethods = typeVariableMainClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            String methodName = declaredMethod.getName();
            if (methodName.equals("main")) {
                // 爲了方便,直接排除main函數了
                continue;
            }
            TypeVariable<Method>[] typeVariables = declaredMethod.getTypeParameters();
            int i = 1;
            for (TypeVariable<Method> typeVariable : typeVariables) {
                System.out.println("方法:\"" + methodName + "\"的第" + (i++) + "個類型變量爲" + typeVariable.getName());
            }
        }
    }
}

程序打印如下:

1個類型變量的名稱爲:T
通過getBounds方法獲取到,第1個類型變量的邊界爲:[class java.lang.Object]1個類型變量的申明的位置爲:class main.java.TypeVariableMain
通過getAnnotatedBounds方法獲取到,第1個類型變量的邊界爲:[class java.lang.Object]2個類型變量的名稱爲:K
通過getBounds方法獲取到,第2個類型變量的邊界爲:[class java.lang.Integer, interface java.lang.reflect.Type]2個類型變量的申明的位置爲:class main.java.TypeVariableMain
通過getAnnotatedBounds方法獲取到,第2個類型變量的邊界爲:[class java.lang.Integer, interface java.lang.reflect.Type]2個類型變量的上界上添加了註解,註解爲@main.java.TypeAnnotation()
===================基於方法獲取類型變量====================
方法:"testTypeVariable"的第1個類型變量爲U
方法:"testTypeVariable"的第2個類型變量爲V

爲了讓大家加深對ParameterizedType以及TypeVariable理解,這裏我額外添加一個Demo

public class TypeVariableMain02<T, K extends @TypeAnnotation Integer & Type> {

    private K k;

    private List<T> list;

    public static void main(String[] args) {
        Class<TypeVariableMain02> typeVariableMain02Class = TypeVariableMain02.class;

        Field[] declaredFields = typeVariableMain02Class.getDeclaredFields();
        for (Field field : declaredFields) {
            Type genericType = field.getGenericType();
            String typeName = genericType.getTypeName();
            String name = field.getName();
            if (genericType instanceof ParameterizedType) {
                System.out.println(name + "是一個參數化類型,類型名稱爲:" + typeName);
                ParameterizedType parameterizedType = (ParameterizedType) genericType;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                System.out.println(name + "的actualTypeArguments:" + Arrays.toString(actualTypeArguments));
                Type ownerType = parameterizedType.getOwnerType();
                System.out.println(name + "的ownerType:" + ownerType);
                Type rawType = parameterizedType.getRawType();
                System.out.println(name + "的rawType:" + rawType);
                for (Type actualTypeArgument : actualTypeArguments) {
                    if (actualTypeArgument instanceof TypeVariable) {
                        System.out.println("字段:" + name + "中包含一個類型變量");
                        String name1 = ((TypeVariable) actualTypeArgument).getName();
                        AnnotatedType[] annotatedBounds = ((TypeVariable) actualTypeArgument).getAnnotatedBounds();
                        Type[] bounds = ((TypeVariable) actualTypeArgument).getBounds();
                        GenericDeclaration genericDeclaration = ((TypeVariable) actualTypeArgument).getGenericDeclaration();
                        System.out.println("類型變量的名稱爲:" + name1);
                        System.out.println("個類型變量的邊界爲:" + Arrays.toString(bounds));
                        System.out.println("類型變量的申明的位置爲:" + genericDeclaration);
                        System.out.println("通過getAnnotatedBounds方法獲取到,類型變量的邊界爲:" + annotatedBounds[0].getType());
                    }
                }
            } else if (genericType instanceof TypeVariable) {
                System.out.println(name + "是一個類型變量,類型名稱爲:" + typeName);
                TypeVariable typeVariable = (TypeVariable) genericType;
                Type[] bounds = typeVariable.getBounds();
                String name1 = typeVariable.getName();
                AnnotatedType[] annotatedBounds = typeVariable.getAnnotatedBounds();
                GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
                System.out.println("類型變量的名稱爲:" + name1);
                System.out.println("個類型變量的邊界爲:" + Arrays.toString(bounds));
                System.out.println("類型變量的申明的位置爲:" + genericDeclaration);
                System.out.println("通過getAnnotatedBounds方法獲取到,類型變量的邊界爲:" + annotatedBounds[0].getType() + "  " + annotatedBounds[1].getType());
            }
        }
    }
}

程序輸出:

k是一個類型變量,類型名稱爲:K
類型變量的名稱爲:K
個類型變量的邊界爲:[class java.lang.Integer, interface java.lang.reflect.Type]
類型變量的申明的位置爲:class main.java.TypeVariableMain02
通過getAnnotatedBounds方法獲取到,類型變量的邊界爲:class java.lang.Integer  interface java.lang.reflect.Type
list是一個參數化類型,類型名稱爲:java.util.List<T>
list的actualTypeArguments:[T]
list的ownerType:null
list的rawType:interface java.util.List
字段:list 中包含一個類型變量
類型變量的名稱爲:T
個類型變量的邊界爲:[class java.lang.Object]
類型變量的申明的位置爲:class main.java.TypeVariableMain02
通過getAnnotatedBounds方法獲取到,類型變量的邊界爲:class java.lang.Object

GenericArrayType

簡介

GenericArrayType是Type的子接口,用於表示“泛型數組”,描述的是形如:A[]或T[]的類型。其實也就是描述ParameterizedType類型以及TypeVariable類型的數組,即形如:classA[][]、T[]等

接口定義
public interface GenericArrayType extends Type {
    // 返回數組中元素的類型,TypeVariable或者ParameterizedType
    Type getGenericComponentType();
}
使用示例
public class GenericArrayTypeMain<T> {
    T[] t1;

    T[][] t2;

    List<T> list;

    List<String>[] stringListArray;

    String[][] stringArray;

    public static void main(String[] args) {
        Class<GenericArrayTypeMain> genericArrayTypeMainClass = GenericArrayTypeMain.class;
        Field[] declaredFields = genericArrayTypeMainClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            String name = declaredField.getName();
            Type genericType = declaredField.getGenericType();
            if (genericType instanceof GenericArrayType) {
                System.out.println(name + "是一個泛型數組");
                Type genericComponentType = ((GenericArrayType) genericType).getGenericComponentType();
                System.out.println("數組的元素類型爲:" + genericComponentType);
            } else {
                System.out.println(name + "不是一個泛型數組");
            }
        }
    }
}

程序輸出:

t1是一個泛型數組
數組的元素類型爲:T
t2是一個泛型數組
數組的元素類型爲:T[]
list不是一個泛型數組
stringListArray是一個泛型數組
數組的元素類型爲:java.util.List<java.lang.String>
stringArray不是一個泛型數組

通過上面的Demo我們會發現,無論從左向右有幾個[]並列,這個方法僅僅脫去最右邊的[]之後剩下的內容就作爲這個方法的返回值。

另外,在上面的例子中,大家可以思考以下幾個問題:

  1. t1是一個泛型數組,數組的元素類型爲:T,那麼T是一個什麼類型呢?
  2. t2是一個泛型數組,數組的元素類型爲:T[],那麼T[]又是什麼類型?

上述問題留給大家自行思考


瞭解了ParameterizedType跟TypeVariable以及這兩種類型的數組類型GenericArrayType之後,接着我們思考一個問題,我們在定義泛型時,經常會使用來通配符,形如下面這種形式{? extends Number},這個時候即使我們獲取到? extends Number也沒有辦法做進一步的處理。這個時候就要用到我們接下來要介紹的這個接口了,請往下看

WildcardType

簡介

專門用來處理泛型中的通配符,需要注意的是,WildcardType並不是JAVA所有類型中的一種,表示的僅僅是類似 {? extends T}{? super K}這樣的通配符表達式。

接口定義
public interface WildcardType extends Type {
	
    // 獲取通配符表達式的上界
    Type[] getUpperBounds();
	
    // 獲取通配符表達式的下界
    Type[] getLowerBounds();

}

上面這兩個方法之所以會返回數組是爲了保持擴展性,實際上現在返回的數組的大小就是1,JDK8中至少是這樣的嗎,更高版本的沒有去嘗試。

使用示例
public class WildcardTypeDemo<T> {

    Map<? super String, ? extends List<T>> map;

    public static void main(String[] args) {
        Class<WildcardTypeDemo> wildcardTypeDemoClass = WildcardTypeDemo.class;
        Field[] declaredFields = wildcardTypeDemoClass.getDeclaredFields();
        for (Field field : declaredFields) {
            Type genericType = field.getGenericType();
            if (genericType instanceof ParameterizedType) {
                // 使用了通配符表達泛型的,必定是一個參數化類型
                // 獲取泛型的實際類型,就是獲取<>中的內容,這裏獲取到的是<? super String, ? extends List<T>>
                Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    // 兩個泛型都使用了通配符,都會進入這個判斷
                    if (actualTypeArgument instanceof WildcardType) {
                        // 分別獲取上界跟下界
                        // ? super String,這個表達式的下界爲String,上界爲Object
                        // ? extends List<T>,這個表達式的下界爲Object,上界爲List<T>,
                        // 同時List<T>又是一個參數化類型,而T又是一個類型變量
                        Type[] lowerBounds = ((WildcardType) actualTypeArgument).getLowerBounds();
                        Type[] upperBounds = ((WildcardType) actualTypeArgument).getUpperBounds();
                        // 這裏我主要處理? extends List<T>
                        for (Type upperBound : upperBounds) {
                            if (upperBound instanceof ParameterizedType) {
                                System.out.println("參數化類型的名稱爲:" + upperBound.getTypeName());
                                Type[] actualTypeArguments1 = ((ParameterizedType) upperBound).getActualTypeArguments();
                                for (Type type : actualTypeArguments1) {
                                    if (type instanceof TypeVariable) {
                                        String name = ((TypeVariable) type).getName();
                                        System.out.println("類型變量名稱爲:" + name);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    
    // 程序輸出:
    // 參數化類型的名稱爲:java.util.List<T>
	// 類型變量名稱爲:T

我相信如果你對Java中的類型已經完全理解了,上面的代碼配合註釋應該不難看懂

ResolvableType

在學習了Java的Type體系後,我們會發現,依賴於整個Type體系去處理泛型代碼非常的繁瑣,並且不易於理解。基於這種情況,Spring開發了一個ResolvableType類,這個類對整個Type體系做了系統的封裝。

實際上關於ResolvableType的學習大家可以參數Spring中的org.springframework.core.ResolvableTypeTests類,這是作者寫好的單元測試類,覆蓋了ResolvableType的所有方法。

這個類的代碼量很大,不過我們也沒有必要去詳細地看每一行代碼,粗略閱讀源碼後會發現這個類有以下幾個特點

概覽

  1. 所有的構造函數都是私有的

在這裏插入圖片描述

在上圖中那把小鎖代表權限爲private,就是私有的意思

  1. 構造函數都是在爲相同的成員變量賦值,這裏我隨便放一個構造函數如下
private ResolvableType(Type type, @Nullable TypeProvider typeProvider,
                       @Nullable VariableResolver variableResolver, @Nullable ResolvableType componentType) {

    this.type = type;
    this.typeProvider = typeProvider;
    this.variableResolver = variableResolver;
    this.componentType = componentType;
    this.hash = null;
    this.resolved = resolveClass();
}
  1. 因爲構造函數是私有的,所有它提供了一系列的方法來創建一個ResolvableType,如下:

在這裏插入圖片描述

所有for開頭的方法都是靜態方法,同時都能獲取一個ResolvableType,現在對常見的幾個方法進行分析:

方法分析

forClass系列方法

Spring中經常會用到一個方法,ResolvableType.forRawClass(type),我們就先看下這一系列的三個方法

  1. ResolvableType.forRawClass(type)
  2. ResolvableType forClass(@Nullable Class<?> clazz)
  3. ResolvableType forClass(Class<?> baseType, Class<?> implementationClass)
  4. ResolvableType forClassWithGenerics(Class<?> clazz, Class<?>… generics)
  5. ResolvableType forClassWithGenerics(Class<?> clazz, ResolvableType… generics)

forRawClass(Class<?> clazz)

public static ResolvableType forRawClass(@Nullable Class<?> clazz) {
    return new ResolvableType(clazz) {
        @Override
        public ResolvableType[] getGenerics() {
            return EMPTY_TYPES_ARRAY;
        }
        @Override
        public boolean isAssignableFrom(Class<?> other) {
            return (clazz == null || ClassUtils.isAssignable(clazz, other));
        }
        @Override
        public boolean isAssignableFrom(ResolvableType other) {
            Class<?> otherClass = other.getRawClass();
            return (otherClass != null && (clazz == null || ClassUtils.isAssignable(clazz, otherClass)));
        }
    };
}

這個方法實際上做了兩件事

  1. 調用了構造函數,private ResolvableType(@Nullable Class<?> clazz)
  2. 複寫了三個方法

對比另外一個方法

forClass(Class<?> clazz)

public static ResolvableType forClass(@Nullable Class<?> clazz) {
    return new ResolvableType(clazz);
}

對比後可以發現,這兩個方法唯一的區別就是沒有複寫其中的三個方法。大家可以思考下,這是爲什麼呢?

其實區別在於,對於第一個forRawClass方法,入參傳入的一定是一個原始數據類型,也就是一個不帶泛型的類的Class對象,比如傳入的可能是一個Person.class,Dog.class。對於這種原始數據類型,其getGenericsisAssignableFrom方法的實現邏輯是固定的,所以forRawClass方法直接對這三個方法進行了複寫。

forClass(Class<?> baseType, Class<?> implementationClass)

public static ResolvableType forClass(Class<?> baseType, Class<?> implementationClass) {
    Assert.notNull(baseType, "Base type must not be null");
    // as方法在之後分析,就是根據繼承鏈找打對應的父類
    ResolvableType asType = forType(implementationClass).as(baseType);
    return (asType == NONE ? forType(baseType) : asType);
}

implementationClass是baseType的子類,這個方法主要獲取baseType上定義的泛型,例如:

public class ResolvableTypeDemo {
    public static void main(String[] args) {
  		// 獲取到C繼承的HashMap所構建的一個ResolvableType,會帶用泛型<String, Integer>
        ResolvableType resolvableType = ResolvableType.forClass(HashMap.class, C.class);
        ResolvableType[] generics = resolvableType.getGenerics();
        for (ResolvableType generic : generics) {
            // 程序打印:
            // class java.lang.String
            // class java.lang.Integer
            System.out.println(generic.getType());
        }
    }
}

class C extends HashMap<String, Integer> {

}

forConstructor系列方法

public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex,
                                                     Class<?> implementationClass) {

    Assert.notNull(constructor, "Constructor must not be null");
    MethodParameter methodParameter = new MethodParameter(constructor, parameterIndex);
    methodParameter.setContainingClass(implementationClass);
    return forMethodParameter(methodParameter);
}

public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex) {
    Assert.notNull(constructor, "Constructor must not be null");
    return forMethodParameter(new MethodParameter(constructor, parameterIndex));
}

可以看到,forConstructor系列方法最後都調用了forMethod系列方法,我們直接分析forMethod系列的方法

forMethod系列方法

在這裏插入圖片描述

主要分爲兩類方法

  1. forMethodParameter,解決方法參數上的類型問題
  2. forMethodReturnType,解決方法返回值的類型問題

forMethodParameter

public class ResolvableTypeDemo {

    public void test(List<String> list, Map<String, List<Integer>> map) {

    }

    public static void main(String[] args) throws Exception {
        Class<ResolvableTypeDemo> resolvableTypeDemoClass = ResolvableTypeDemo.class;
        Method[] declaredMethods = resolvableTypeDemoClass.getDeclaredMethods();
        Method test = declaredMethods[1];
        // 獲取方法的第一個參數對應的ResolvableType,參數爲-1代表返回值,0爲第一個,1爲第二個,一次增加
        ResolvableType resolvableType0 = ResolvableType.forMethodParameter(test, 0);
        System.out.println(resolvableType0.resolve());
        System.out.println(resolvableType0.getType());
         // 獲取方法的第二個參數對應的ResolvableType
        ResolvableType resolvableType1 = ResolvableType.forMethodParameter(test, 1);
        System.out.println(resolvableType1.resolve());
        System.out.println(resolvableType1.getType());
    }
}

forMethodReturnType

public static ResolvableType forMethodReturnType(Method method) {
    Assert.notNull(method, "Method must not be null");
    return forMethodParameter(new MethodParameter(method, -1));
}

調用邏輯很簡單,調用forMethodParameter,並將方法的參數索引替換爲-1,代表返回值

forConstructor系列方法

構造函數就是一個特殊的方法,所以都是直接調用的forMethod系列方法,這裏就不多介紹了

forField系列方法

專門用於處理字段的類型,如下:

在這裏插入圖片描述

測試方法Demo

public class ResolvableTypeDemo {

    List<String> stringList;

    List<List<String>> lists;

    public static void main(String[] args) throws Exception {
        Class<ResolvableTypeDemo> resolvableTypeDemoClass = ResolvableTypeDemo.class;
        Field[] declaredFields = resolvableTypeDemoClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("=======字段名稱"+declaredField.getName()+"=========");
            System.out.println("nestingLevel爲1");
            ResolvableType resolvableType1 = ResolvableType.forField(declaredField,1);
            System.out.println(resolvableType1.getType());
            System.out.println(resolvableType1.resolve());
            System.out.println("nestingLevel爲2");
            ResolvableType resolvableType2 = ResolvableType.forField(declaredField,2);
            System.out.println(resolvableType2.getType());
            System.out.println(resolvableType2.resolve());
            System.out.println("nestingLevel爲3");
            ResolvableType resolvableType3 = ResolvableType.forField(declaredField,3);
            System.out.println(resolvableType3.getType());
            System.out.println(resolvableType3.resolve());
        }
    }
}

程序打印:

=======字段名稱stringList=========
nestingLevel爲1
java.util.List<java.lang.String>
interface java.util.List
nestingLevel爲2
class java.lang.String
class java.lang.String
nestingLevel爲3
org.springframework.core.ResolvableType$EmptyType@723279cf
null
=======字段名稱lists=========
nestingLevel爲1
java.util.List<java.util.List<java.lang.String>>
interface java.util.List
nestingLevel爲2
java.util.List<java.lang.String>
interface java.util.List
nestingLevel爲3
class java.lang.String
class java.lang.String

在上面的所有方法,最後都會調用一個forType方法,所以我們着重也就分析這個系列的方法

forType系列(源碼分析)

在這裏插入圖片描述

最終都會調用到這個方法中,源碼如下:

static ResolvableType forType(
    @Nullable Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) {
	
    // 這裏可以看出,即使我們提供了一個typeProvider,也不會直接調用它的getType返回,而是會進行一層包裝,這個是爲什麼呢?我們稍後分析
    if (type == null && typeProvider != null) {
        type = SerializableTypeWrapper.forTypeProvider(typeProvider);
    }
    
    if (type == null) {
        // 自身定義的一個常量,ResolvableType NONE = new ResolvableType(EmptyType.INSTANCE, null, null, 0);
        return NONE;
    }

	// 如果是原始的數據類型(一個簡單的Class引用),那麼直接封裝後返回,這裏不做緩存,因爲沒有上面昂貴的開銷
    if (type instanceof Class) {
        return new ResolvableType(type, typeProvider, variableResolver, (ResolvableType) null);
    }

	// 省略緩存相關的代碼。。。
    return resultType;
}

上面這段代碼比較核心的就是SerializableTypeWrapper.forTypeProvider(typeProvider),我之前也提到了一個問題,爲什麼要多包裝一層呢?這麼做的目的主要就是爲了得到一個可以進行序列化的Type。

它的核心代碼如下:

static Type forTypeProvider(TypeProvider provider) {
    // 直接從provider獲取到具體的類型
    Type providedType = provider.getType();
    if (providedType == null || providedType instanceof Serializable) {
        // 如果本身可以序列化的直接返回,例如Java.lang.Class。
        // 如果不能進行序列化,多進行一層包裝
        return providedType;
    }
    // 不用管這段代碼,我們開發過程中必定不成立
    if (GraalDetector.inImageCode() || !Serializable.class.isAssignableFrom(Class.class)) {
        return providedType;
    }

    // 從緩存中獲取
    Type cached = cache.get(providedType);
    if (cached != null) {
        return cached;
    }
    // 遍歷支持的集合,就是GenericArrayType.class, ParameterizedType.class, TypeVariable.class, WildcardType.class,處理這個四種類型
    for (Class<?> type : SUPPORTED_SERIALIZABLE_TYPES) {
        if (type.isInstance(providedType)) {
            ClassLoader classLoader = provider.getClass().getClassLoader();
            // 創建的代理類實現的接口,type就不用說了代理類跟目標類必須是同一個類型
            // SerializableTypeProxy:標記接口,標誌是一個代理類
            // Serializable:代表可以被序列化
            Class<?>[] interfaces = new Class<?>[] {type, SerializableTypeProxy.class, Serializable.class};
            // 核心代碼:TypeProxyInvocationHandler是什麼?
            InvocationHandler handler = new TypeProxyInvocationHandler(provider);
            // 依賴於先前的InvocationHandler,以當前的type爲目標對象創建了一個代理對象
            // 
            cached = (Type) Proxy.newProxyInstance(classLoader, interfaces, handler);
            cache.put(providedType, cached);
            return cached;
        }
    }
    throw new IllegalArgumentException("Unsupported Type class: " + providedType.getClass().getName());
}

解析來我們分下下TypeProxyInvocationHandler這個類

private static class TypeProxyInvocationHandler implements InvocationHandler, Serializable {

    private final TypeProvider provider;

    public TypeProxyInvocationHandler(TypeProvider provider) {
        this.provider = provider;
    }

    @Override
    @Nullable
    public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
        // 複寫目標類的equals方法
        if (method.getName().equals("equals") && args != null) {
            Object other = args[0];
            // Unwrap proxies for speed
            if (other instanceof Type) {
                other = unwrap((Type) other);
            }
            return ObjectUtils.nullSafeEquals(this.provider.getType(), other);
        }
        // 複寫目標類的hashCode方法
        else if (method.getName().equals("hashCode")) {
            return ObjectUtils.nullSafeHashCode(this.provider.getType());
        }
        
         // 複寫目標類的getTypeProvider方法
        else if (method.getName().equals("getTypeProvider")) {
            return this.provider;
        }
		
        // 之所以不直接返回method.invoke(this.provider.getType(), args);也是爲了緩存
        // 空參的時候才能緩存,帶參數的話不能緩存,因爲每次調用傳入的參數可能不一樣
        if (Type.class == method.getReturnType() && args == null) {
            return forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, -1));
        }
        else if (Type[].class == method.getReturnType() && args == null) {
            Type[] result = new Type[((Type[]) method.invoke(this.provider.getType())).length];
            for (int i = 0; i < result.length; i++) {
                result[i] = forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, i));
            }
            return result;
        }

        try {
            return method.invoke(this.provider.getType(), args);
        }
        catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }
}

總結

在這篇文章中我們主要學習了java的Type機制,如下:

在這裏插入圖片描述

Type主要是用來處理泛型的,但是通過Java原始的這一套,處理起來及其的繁瑣,所以Spring自行封裝了一個ResolvableType,我們在處理類,方法,構造函數,字段時,只需要調用對應的方法就能返回一個對應的ResolvableType,一個ResolvableType就封裝了對應的這個對象的原始類型,泛型等等,封裝了Java中的所有類型。從這裏也能看出Spring的牛逼之處,處理提供了IOC,AOP這兩個強大的功能,還封裝了一系列的簡單易用的工具類。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章