天天看點

Java代理模式

代理模式主要為其他對象提供一種代理以控制這個對象的通路。在某些情況下,一個對象不想或者不能直接引用另一個對象,而代理對象可以在用戶端和目标對象之間起到中介的作用,好比你将一些繁瑣的事情交給第三方去管理,那麼第三方就是你的代理,其他人隻會去找這個代理,而不會去找你,AOP本身就是基于動态代理實作的,是以掌握了代理模式對AOP的學習很有幫助的哦。

Java代理模式
本文将給大家介紹代理模式的兩種實作方式,靜态代理和動态代理,并介紹相關使用場景

一、代理模式中涉及到的角色

  • 抽象角色:為真實對象和代理對象提供一個共同的接口,一般是抽象類或者接口。
  • 代理角色:代理角色内部含有對真實對象的引用,進而可以操作真實對象,同時代理對象提供與真實對象相同的接口以便在任何時刻都能夠代替真實對象。同時,代理對象可以在執行真實對象的操作時,附加其他操作,相當于對真實對象的功能進行拓展。
  • 真實角色:最終引用的對象。

二、靜态代理

代理類在程式運作前就已經存在,那麼這種代理方式被成為

靜态代理

1、定義抽象角色

/**
 * @author Gjing
 * 定義一個産家,提供賣貨的功能
 **/
public interface Producer {
    void sell();
}           

2、定義一個真實角色

/**
 * @author Gjing
 * 定義一個小賣部,幫産家賣貨
 **/
public class Canteen implements Producer {
    @Override
    public void sell() {
        System.out.println("小賣部進行賣貨");
    }
}           

3、定義一個代理類和測試類

/**
 * @author Gjing
 * 定義産家的代理商,也具備賣貨的功能
 **/
public class ProducerProxy implements Producer {
    private Producer producer;

    ProducerProxy(Producer producer) {
        this.producer = producer;
    }

    @Override
    public void sell() {
        System.out.println("--------小賣部賣貨前--------");
        producer.sell();
        System.out.println("--------小賣部賣貨後--------");
    }
}

/**
 * @author Gjing
 */
class StaticProxyTest {
    public static void main(String[] args) {
        Producer producer = new Canteen();
        ProducerProxy personProxy = new ProducerProxy(producer);
        personProxy.sell();
    }
}           

測試結果

Java代理模式

三、動态代理

代理類在程式運作時建立的代理方式被稱為 動态代理,如果目标對象實作了接口,采用JDK的動态代理,如果目标對象沒有實作接口,必須采用cglib動态代理

1、JDK動态代理

a、定義一個産家

/**
 * @author Gjing
 * 定義一個産家
 **/
public interface Producer2 {
    void sell();
}           

b、定義一個真實角色

/**
 * @author Gjing
 * 定義商家
 **/
public class Canteen2 implements Producer2 {
    @Override
    public void sell() {
        System.out.println("小賣部進行賣貨");
    }
}           

c、實作代理

/**
 * @author Gjing
 **/
public class Producer2Proxy {
    public static void main(String[] args) {
        Producer2 producer2 = new Canteen2();
        Producer2 producerProxy = (Producer2) Proxy.newProxyInstance(producer2.getClass().getClassLoader(),
                producer2.getClass().getInterfaces(), (proxy, method, args1) -> {
                    System.out.println("----------小賣部賣貨前--------");
                    Object invoke = method.invoke(producer2,args1);
                    System.out.println("----------小賣部賣貨後--------");
                    return invoke;
                });
        producerProxy.sell();
    }
}           

輸出

Java代理模式

2、CGLib代理

目标類

不能為final

,目标對象的方法如果為

final / static

,那麼就不會被攔截,即不會執行目标對象額外的業務方法

a、引入依賴

Spring環境下不需要,因為Spring-Core裡已經引入了

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.12</version>
</dependency>           

b、建立一個目标類

/**
 * @author Gjing
 * 真實對象
 **/
public class Canteen {
    public void sell() {
        System.out.println("小賣部進行賣貨");
    }
}           

c、建立CGLib的工廠類和測試類

/**
 * @author Gjing
 **/
public class CgLibProxy implements MethodInterceptor {
    private Canteen canteen;

    CgLibProxy(Canteen canteen) {
        this.canteen = canteen;
    }

    Canteen proxy() {
        // 建立Enhancer對象
        Enhancer enhancer = new Enhancer();
        // 設定代理的目标類
        enhancer.setSuperclass(Canteen.class);
        // 設定回調方法, this代表是目前類, 因為目前類實作了CallBack
        enhancer.setCallback(this);
        return (Canteen) enhancer.create();
    }

    //這個方法就是回調方法了
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("----------小賣部賣貨前----------");
        Object invoke = method.invoke(canteen, objects);
        System.out.println("----------小賣部賣貨後----------");
        return invoke;
    }
}

/**
 * @author Gjing
 */
class TestCglibProxy{
    public static void main(String[] args) {
        Canteen canteen = new Canteen();
        Canteen proxy = new CgLibProxy(canteen).proxy();
        proxy.sell();
    }
}           
Java代理模式

四、三種代理方式的優缺點

1、靜态代理:

可以做到在不修改目标對象的功能前提下,對目标功能擴充

缺點:

代理對象需要與目标對象實作一樣的接口,是以會有很多代理類,類太多.同時,一旦接口增加方法,目标對象與代理對象都要維護

2、JDK動态代理

代理對象不需要實作接口, 利用JDK的API,動态的在記憶體中建構代理對象(需要我們指定建立代理對象/目标對象實作的接口的類型)

目标對象一定要實作接口,否則不能用動态代理

3、CGLib代理

靜态代理和動态代理模式都是要求目标對象是實作一個接口的目标對象,但是有時候目标對象隻是一個單獨的對象,并沒有實作任何的接口,這個時候就可以使用以目标對象類實作代理

五、什麼時候使用代理模式

1、當我們想要隐藏某個類時,可以為其提供代理。

2、當一個類需要對不同的調用者提供不同的調用權限時,可以使用代理類來實作(代理類不一定隻有一個,我們可以建立多個代理類來實作,也可以在一個代理類中進行權限判斷來進行不同權限的功能調用)。

3、當我們要擴充某個類的某個功能時,可以使用代理模式,在代理類中進行簡單擴充(隻針對簡單擴充,可在引用委托類的語句之前與之後進行)。

六、JDK代理和CGLib代理的差別

JDK動态代理

使用Java的反射技術生成代理類,隻能代理實作了接口的類,沒有實作接口的類不能實作動态代理,

CGLib

會在運作時動态的生成一個被代理類的子類,子類重寫了被代理類中所有

非final

的方法,在子類中采用方法攔截的技術攔截所有父類方法的調用,不需要被代理類對象實作接口,進而

CGLIB

動态代理效率比Jdk動态代理反射技術

效率要高

本文到此就結束了,如果有任何錯誤的地方,可以在下方評論指出,也希望大家可以關注我,我會持續釋出相關技術文章,Demo源代碼位址:

Design-Mode