1. 程式人生 > 其它 >Spring入門系列-動態代理

Spring入門系列-動態代理

動態代理

  1. 動態代理和靜態代理的角色一樣
  2. 動態代理的代理類是動態生成的,不是我們直接寫好的
  3. 動態代理可以分為兩大類,基於介面的動態代理,基於類的動態代理
    • 基於介面---JDK動態代理[我們在這裡使用]
    • 基於類---cglib
    • Java位元組碼實現:Javassist

需要了解兩個類 Proxy:代理,invocationHandler:呼叫代理程式

在此之前,我們要思考class和interface的區別和關係:

  • 可以例項化class(非abstract)
  • 不可以例項化interface

所有的interface型別的變數都是通過向上轉型並指向某個例項的

CharSequence charSequence=new StringBuffer();

有沒有可能不編寫實現類,自動生成介面所指向的例項呢?

答案是可以的,Java標準庫提供了對應的機制:可以在執行期間動態的建立某個interface的例項,什麼叫做動態的建立呢?我們先看一下靜態情況下是怎樣的。

1.編寫介面

package com.dreamcold.aop.demo02;

public interface Hello {
    void sayHello();
}

  1. 編寫具體實現類
package com.dreamcold.aop.demo02;

public class HelloWorld implements Hello {
    public void sayHello(){
        System.out.println("Hello World");
    }
}

3.測試

package com.dreamcold.aop.demo02;

public class Demo01 {
    public static void main(String[] args) {
        Hello hello=new HelloWorld();
        hello.sayHello();
    }
}
  1. 輸出

還有另外一種方式是動態程式碼,過程如下:

  • 我們仍然先定義介面Hello
  • 但是我們不編寫實現類
  • 實現類是通過JDK提供的一個Proxy.newProxyInstance()建立了一個Hello介面物件
  • 這種沒有實現類,但是在執行期間建立一個介面物件的方式稱為動態程式碼
  • JDK提供的動態建立介面物件的方式,叫做動態代理。

1.先定義介面

package com.dreamcold.aop.demo03;

public interface Hello {
    void sayGoodMorning(String name);
}

2.實現動態代理

package com.dreamcold.aop.demo03;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class HelloWorld  {
    public static void main(String[] args) {
        //invocationHandler負責介面的方法呼叫
        InvocationHandler handler=new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //列印方法
                System.out.println(method);
                if(method.getName().equals("sayGoodMorning")){
                    System.out.println("Good Morning "+args[0]);
                }

                return null;
            }
        };
        //負責建立interface例項
        Hello hello=(Hello) Proxy.newProxyInstance(
                Hello.class.getClassLoader(),
                new Class[]{Hello.class},
                handler
        );
        hello.sayGoodMorning("dreamcold");
    }
}

在執行的時候建立一個interface例項的方法如下:

  1. 定義一個InvocationHandler例項,它負責實現介面方法的呼叫
  2. 通過Proxy.newInstance()建立interface實錄,它需要三個引數
    • 使用ClassLoader,通常就是介面類的ClassLoader
    • 需要實現的介面資料,至少需要傳入一個介面進去
    • 用來處理介面方法呼叫的Invocationhandler例項
  3. 將返回的Object強制轉換為介面

實質:動態代理實際上就是JDK在執行期間建立class位元組碼並載入的過程,它並沒有什麼黑魔法,把上面的靜態實現類:

public class HelloDynamicProxy implements Hello {
    InvocationHandler handler;
    public HelloDynamicProxy(InvocationHandler handler) {
        this.handler = handler;
    }
    public void morning(String name) {
        handler.invoke(
           this,
           Hello.class.getMethod("morning", String.class),
           new Object[] { name });
    }
}

其實就是JDK幫助我們自動編寫了一個上述類(不需要原始碼,可以直接生成位元組碼),並不存在直接可以直接例項化介面的黑魔法。

總結

  • Java標準庫提供類動態代理的功能,允許在執行期間動態的建立一個介面例項
  • 動態代理是通過Proxy建立代理物件,然後將介面代理給InvocationHandler完成的

學習自連結:狂神說