動態代理簡單實現

動態代理簡單實現

一、反射

●Reflection (反射)是被視爲動態語言的關鍵,反射機制允許程序在執行期
藉助於Reflection API取得任何類的內部信息,並能直接操作任意對象的內
部屬性及方法。

●加載完類之後,在堆內存的方法區中就產生了一個Class類型的對象(一箇中
類只有一個Class對象),這個對象就包含了完整的類的結構信息。我們可
以通過這個對象看到類的結構。這個對象就像一面鏡子,透過這個鏡子看
到類的結構,所以,我們形象的稱之爲:反射。
正常方式: 引入需要的“包類”名稱 -》通過new實例化-》取得實例化對象

反射方式:實例化對象-》getClass()方法-》得到完整的“包類”名稱

框架 = 反射 + 註解 + 設計模式

二、反射機制提供的功能

➢在運行時判斷任意一個對象所屬的類
➢在運行時構造任意一個類的對象
➢在運行時判斷任意一個類所具有的成員變量和方法
➢在運行時獲取泛型信息
➢在運行時調用任意一-個對象的成員變量和方法
➢在運行時處理註解
➢生成動態代理

1.相關API

java.lang.Class:反射的源頭
java.lang.reflect.Method
java.lang.reflect.Field
java.lang.reflect.Constructor

2.Class類的理解

1.類的加載過程:
程序經過javac.exe命令以後,會生成一個或多個字節碼文件(.class結尾)。接者我們使用java. exe命令對某個字節碼文件進行解釋運行。相當於將某個字節碼文件加載到內存中。此過程就稱爲類的加載。加載到內存中的類,我們就稱爲運行時類,此運行時類,就作爲Class的一 個實例。
2.換句話說,Class 的實例就對應者-個運行時類。
3.加載到內存中的運行時類,會緩存- 定的時間。在此時間之內,我們可以通過不同的方式
來獲取此運行時類。

4.創建類的對象的方式

方式一:new +構造器
方式二:要創建Xxx類的對象,可以考慮: Xxx、Xxxs、XxxFactory、 xxxBuilder類中 查看是否有靜態方法的存在。可以調用其靜態方法,創建xx對象。

方式三:通過反射

5.Class實例可以是那些結構的說明

  1. class:外部類,成員(成員內部類,靜態內部類),局部內部類,匿名內部類
  2. interface: 接口
  3. []: 數組
  4. enum:枚舉
  5. annotation: 註解@ interface
  6. primitive type:基本數據類型
  7. void

三、類的加載過程

類的加載(Load)將類的class文件讀入內存,併爲之創建一個java.lang.Class對象。此過程由類加載器完成

類的鏈接(Link)將類的二進制數據合併到jre

類的初始化(Initialize)jvm負責對類進行初始化

●加載:將class文件 字節碼內容加載到內存中,並將這些靜態數據轉換成方法區的運行時數據結構,然後生成一個代表這個類的java.lang.Class對象,作爲方法區中類數據的訪問入口(即引用地址)。所有需要訪問和使用類數據只能通過這個Class對象。這個加載的過程需要類加載器參與。
●鏈接:將Java類的二進制代碼合併到JVM的運行狀態之中的過程。
➢驗證:確保加載的類信息符合JVM規範,例如:以cafe開頭, 沒有安全方面的問題
➢準備:正式爲類變量(static) 分配內存並設置類變量默認初始值的階段,這些內存都將在方法區中進行分配。
➢解析:虛擬機常量池內的符號引用(常量名)替換爲直接引用(地址)的過程。
●初始化:
➢**執行類構造器()**方法的過程。類構造器()方法是由編譯期自動收集類中所有類變量的賦值動作和靜態代碼塊中的語句合併產生的。( 類構造器是構造類信息的,不是構造該類對象的構造器)。
➢當初始化一個類的時候,如果發現其父類還沒有進行初始化,則需要先觸發其父類的初始化。
➢虛擬機會保證一個類的()方法在 多線程環境中被正確加鎖和同步。
在這裏插入圖片描述
**類加載器作用是用來把類(class)裝載進內存的。**JVM規範定義瞭如下類型的類的加載器。

在這裏插入圖片描述

四、動態代理(反射的動態性)

一、反射的應用:動態代理

●代理設計模式的原理:
使用一個代理將對象包裝起來,然後用該代理對象取代原始對象。任何對原始對象的調用都要通過代理。代理對象決定是否以及何時將方法調用轉到原始對象上。
●之前爲大家講解過代理機制的操作,屬於靜態代理,特徵是代理類和目標對象的類都是在編譯期間確定下來,不利於程序的擴展。同時,每一個代理類只能爲一個接口服務,這樣-來程序開發中必然產生過多的代理。最好可以通過一個代理類完成全部的代理功能。

因爲是在運行期間進行動態代理,所以我們這裏用到反射

●動態代理是指客戶通過代理類來調用其它對象的方法,並且是在程序運行時根據需要動態創建目標類的代理對象。
●動態代理使用場合:
➢調試
➢遠程方法調用
●動態代理相比於靜態代理的優點:
抽象角色中(接口)聲明的所有方法都被轉移到調用處理器一個集中的方法中處理,這樣,我們可以更加靈活和統一的處理衆多的方法。

五、靜態代理舉例

interface ClothFactory{
    void produceCloth();
}
//代理類
class ProxyClothFactory implements ClothFactory{
    private ClothFactory factory;//用被代理對象進行實例化
    public ProxyClothFactory(ClothFactory factory) {
        this.factory = factory;
    }
    @Override
    public void produceCloth() {
        System.out.println("代理工廠做一些準備工作");
        factory.produceCloth();
        System.out.println("代理工廠做一些後續的收尾工作");
    }
}
//被代理類
class NikeClothFactory implements ClothFactory{
    @Override
    public void produceCloth() {
        System.out.println("Nike工廠生產一批運動服");
    }
}
public class StaticProxyTest {
    public static void main(String[] args) {
        //創建被代理類對象
        NikeClothFactory nike = new NikeClothFactory();
        //創建代理類的對象
        ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike);
        proxyClothFactory.produceCloth();
    }
}

在這裏插入圖片描述

六、動態代理舉例

通過反射的Proxy.newProxyInstance

我們來看一下

在這裏插入圖片描述

再來看一下InvocationHandler

在這裏插入圖片描述

invocationHandler的invoke方法
在這裏插入圖片描述
上代碼:

/**
 * @Author: piwenjing
 * @Description:
 * @Date: Created in 21:49 2020/6/20 0020
 */
interface Human{
    String getBelief();
    void eat(String food);
}
//被代理類
class SuperMan implements Human{

    @Override
    public String getBelief() {
        return "I belive I can fly";
    }

    @Override
    public void eat(String food) {
        System.out.println("我喜歡喫" + food);
    }
}

/**
 * 要想實現動態代理,需要解決的問題
 * 問題一:如何根據加載到內存中的被代理類,動態的創建一個代理類及其對象
 * 問題二:當通過代理類的對象調用方法a時,如何動態的去調用被帶代理類中的同名方法a
 */
//針對問題一
class ProxyFactory{
    //調用此方法,返回一個代理類的對象,解決問題一
    public static Object getProxyInstance(Object obj){//obj:被代理類的對象
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        myInvocationHandler.bind(obj);
        //根據反射中的這個Proxy
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),myInvocationHandler);
    }
}
class MyInvocationHandler implements InvocationHandler{
    private Object obj;//需要使用被代理類的對象進行賦值

    public void bind(Object obj) {
        this.obj = obj;
    }

    //當我們通過代理類的對象,調用方法a時,就會自動的調用如下的方法: invoke()
    //將被帶離類要執行的方法a的功能就聲明在invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //method:即爲代理類對象調用的方法,次方法也就作爲了被代理類對象要調用的方法
        //obj:被代理類的對象
        Object returnValue = method.invoke(obj, args);
        //上述方法的返回值就作爲當前類中的invoke()的返回值
        return returnValue;
    }
}
public class ProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        //proxyInstance:代理類的對象
        Human proxyInstance = (Human)ProxyFactory.getProxyInstance(superMan);
        //當通過代理類對象調用方法時,會自動地調用被代理類中同名的方法
        System.out.println(proxyInstance.getBelief());
        proxyInstance.eat("12312312");
        System.out.println("******************************************");
        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ClothFactory proxyClothFactory = (ClothFactory)ProxyFactory.getProxyInstance(nikeClothFactory);
        proxyClothFactory.produceCloth();
    }
}

運行結果:

在這裏插入圖片描述

七、AOP與動態代理的舉例

在這裏插入圖片描述

在這裏插入圖片描述
我們在前面代碼的基礎上來體驗一下
在這裏插入圖片描述
修改如下:
在這裏插入圖片描述
在這裏插入圖片描述
結果:

在這裏插入圖片描述
好處就在於我們不用跟以前一樣將共同代碼抽出來後,還需要在原來的幾個方法中顯示地去調用這個方法

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