首頁  >  文章  >  Java  >  SpringBoot如何實作使用反射模擬IOC和getBean

SpringBoot如何實作使用反射模擬IOC和getBean

王林
王林轉載
2023-05-30 09:25:121031瀏覽

spring基礎思想IOC

其次就是java的反射,反射機制是spring的重要實現核心,今天我看spring的三級緩存解決循壞引用的問題時,發現一個bean的生命週期與java物件的產生流程具備高度相似性,接著我就去重溫了一下bean的創建流程,發現一個bean實例從無到有經歷的過程非常有意思,spring用極其優雅的程式碼​​實現了用反射和各種map資料結構實現了bean的管線生產,非常優雅,於是我就嘗試用反射寫一個逆向生成實例物件的小玩意。

那麼前置需要了解一個物件產生的過程:

我將物件的建立過程總結為:

檢查常數池是否存在該物件的符號參考並確定是否經過類別載入過程,都沒有則進行類別載入過程。

為新生物件分配記憶體(兩種方式:指標碰撞和空閒清單 )並將除物件頭外的其他記憶體空間賦值W為0。

設定物件頭。

物件的初始化,這個就是執行你的建構方法的過程,給你需要的欄位賦值你想要定義的值。

補充一下其中的細節:為新生物件分配記憶體過程中,首先一個物件在類別載入完成後它所需要的記憶體大小是完全確定的,分配記憶體的過程其實就是在java堆裡劃分一塊等大的記憶體給它,但是該怎麼劃分呢?如果java堆的記憶體佈局是嚴格的順序分配,即一邊是使用過的,一邊是空閒的,那麼就會採取指針碰撞的方式分配內存,所謂的指針在空閒區與使用區的分界線處,收到記憶體需求時,指標會向後移動直到移動所覆蓋的長度等於java物件所需的記憶體大小時停止並進行分配。但如果java堆的記憶體佈局是碎片化的不連續的呢?我們就只能維護一個列表,這個列表記錄了所有java堆空閒區的大小與位置信息,分配時只需要查找最適合新生對象的區域分配即可。

垃圾收集器的能力和是否能夠進行空間壓縮整理決定了Java堆是否規則。當我們採用的收集器是Serial與Parnew時是用指標碰撞的方式分配的,當採用的是CMS垃圾收集器的時候,則是需要使用麻煩的空閒區表分配。

這裡我們著重的去關注屬性與方法的填充即可:一個物件的靈魂就是它的屬性與方法:

整個工具用到的核心屬性:

    private static volatile Constructor<?> constructor;
    private static volatile Object newInstance;
    private static volatile Map<String, Method> methodMap;

我們先看看這幾個方法的作用:

  public static Constructor<?> getConstructor(Object dataType) {
        Class<?> typeClass = dataType.getClass();
        try {
            Constructor<?> constructor = typeClass.getConstructor();
            constructor.setAccessible(true);
            return constructor;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            return null;
        }
    }

取得類型的構造器,注意這可是無參構造,如果你沒有無參構造那麼很有可能報錯,因為我們也不知道它有多少屬性對吧?(時刻記住咱們是逆向!!!不知道這個類型裡有什麼!!!一切都是反射帶來的信息)

public static void fillValueToNewInstance(Object dataType, Map<String, Object> initialMap) throws Exception {
        constructor = getConstructor(dataType);
        Class<?> typeClass = dataType.getClass();
        Field[] declaredFields = typeClass.getDeclaredFields();
        Iterator<Field> fieldIterator = Arrays.stream(declaredFields).iterator();
        newInstance = constructor.newInstance();
        while (fieldIterator.hasNext()) {
            Field field = fieldIterator.next();
            field.setAccessible(true);
            if (initialMap != null)
                field.set(newInstance, initialMap.get(field.getName()));
        }
    }

獲取屬性並填充屬性值,這裡也順帶著將屬性給進去了。

 public static Method[] getMethodArray(Object dataType) {
        return dataType.getClass().getDeclaredMethods();
    }

取得一切方法組成方法陣列。

  public static void fillMethodMap(Object dataType) {
        methodMap = new HashMap<>();
        Method[] methodArray = getMethodArray(dataType);
        Iterator<Method> iterator = Arrays.stream(methodArray).iterator();
        while (iterator.hasNext()) {
            Method method = iterator.next();
            method.setAccessible(true);
            methodMap.put(method.getName(), method);
        }
    }

將方法存到方法集合中去儲存。

 public static Object useMethod(String methodName, @Nullable Object... parameters) throws Exception {
        return methodMap.get(methodName).invoke(newInstance, parameters);
    }

使用方法要透過名稱。

    @SneakyThrows
    public static Object getBean(Object dataType, Map<String, Object> parameterMap) {
        fillValueToNewInstance(dataType, parameterMap);
        fillMethodMap(dataType);
        return newInstance;
    }

getBean方法。

  public static void main(String[] args) throws Exception {
        Map<String,Object> map = new HashMap<>();
        map.put("name","xu");
        map.put("age",Integer.valueOf(18));
        map.put("sex",&#39;女&#39;);
        Person bean = (Person) getBean(new Person(), map);
        System.out.println(bean.toString());
        System.out.println(useMethod("toString"));
    }

測試方法。類型資訊如下:

class Person {
    private String name;
    private Integer age;
    private Character sex;
    //无参构造绝对不能少
    public Person() {
    }
    @Override
    public String toString() {
        return "Person{" +
                "name=&#39;" + name + &#39;\&#39;&#39; +
                ", age=" + age +
                ", sex=" + sex +
                &#39;}&#39;;
    }
}

測試結果如下:

SpringBoot如何實作使用反射模擬IOC和getBean

這裡我們可沒有用Person person = new Person();的方式實例化對象,用反射實現了物件的實例化。

裡面用到關於反射的方法我列下來:

getDeclaredFields 取得域屬性物件

getName 取得屬性名稱

##getType 取得屬性類型的字節碼檔案

setAccessible(true) 設定暴力破解,取得對私有屬性的使用

getDeclaredMethods 取得全部方法陣列

getClass 取得字節碼檔案

getConstructor 取得無參考構造器

以上是SpringBoot如何實作使用反射模擬IOC和getBean的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除