首頁 > Java > java教程 > 主體

完全掌握Java動態代理

WBOY
發布: 2022-07-26 13:55:47
轉載
1645 人瀏覽過

本篇文章為大家帶來了關於java的相關知識,動態代理指的是,代理類別和目標類別的關係在程式運行的時候確定的,客戶透過代理類別來調用目標物件的方法,是在程式運行時根據需要動態的創建目標類別的代理物件。以下將透過案例詳細解說Java動態代理的原理及實現,希望對大家有幫助。

完全掌握Java動態代理

推薦學習:《java影片教學

"代理"這個字相信大家並不陌生,簡單來說就是代替廠商來販賣商品,代理替代廠商販售商品,顧客找代理商購買商品。也就是說:1)顧客和廠商之間是看不見的,顧客不知道背後的廠商是誰。 2)代理可以對顧客進行“定位”,更精確的售賣給需要的客戶群。

代理模式

代理模式:為其他物件提供一種代理程式以控制對這個物件的訪問,也就是創建一個代理物件作為客戶端和目標物件之間的中介,主要目的是保護目標物件或增強目標物件

透過使用代理模式,通常有以下兩個優點:

\1) 可以隱藏被代理類別的實作

\ 2) 可以實作客戶與被代理類別間的解耦,在不修改被代理類別程式碼的情況下能夠做一些額外的處理

靜態代理

所謂的動態代理,就是透過聲明一個明確的代理類別來存取來源對象,一個代理只能服務一種產品,當有n種產品時,就需要n個代理,這樣就不利於業務的發展。

範例:我們有兩個接口,Mouse和Keyboard,每個接口都有一個實作類別

實作類別中的程式碼如下:

public class LogitechMouse implements Mouse{
    @Override
    public void sell() {
        System.out.println("出售罗技鼠标");
    }
}
public class HHKBKeyboard implements Keyboard{
    @Override
    public void sell() {
        System.out.println("出售HHKB键盘");
    }
}
登入後複製

現在我們要做的就是讓代理程式在呼叫sell()前輸出一句售前了解,呼叫後輸出一句售後服務

那我們只需寫兩個代理類別MouseProxyKeyboardProxy

public class MouseProxy implements Mouse {
    private Mouse mouse;

    public MouseProxy(Mouse mouse) {
        this.mouse = mouse;
    }
    @Override
    public void sell() {
        System.out.println("售前了解");
        mouse.sell();
        System.out.println("售后服务");
    }
}
public class KeyboardProxy implements Keyboard{
    private Keyboard keyboard;
    public KeyboardProxy(Keyboard keyboard) {
        this.keyboard = keyboard;
    }
    @Override
    public void sell() {
        System.out.println("售前了解");
        keyboard.sell();
        System.out.println("售后服务");
    }
}
登入後複製

最終執行為:

public class Test {
    public static void main(String[] args) {
        Mouse logitechMouse = new LogitechMouse();
        MouseProxy mouseProxy = new MouseProxy(logitechMouse);
        mouseProxy.sell();
        Keyboard hhkbKeyboard = new HHKBKeyboard();
        KeyboardProxy keyboardProxy = new KeyboardProxy(hhkbKeyboard);
        keyboardProxy.sell();
    }
}
登入後複製

輸出:
售前了解
出售羅技滑鼠
售後服務
售前了解
出售HHKB鍵盤
售後服務

##靜態代理程式的程式碼非常簡單易懂,這種模式雖好,但是也有明顯的缺點:

    會存在大量冗餘的代理類,這裡只有兩個接口,如果有n個接口,那麼就要定義n個代理類。
  • 不易維護,一旦介面更改,代理類別和被代理類別都要更改。
那麼這個時候就可以使用動態代理來解決了

動態代理

#代理類別在程式運行時創建代理的方式叫動態代理,也就是說代理類別不是在java程式碼中定義的,而是在運行的時候動態產生的

JDK動態代理

JDK從1.3版本就開始支援動態代理類別的建立。主要核心類別只有2個:

java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler

還是上面的例子,用JDK動態代理如下:

public class JDKProxy implements InvocationHandler {
    private Object object;
    public JDKProxy(Object object) {
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("售前了解");
        Object invoke = method.invoke(object, args);
        System.out.println("售后服务");
        return invoke;
    }
}
登入後複製

當我們呼叫代理類別物件的方法時,這個「呼叫」會轉送到invoke方法中,

代理類別物件作為proxy參數傳入,

#參數method標識了我們具體呼叫的是代理類別的哪個方法,

args為這個方法的參數。

這樣一來,我們對代理類別中的所有方法的調用都會變成對invoke的調用,這樣我們可以在invoke方法中添加統一的處理邏輯(也可以根據method參數對不同的代理類別方法做不同的處理)。因此我們可以在中介類別的invoke方法中實現輸出售前了解,再呼叫被代理類別的方法,再輸出售後服務。

執行程式碼

public class Test {
    public static void main(String[] args) {
        Mouse logitechMouse = new LogitechMouse();
        JDKProxy jdkProxy = new JDKProxy(logitechMouse);
        Mouse mouse= (Mouse)Proxy.newProxyInstance(jdkProxy.getClass().getClassLoader(), new Class[]{Mouse.class}, jdkProxy);
        mouse.sell();
        HHKBKeyboard hhkbKeyboard = new HHKBKeyboard();
        JDKProxy jdkProxy1 = new JDKProxy(hhkbKeyboard);
        Keyboard keyboard = (Keyboard)Proxy.newProxyInstance(jdkProxy1.getClass().getClassLoader(), new Class[]{Keyboard.class}, jdkProxy1);
        keyboard.sell();
    }
}
登入後複製

可以看到無論多少個接口,只需要一個代理類別就可以了。

CGLIB動態代理程式

代理程式類別:

public class CGLIBProcy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();
    private Object object;
    public CGLIBProcy(Object object) {
        this.object = object;
    }
    public Object getProxy(){
        //设置需要创建子类的类
        enhancer.setSuperclass(object.getClass());
        enhancer.setCallback(this);
        //创建代理对象
        return enhancer.create();
    }
    // o: cglib 动态生成的代理类的实例
    // method:实体类所调用的都被代理的方法的引用
    // objects 参数列表
    // methodProxy:生成的代理类对方法的代理引用
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("售前了解");
        Object invoke = method.invoke(object, objects);
        System.out.println("售后处理");
        return invoke;
    }
}
登入後複製

執行程式碼:

public class Test {
    public static void main(String[] args) {
        Mouse logitechMouse = new LogitechMouse();
        CGLIBProcy cglibProcy = new CGLIBProcy(logitechMouse);
        Mouse proxy = (Mouse)cglibProcy.getProxy();
        proxy.sell();
        cglibProcy = new CGLIBProcy(new HHKBKeyboard());
        Keyboard keyboard = (Keyboard)cglibProcy.getProxy();
        keyboard.sell();
    }
}
登入後複製
JDK代理程式與CGLIB代理程式的差異

    #JDK動態代理實現接口,CGLIB動態繼承思想
  • JDK動態代理(目標對象存在接口時)執行效率高於CIGLIB
  • 如果對像有接口實現,選擇JDK代理,如果沒有介面實作選擇CGILB代理
推薦學習:《

java影片教學

以上是完全掌握Java動態代理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:jb51.net
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板