1. 程式人生 > >Spring裡面常用的反射例項化技巧

Spring裡面常用的反射例項化技巧

ResolvableType,可解決的資料型別。它為java語言中的所有型別提供了相同的資料結構,其內部封裝了一個java.lang.reflect.Type型別的物件。

在講解這個資料結構之前,首先要了解一些預備知識,我們不妨思考如下2個問題:

在java語法中,哪些元素可以代表一種型別? 在java語法中,哪些元素具有型別? 在jdk中,Type介面代表一種型別,所有的具體型別都需要實現這個介面。 在這裡插入圖片描述 從圖中可以看出,java語法中的型別可以分為五大類:元件型別為引數化型別或型別變數的陣列、引數化型別、萬用字元表示式型別、型別變數以及所有定義的Class(每個類都是一個具體的型別)。除Class類以外的4個介面是jdk1.5以後出現的,因為單純的Class類無法描述泛型資訊。 回到之前提到的兩個問題,現在第一個問題已經得到了答案。那麼,java中哪些元素具有型別的屬性呢?答案是:只有變數(或者說值,因為變數是值的載體)才具有型別。那麼什麼是變數呢?變數根據其所在位置不同,包括:成員變數、區域性變數、方法形參以及方法返回值。 Class是一種型別,但它本身不具有型別的屬性。

言歸正傳,下面講解ResolvableType。ResolvableType為所有的java型別提供了統一的資料結構以及API,換句話說,一個ResolvableType物件就對應著一種java型別。我們可以通過ResolvableType物件獲取型別攜帶的資訊(舉例如下):

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封裝所有的型別。