動態代理實現AOP-反射的應用

反射應用

要理解AOP需要這麼幾個步驟:
反射——invoke()——代理——AOP。
在上一篇已經介紹了基礎的反射是什麼,和常見的API是如何使用的在此基礎上,說明一下invoke()方法的使用。

invoke方法

invoke方法用於調用運行時類的指定結構。

  1. 通過Class類的getMethod(String name,Class…parameterTypes)方法取得一個Method對象,並設置此方法操作時所需要的參數類型。
  2. 使用Object invoke(Object obj,Object[] args)進行調用,並向方法中傳遞要設置的obj對象的參數信息。

在這裏插入圖片描述

Object invoke(Object obj,Object[] args)說明
  1. Object對應原方法的返回值,若原方法無返回值,此時返回null
  2. 若原方法爲靜態方法,此時形參Object obj可爲null
  3. 若原方法形參列表爲空,則Object[] args爲null
  4. 若原方法聲明爲private,則需要在此調用此invoke()方法前,顯示調用方法對象的setAccessible(true)方法,將可訪問private的方法。
AOP

AOP

代理分爲靜態代理和動態代理。動態代理一般有兩種方式實現,基於JDK和Cglib的實現。我們所說的AOP是使用動態代理實現的。
在軟件業,AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。

AOP最直觀的效果是在不改變當前代碼的前提下,動態的添加自己想要實現的效果,非常強大。

AOP是用來解決某些特定問題的,AOP關注的是系統的“截面”,在適當的時候“攔截”程序的執行流程,把程序的預處理和後期處理交給某個攔截器來完成。例如,訪問數據庫時需要記錄日誌,如果使用AOP的編程思想,那麼在處理業務流程時不必在去考慮記錄日誌,而是把它交給一個專門的例子記錄模塊去完成。這樣,程序員就可以集中精力去處理業務流程,而不是在實現業務代碼時嵌入日誌記錄代碼,實現業務代碼與非業務代碼的分別維護。在AOP術語中,這稱爲關注點分離。AOP的常見應用有日誌攔截、授權認證、數據庫的事務攔截和數據審計等。

下面用JDK動態代理實現一個簡單的AOP示例。

實現示例

JDK和Cglib的區別

java動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokerHandler來處理。
而Cglib動態代理是利用asm開源包,對代理對象的class文件加載進來,通過修改其字節碼生成子類處理。

  • 如果目標對象實現了接口,默認情況下采用JDK的動態代理實現AOP
  • 如果目標對象實現了接口,可以強制使用Cglib實現AOP
  • 如果目標對象沒有實現接口,必須採用Cglib庫,spring會自動在JDK動態代理和Cglib之間轉換

JDK動態代理和Cglib字節碼生成的區別:

  1. JDK動態代理只能對實現了接口的類生成代理,而不能針對類
  2. Cglib是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法
    因此是繼承,在該類或方法中最好不要使用final。

靜態代理

首先實現一個靜態代理。通過靜態代理與動態代理的比較可以更直觀的認識動態代理帶來的好處與優點。
靜態代理是代理類和被代理類在編譯期間就已經被確定下來了。

/**
 * 靜態代理舉例
 *
 * 特點:代理類和被代理類在編譯期間,就確定下來了。
 */
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) {
        //創建被代理類的對象
        ClothFactory nike = new NikeClothFactory();
        //創建代理類的對象
        ClothFactory proxyClothFactory = new ProxyClothFactory(nike);

        proxyClothFactory.produceCloth();

    }
}
代理工廠做一些準備工作
Nike工廠生產一批運動服
代理工廠做一些後續的收尾工作

動態代理(JDK)

要先思考兩個問題:
問題一:如何根據加載到內存中的被代理類,動態的創建一個代理類及其對象。
問題二:當通過代理類的對象調用方法a時,如何動態的去調用被代理類中的同名方法a。

  1. 被代理的類
interface Human{

    void getBelief();

    void eat(String food);

}
//被代理類
class SuperMan implements Human{


    @Override
    public void getBelief() {
        System.out.println("I'm sure I'll see you again!");
    }

    @Override
    public void eat(String food) {
        System.out.println("我還是喜歡喫" + food);
    }
}
  1. 想要增強的動作
class HumanUtil{

  public void methodOne(){
        System.out.println("嗨,好久不見啊");

    }

    public void methodTwo(){
        System.out.println("你還好嗎");
    }

}
  1. 創建代理工廠
class ProxyFactory{
    //調用此方法,返回一個代理類的對象。解決問題一
    public static Object getProxyInstance(Object obj){//obj:被代理類的對象
        MyInvocationHandler handler = new MyInvocationHandler();

        handler.bind(obj);

        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }

}
  1. 實現InvocationHandler
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 {

       HumanUtil util = new HumanUtil();
        util.methodOne();

        //method:即爲代理類對象調用的方法,此方法也就作爲了被代理類對象要調用的方法
        //obj:被代理類的對象
        Object returnValue = method.invoke(obj,args);

        util.methodTwo();

        //上述方法的返回值就作爲當前類中的invoke()的返回值。
        return returnValue;

    }
}
  1. 測試
public class ProxyTest {

    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        //proxyInstance:代理類的對象
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        //當通過代理類對象調用方法時,會自動的調用被代理類中同名的方法
        proxyInstance.getBelief();
        proxyInstance.eat("三汁燜鍋");

    }
}
輸出結果:

嗨,好久不見啊
I'm sure I'll see you again!
你還好嗎
嗨,好久不見啊
我還是喜歡喫三汁燜鍋
你還好嗎

感謝大佬們:
https://www.cnblogs.com/leifei/p/8263448.html
https://blog.csdn.net/q982151756/article/details/80513340

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