Spring裡面常用的反射例項化技巧
ResolvableType,可解決的資料型別。它為java語言中的所有型別提供了相同的資料結構,其內部封裝了一個java.lang.reflect.Type型別的物件。
在講解這個資料結構之前,首先要了解一些預備知識,我們不妨思考如下2個問題:
在java語法中,哪些元素可以代表一種型別? 在java語法中,哪些元素具有型別? 在jdk中,Type介面代表一種型別,所有的具體型別都需要實現這個介面。 從圖中可以看出,java語法中的型別可以分為五大類:元件型別為引數化型別或型別變數的陣列、引數化型別、萬用字元表示式型別、型別變數以及所有定義的Class(每個類都是一個具體的型別)。除Class類以外的4個介面是jdk1.5以後出現的,因為單純的Class類無法描述泛型資訊。 回到之前提到的兩個問題,現在第一個問題已經得到了答案。那麼,java中哪些元素具有型別的屬性呢?答案是:只有變數(或者說值,因為變數是值的載體)才具有型別。那麼什麼是變數呢?變數根據其所在位置不同,包括:成員變數、區域性變數、方法形參以及方法返回值。 Class是一種型別,但它本身不具有型別的屬性。
getSuperType():獲取直接父型別 getInterfaces():獲取介面型別 getGeneric(int…):獲取型別攜帶的泛型型別 resolve():Type物件到Class物件的轉換 另外,ResolvableType的構造方法全部為私有的,我們不能直接new,只能使用其提供的靜態方法進行型別獲取:
forField(Field):獲取指定欄位的型別 forMethodParameter(Method, int):獲取指定方法的指定形參的型別 forMethodReturnType(Method):獲取指定方法的返回值的型別 forClass(Class):直接封裝指定的型別
package com.sise.test; import org.junit.Assert; import org.junit.Test; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; /** * @author hao_lin * @data 2018/10/8 10:46 */ public class ResolvableTypeTest { static class ExtendsList extends ArrayList<CharSequence> { public String field1; public List<String> stringList(int id) { List<String> list = new ArrayList<>(); list.add(id + "iii"); return list; } public void handlePassword(HeadVO<User, Accout> param) { } } /** * 如何防止某個引數為空,進行反射例項化的做法 * * @throws NoSuchMethodException * @throws IllegalAccessException * @throws InstantiationException */ @Test public void handleParameter() throws NoSuchMethodException, IllegalAccessException, InstantiationException { Method method = ExtendsList.class.getMethod("handlePassword", HeadVO.class); //獲取到相應方法的第一個引數 ResolvableType resolvableType = ResolvableType.forMethodParameter(method, 0); //獲取到相應方法的第一個引數的泛型型別的第二個引數型別 Class<?> bodyClass = resolvableType.getGeneric(1).resolve(); Accout accout = (Accout) bodyClass.newInstance(); System.out.println(accout.toString()); } /** * 獲取類名 */ @Test public void forClass() { ResolvableType type = ResolvableType.forClass(ExtendsList.class); Assert.assertTrue(type.getType().equals((Type) ExtendsList.class)); } /** * 獲取欄位的名稱 */ @Test public void forField() throws NoSuchFieldException { //公開方法才有許可權獲取 Field[] fields = ExtendsList.class.getFields(); for (Field field : fields) { System.out.println(field.getName()); } //公開方法才有許可權獲取 Field field = ExtendsList.class.getField("field1"); //獲取到相應欄位是屬於String型別 ResolvableType type = ResolvableType.forField(field); Assert.assertTrue(type.getType().equals(field.getGenericType())); } /** * 獲取一個方法的引數型別 * * @throws NoSuchMethodException */ @Test public void forMethodParameter() throws NoSuchMethodException { Method method = ExtendsList.class.getMethod("stringList", int.class); MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(method, 0); ResolvableType type = ResolvableType.forMethodParameter(methodParameter); Assert.assertTrue(type.getType().equals(method.getGenericParameterTypes()[0])); } /** * 獲取方法的返回型別 * * @throws NoSuchMethodException */ @Test public void forMethodReturnType() throws NoSuchMethodException { Method method = ExtendsList.class.getMethod("stringList", int.class); ResolvableType type = ResolvableType.forMethodReturnType(method); System.out.println(type.getType()); MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(method, 0); ResolvableType type1 = ResolvableType.forMethodParameter(methodParameter); System.out.println(type1.getType()); } /** * 獲取自身的一個型別 */ @Test public void forClassTest() { ResolvableType type = ResolvableType.forClass(ExtendsList.class); System.out.println(type.getType()); System.out.println(type.getRawClass()); Assert.assertTrue(type.getType().equals(ExtendsList.class)); Assert.assertTrue(type.getRawClass().equals(ExtendsList.class)); } /** * 獲取該類的父類型別 */ @Test public void getSuperTypeTest() { ResolvableType type = ResolvableType.forType(ExtendsList.class); ResolvableType superType = type.getSuperType(); System.out.println(superType.getType()); System.out.println(superType.getRawClass()); System.out.println(superType.getGeneric().getType()); } /** * 可以將ExtendsList以as的方式轉換一下,向上取介面或父類 */ @Test public void asTest() { ResolvableType type = ResolvableType.forType(ExtendsList.class); ResolvableType listType = type.as(ArrayList.class); System.out.println(listType.getType()); System.out.println(listType.getRawClass()); //獲取到繼承的父類的泛型型別 System.out.println(listType.getGeneric().getType()); } }
最後,總結一下ResolvableType的使用場景。它的使用場景是非常廣泛的,在spring框架中需要反射的時候,為了不丟失泛型資訊,通常都會使用ResolvableType封裝所有的型別。