邁出成爲架構師的第一步!看完這篇,帶你秒懂Java三種代理模式!

邁出成爲架構師的第一步!看完這篇,帶你秒懂Java三種代理模式!

前言

代理(Proxy)模式是一種結構型設計模式,提供了對目標對象另外的訪問方式;即經過代理對象訪問目標對象。java

image-20210724125009890

這樣作的好處是:能夠在目標對象實現的基礎上,加強額外的功能操做,即擴展目標對象的功能。編程

這裏使用到編程中的一個思想:不要隨意去修改別人已經寫好的代碼或者方法,若是須要修改,能夠經過代理的方式來擴展該方法。設計模式

代理模式大體有三種角色:markdown

  • Real Subject:真實類,也就是被代理類、委託類。用來真正完成業務服務功能;
  • Proxy:代理類,將自身的請求用 Real Subject 對應的功能來實現,代理類對象並不真正的去實現其業務功能;
  • Subject:定義 RealSubject 和 Proxy 角色都應該實現的接口。

image-20210724125955100

代理模式有三種類型,靜態代理,動態代理(JDK代理,接口代理)、Cglib代理(在內存中動態的建立目標對象的子類)架構

正文

靜態代理

靜態代理須要先定義接口,被代理對象與代理對象一塊兒實現相同的接口,而後經過調用相同的方法來調用目標對象的方法ide

image-20210726222112024

能夠看見,代理類無非是在調用委託類方法的先後增長了一些操做。委託類的不一樣,也就致使代理類的不一樣。函數

某公司生產電視機,在當地銷售須要找到一個代理銷售商。那麼客戶須要購買電視機的時候,就直接經過代理商購買就能夠。工具

代碼示例:this

public class TV {

    private String name;//名稱

    private String address;//生產地

    public TV(String name, String address) {
        this.name = name;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "TV{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

建立公司接口:設計

public interface TVCompany {

    /**
     * 生產電視機
     * @return 電視機
     */
    public TV produceTV();
}

公司的工廠生產電視機:

public class TVFactory implements TVCompany {
    @Override
    public TV produceTV() {
        System.out.println("TV factory produce TV...");
        return new TV("小米電視機","合肥");
    }
}

代理商去下單拿貨(靜態代理類):

public class TVProxy implements TVCompany{

    private TVCompany tvCompany;

    public TVProxy(){

    }

    @Override
    public TV produceTV() {
        System.out.println("TV proxy get order .... ");
        System.out.println("TV proxy start produce .... ");
        if(Objects.isNull(tvCompany)){
            System.out.println("machine proxy find factory .... ");
            tvCompany = new TVFactory();
        }
        return tvCompany.produceTV();
    }
}

消費者經過代理商拿貨(代理類的使用):

public class TVConsumer {

    public static void main(String[] args) {
        TVProxy tvProxy = new TVProxy();
        TV tv = tvProxy.produceTV();
        System.out.println(tv);
    }
}

輸出結果:

TV proxy get order .... 
TV proxy start produce .... 
machine proxy find factory .... 
TV factory produce TV...
TV{name='小米電視機', address='合肥'}

Process finished with exit code 0

小結:

  • 優勢:靜態代理模式在不改變目標對象的前提下,實現了對目標對象的功能擴展。

  • 缺點:靜態代理實現了目標對象的全部方法,一旦目標接口增長方法,代理對象和目標對象都要進行相應的修改,增長維護成本。

如何解決靜態代理中的缺點呢?答案是可使用動態代理方式

動態代理

image-20210726224121229

動態代理具備以下特色:

  1. JDK動態代理對象不須要實現接口,只有目標對象須要實現接口。

  2. 實現基於接口的動態代理須要利用JDK中的API,在JVM內存中動態的構建Proxy對象

  3. 須要使用到 java.lang.reflect.Proxy,和其newProxyInstance方法,可是該方法須要接收三個參數。

image-20210724132028289

注意該方法是在Proxy類中是靜態方法,且接收的三個參數依次爲:

  • ClassLoader loader:指定當前目標對象使用類加載器,獲取加載器的方法是固定的。
  • Class<?>[] interfaces:目標對象實現的接口的類型,使用泛型方式確認類型。
  • InvocationHandler h:事件處理,執行目標對象的方法時,會觸發事件處理器的方法,會把當前執行目標對象的方法做爲參數傳入。

有一天公司增長了業務,出售的商品愈來愈多,售後也須要更上。可是公司發現原來的代理商,還要再培訓才能完成所有的業務,因而就找了另外的動態代理商B代理商B 承諾無縫對接公司全部的業務,無論新增什麼業務,均不須要額外的培訓便可完成。

代碼示例:

公司增長了維修業務:

public interface TVCompany {

    /**
     * 生產電視機
     * @return 電視機
     */
    public TV produceTV();

    /**
     * 維修電視機
     * @param tv 電視機
     * @return 電視機
     */
    public TV repair(TV tv);
}

工廠也得把維修業務搞起來:

public class TVFactory implements TVCompany {
    @Override
    public TV produceTV() {
        System.out.println("TV factory produce TV...");
        return new TV("小米電視機","合肥");
    }

    @Override
    public TV repair(TV tv) {
        System.out.println("tv is repair finished...");
        return new TV("小米電視機","合肥");
    }
}

B代理商 全面代理公司全部的業務。使用Proxy.newProxyInstance方法生成代理對象,實現InvocationHandler中的 invoke方法,在invoke方法中經過反射調用代理類的方法,並提供加強方法。

public class TVProxyFactory {

    private Object target;

    public TVProxyFactory(Object o){
        this.target = o;
    }

    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("TV proxy find factory for tv.... ");
                Object invoke = method.invoke(target, args);
                return invoke;
            }
        });
    }
}

購買、維修這兩個業務 B代理就能夠直接搞定了。後面公司再增長業務,B代理也能夠同樣搞定。

public class TVConsumer {

    public static void main(String[] args) {
        TVCompany target = new TVFactory();
        TVCompany tvCompany = (TVCompany) new TVProxyFactory(target).getProxy();
        TV tv = tvCompany.produceTV();
        tvCompany.repair(tv);
    }
}

輸出結果:

TV proxy find factory for tv.... 
TV factory produce TV...
TV proxy find factory for tv.... 
tv is repair finished...

Process finished with exit code 0

小結:

  1. 代理對象不須要實現接口,可是目標對象必定要實現接口,不然不能用動態代理。

  2. 動態代理的方式中,全部的函數調用最終都會通過 invoke 函數的轉發,所以咱們就能夠在這裏作一些本身想作的操做,好比日誌系統、事務、攔截器、權限控制等。

JDK 動態代理有一個最致命的問題是它只能代理實現了某個接口的實現類,而且代理類也只能代理接口中實現的方法,要是實現類中有本身私有的方法,而接口中沒有的話,該方法不能進行代理調用。

怎麼解決這個問題呢?咱們能夠用 CGLIB 動態代理機制。

Cglib代理

靜態代理和JDK代理都須要某個對象實現一個接口,有時候代理對象只是一個單獨對象,此時可使用Cglib代理。

image-20210726224750356

Cglib代理能夠稱爲子類代理,是在內存中構建一個子類對象,從而實現對目標對象功能的擴展。

C代理商不只想代理公司,並且還想代理多個工廠的產品。

Cglib經過Enhancer 來生成代理類,經過實現MethodInterceptor接口,並實現其中的intercept方法,在此方法中能夠添加加強方法,並能夠利用反射Method或者MethodProxy繼承類 來調用原方法。

看到 B代理商承接了公司(接口)的多種業務,那麼此時C代理商又從中發現新的商機, B 只能代理某個公司的產品,而我不只想要代理公司產品,並且對接不一樣的工廠,拿貨渠道更廣,賺錢更爽快。因而Cglib就用上了。

代碼示例:

public class TVProxyCglib implements MethodInterceptor {

    //給目標對象建立一個代理對象
    public Object getProxyInstance(Class c){
        //1.工具類
        Enhancer enhancer = new Enhancer();
        //2.設置父類
        enhancer.setSuperclass(c);
        //3.設置回調函數
        enhancer.setCallback(this);
        //4.建立子類(代理對象)
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("TVProxyFactory enhancement.....");
        Object object = methodProxy.invokeSuper(o, objects);
        return object;
    }
}

新代理的B工廠

public class TVFactoryB {

    public TV produceTVB() {
        System.out.println("tv factory B producing tv.... ");
        return new TV("華爲電視機", "南京");
    }

    public TV repairB(TV tv) {
        System.out.println("tv B is repair finished.... ");
        return tv;
    }
}

C代理能夠直接和公司合做,也能夠和工廠打交道。而且能夠代理任何工廠的產品。

public class TVConsumer {

    public static void main(String[] args) {
        TVCompany tvCompany = (TVCompany) new TVProxyCglib().getProxyInstance(TVFactory.class);
        TV tv = tvCompany.produceTV();
        tvCompany.repair(tv);
        System.out.println("==============================");

        TVFactoryB tvFactoryB = (TVFactoryB) new TVProxyCglib().getProxyInstance(TVFactoryB.class);
        TV tv = tvFactoryB.produceTVB();
        tvFactoryB.repairB(tv);
    }
}

輸出結果:

TVProxyFactory enhancement.....
TV factory produce TV...
TVProxyFactory enhancement.....
tv is repair finished...
==============================
TVProxyFactory enhancement.....
tv factory B producing tv.... 
TVProxyFactory enhancement.....
tv B is repair finished.... 

Process finished with exit code 0

Spring中AOP使用代理

Spring中AOP的實現有JDK和Cglib兩種,以下圖:

image-20210724133134109

若是目標對象須要實現接口,則使用JDK代理。

若是目標對象不須要實現接口,則使用Cglib代理。

總結

  1. 靜態代理:須要代理類和目標類都實現接口的方法,從而達到代理加強其功能。

  2. JDK動態代理:須要代理類實現某個接口,使用Proxy.newProxyInstance方法生成代理類,並實現InvocationHandler中的invoke方法,實現加強功能。

  3. Cglib動態代理:無需代理類實現接口,使用Cblib中的Enhancer來生成代理對象子類,並實現MethodInterceptor中的intercept方法,在此方法中能夠實現加強功能。

標籤: [Java]
在這裏插入圖片描述

相關文章
相關標籤/搜索