動(dòng)態(tài)代理詳解

簡(jiǎn)介

在某些情況下,我們不希望或是不能直接訪問(wèn)對(duì)象 A,而是通過(guò)訪問(wèn)一個(gè)中介對(duì)象 B,由 B 去訪問(wèn) A 達(dá)成目的,這種方式我們就稱為代理。

這里對(duì)象 A 所屬類我們稱為委托類,也稱為被代理類,對(duì)象 B 所屬類稱為代理類。

代理優(yōu)點(diǎn)有:

  • 隱藏委托類的實(shí)現(xiàn)
  • 解耦,不改變委托類代碼情況下做一些額外處理,比如添加初始判斷及其他公共操作

根據(jù)程序運(yùn)行前代理類是否已經(jīng)存在,可以將代理分為靜態(tài)代理和動(dòng)態(tài)代理。
動(dòng)態(tài)代理實(shí)現(xiàn)方式主要有兩種,分別是JDK動(dòng)態(tài)代理和CGlib動(dòng)態(tài)代理。二者的主要區(qū)別是JDK動(dòng)態(tài)代理是通過(guò)反射,只能代理實(shí)現(xiàn)接口的目標(biāo)類,而CGlib是通過(guò)繼承目標(biāo)類,因而不能代理final類。

代理類在程序運(yùn)行前不存在、運(yùn)行時(shí)由程序動(dòng)態(tài)生成的代理方式稱為動(dòng)態(tài)代理。

Java 提供了動(dòng)態(tài)代理的實(shí)現(xiàn)方式,可以在運(yùn)行時(shí)刻動(dòng)態(tài)生成代理類。這種代理方式的一大好處是可以方便對(duì)代理類的函數(shù)做統(tǒng)一或特殊處理,如記錄所有函數(shù)執(zhí)行時(shí)間、所有函數(shù)執(zhí)行前添加驗(yàn)證判斷、對(duì)某個(gè)特殊函數(shù)進(jìn)行特殊操作,而不用像靜態(tài)代理方式那樣需要修改每個(gè)函數(shù)。

代理模式原理

代理模式就是給某一個(gè)對(duì)象創(chuàng)建了一個(gè)代理對(duì)象,由這個(gè)代理對(duì)象控制原對(duì)象的引用,而創(chuàng)建這個(gè)代理對(duì)象后可以在調(diào)用原對(duì)象時(shí)增加一些額外的操作。如下圖所示是代理模式的結(jié)構(gòu)。

接口:它是代理對(duì)象的真實(shí)對(duì)象要實(shí)現(xiàn)的接口

代理類:除了實(shí)現(xiàn)上述的接口外,還需要持有代理對(duì)象的引用

實(shí)現(xiàn)類:被代理的類,是目標(biāo)對(duì)象

代理模式結(jié)構(gòu)

創(chuàng)建代理對(duì)象的時(shí)序圖如下所示

創(chuàng)建代理對(duì)象的時(shí)序圖

動(dòng)態(tài)代理實(shí)現(xiàn)

JDK實(shí)現(xiàn)動(dòng)態(tài)代理包括三步:

  1. 新建委托類;
  2. 實(shí)現(xiàn)InvocationHandler接口,這是負(fù)責(zé)連接代理類和委托類的中間類必須實(shí)現(xiàn)的接口;
  3. 通過(guò)Proxy類新建代理類對(duì)象。

接口

public interface Greeting {
    void sayHello(String name);
}

目標(biāo)類

public class GreetingImpl implements Greeting {
    public void sayHello(String name) {
        System.out.println("say Hello:"+name);
    }
}

JDK代理類

InvocationHandler接口,前者Invocation為執(zhí)行,后者Handler為句柄(實(shí)際對(duì)象的間接引用)。對(duì)于invoke函數(shù),調(diào)用代理對(duì)象的所有方法都會(huì)調(diào)用這個(gè)函數(shù)。

public class JDKDynamicProxy implements InvocationHandler {

    private Object target;

    public JDKDynamicProxy(Object target){
        this.target = target;
    }

    public <T> T getPoxy(){
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this
        );
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target,args);
        after();
        return result;
    }

    public void before(){
        System.out.println("before");
    }

    public void after(){
        System.out.println("after");
    }
}

cglib代理類

public class CGLibDynamicProxy implements MethodInterceptor {

    private static CGLibDynamicProxy instance = new CGLibDynamicProxy();

    private CGLibDynamicProxy(){
    }

    public static CGLibDynamicProxy getInstance(){
        return instance;
    }

    public <T> T getProxy(Class<T> cls){
        return (T) Enhancer.create(cls,this);
    }

    @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        before();
        Object result = proxy.invokeSuper(target,args);
        after();
        return result;
    }


    public void before(){
        System.out.println("before");
    }

    public void after(){
        System.out.println("after");
    }

}

測(cè)試

public class Client {
    public static void main(String[] args) {
        //JDK動(dòng)態(tài)代理
        Greeting greeting = new JDKDynamicProxy(new GreetingImpl()).getPoxy();
        greeting.sayHello("Jack");
        //CGLib動(dòng)態(tài)代理
        Greeting greeting2 = CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class);
        greeting2.sayHello("Rose");
    }
}

結(jié)果

before
say Hello:Jack
after
before
say Hello:Rose
after

參考資料

《架構(gòu)探險(xiǎn)》

《深入分析Java Web技術(shù)內(nèi)幕》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • JDK動(dòng)態(tài)代理詳解 java動(dòng)態(tài)代理類 Java動(dòng)態(tài)代理類位于java.lang.reflect包下,一般主要涉及...
    冰火人生閱讀 572評(píng)論 0 1
  • 動(dòng)態(tài)代理的作用通過(guò)反射調(diào)用代理對(duì)象,讓其幫我們實(shí)現(xiàn)一些非常頻繁的操作,如:權(quán)限校驗(yàn)和日志記錄代理的實(shí)現(xiàn)原理:在Ja...
    柒黍閱讀 827評(píng)論 0 1
  • 代理在我們?nèi)粘i_(kāi)發(fā)中是一個(gè)很常見(jiàn)的知識(shí)點(diǎn),也是我們面試中經(jīng)常被問(wèn)到的內(nèi)容,本本博文帶大家來(lái)學(xué)習(xí)和分析下代理的相關(guān)內(nèi)...
    24K男閱讀 752評(píng)論 2 0
  • Java代理和動(dòng)態(tài)代理機(jī)制分析和應(yīng)用 概述 代理是一種常用的設(shè)計(jì)模式,其目的就是為其他對(duì)象提供一個(gè)代理以控制對(duì)某個(gè)...
    丸_子閱讀 3,171評(píng)論 6 57
  • 虹岑閱讀 301評(píng)論 0 0

友情鏈接更多精彩內(nèi)容