Java之動(dòng)態(tài)代理

靜態(tài)代理

先看一個(gè)例子,有個(gè)汽車(chē)記錄功能荔仁,我們既要記錄行駛的時(shí)間伍宦,又要記錄其它日志,如果這些事全部交給Car這個(gè)對(duì)象來(lái)做乏梁,那么它要處理的事情就太多了蜗细,既要跑還有寫(xiě)灌灾,所以為了給Car減輕負(fù)擔(dān)喳魏,代理類(lèi)就誕生了羡藐,代碼如下:

//接口類(lèi)
public interface MoveAble {
    void move();
}

//Car類(lèi)
public class Car implements MoveAble {
    @Override
    public void move() {
        System.out.println("car move中");
    }
}

//記錄時(shí)間代理類(lèi)
public class TimeProxy implements MoveAble{
    private MoveAble m;
    public TimeProxy(MoveAble m){
        this.m = m;
    }

    @Override
    public void move() {
        System.out.println("time開(kāi)始");
        m.move();
        System.out.println("time結(jié)束");
    }
}

//記錄日志代理類(lèi)
public class LogProxy implements MoveAble {
    private MoveAble m;
    public LogProxy(MoveAble m) {
        this.m = m;
    }

    @Override
    public void move() {
        System.out.println("log開(kāi)始");
        m.move();
        System.out.println("log結(jié)束");
    }
}

從以上的代碼中可以看出我們創(chuàng)造了兩個(gè)新的類(lèi)去繼承和Car一樣的接口,這樣做本質(zhì)上來(lái)說(shuō)它們?nèi)齻€(gè)就都有跑的方法了落萎。那么我們可以像下面的代碼那樣調(diào)用它們:

public static void main(String[] args) {
        Car car = new Car();
        LogProxy logProxy = new LogProxy(car);
        TimeProxy timeProxy = new TimeProxy(logProxy);
        timeProxy.move();
}

上面這段代碼中把Car先放進(jìn)了LogProxy里亥啦,又把LogProxy放進(jìn)了TimeProxy里,接著調(diào)用了TimeProxy對(duì)象的move方法练链,這時(shí)候會(huì)一層一層的調(diào)用move方法翔脱,從TimeProxy到LogProxy再到Car,這樣子就把記錄時(shí)間和日志的功能給完成了媒鼓。
那么這種代理模式有什么好處呢届吁,可以讓不同的功能做不同的事,也就是單一職責(zé)隶糕,拆分了代碼邏輯瓷产,使邏輯更加的清晰了站玄。
但是這種靜態(tài)的代理有個(gè)問(wèn)題枚驻,就是我們需要自己去寫(xiě)代理類(lèi),如果功能少還好說(shuō)株旷,要是功能多的話(huà)就會(huì)很麻煩再登,所以,動(dòng)態(tài)代理就開(kāi)始登場(chǎng)了晾剖。

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

jdk中為我們提供了創(chuàng)建動(dòng)態(tài)代理對(duì)象的方式锉矢,即Proxy類(lèi)。
Proxy類(lèi)位于java.lang.reflect包中齿尽,包含有一個(gè)靜態(tài)的創(chuàng)建方法newProxyInstance沽损,代碼如下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException{
       ......//暫時(shí)可以忽略的邏輯
}

在上面的創(chuàng)建Proxy類(lèi)代碼中需要接口三個(gè)參數(shù):

  • ClassLoader loader
    類(lèi)加載器
  • Class<?>[] interfaces
    類(lèi)實(shí)現(xiàn)的接口
  • InvocationHandler h 處理器

我們重點(diǎn)看一下第三個(gè)參數(shù)InvocationHandler,這個(gè)類(lèi)是個(gè)接口循头,里面只有一個(gè)invoke方法绵估,我們所要做的處理邏輯都會(huì)在這個(gè)方法里執(zhí)行炎疆。代碼如下:

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

invoke方法里也有三個(gè)參數(shù):

  • Object proxy 調(diào)用invoke方法的對(duì)象實(shí)例
  • Method method 對(duì)象的方法
  • Object[] args 參數(shù)

那么我們應(yīng)該怎么使用動(dòng)態(tài)代理創(chuàng)建我們的代理類(lèi)呢,基于上面Car記錄時(shí)間和日志的例子国裳,我們可以這樣寫(xiě):

public static void main(String[] args) {
        Car car = new Car();
        MoveAble m = (MoveAble) Proxy.newProxyInstance(car.getClass().getClassLoader(), car.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                System.out.println("log開(kāi)始");
                System.out.println("time開(kāi)始");

                method.invoke(car);

                System.out.println("time結(jié)束");
                System.out.println("log結(jié)束");

                return null;
            }
        });
        m.move();
}

是不是很簡(jiǎn)單形入,有個(gè)動(dòng)態(tài)代理,我們就不用自己去創(chuàng)建LogProxy缝左,TimeProxy等等代理類(lèi)了亿遂。

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

知道了通過(guò)動(dòng)態(tài)代理的方式可以幫我們自動(dòng)的生成一些代理類(lèi),那么Proxy究竟是怎么辦到的呢渺杉,下面就開(kāi)始分析一下Proxy的內(nèi)部原理蛇数。首先,看一下newProxyInstance這個(gè)靜態(tài)方法是越,代碼如下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        
        Class<?> cl = getProxyClass0(loader, intfs); //1

        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);//2
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                cons.setAccessible(true);
                AccessController.doPrivileged call.
            }
            return cons.newInstance(new Object[]{h});//3
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

為了邏輯清晰我刪除了注釋?zhuān)@段代碼的主要邏輯就是在1苞慢,2,3處英妓,這三步分別是:

  • 1.得到Proxy的Class對(duì)象
  • 2.通過(guò)Class對(duì)象得到Constructor對(duì)象
  • 3.通過(guò)Constructor對(duì)象的newInstance方法獲得實(shí)例對(duì)象

下面我們主要分析Class<?> cl = getProxyClass0(loader, intfs); //1這行代碼挽放,來(lái)看看這個(gè)方法是怎么生成的Class類(lèi)對(duì)象的。
這個(gè)方法的代碼很簡(jiǎn)單:

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
}

主要就是看最后一行代碼蔓纠,返回的就是Class對(duì)象

public V get(K key, P parameter) {

       ......

        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;//1
                }
            }

            // lazily construct a Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

這段代碼我刪除了一些注釋和與主要邏輯關(guān)系不大的代碼辑畦。
首先來(lái)看幾個(gè)比較的對(duì)象:

  1. Supplier<V> supplier
  2. Factory factory

先看最后返回的value是通過(guò)supplier.get()的,也就是代碼中1處的邏輯腿倚,那么這個(gè)supplier是什么呢纯出,它是個(gè)接口,下面的factory是它的實(shí)現(xiàn)類(lèi)敷燎,下面的while循環(huán)中就是給supplier賦值的過(guò)程暂筝。也就是說(shuō)value的最終獲取是通過(guò)factory里的get方法取得,那么我們?cè)倏匆幌逻@個(gè)get方法邏輯:

public synchronized V get() { // serialize access
            ......

            // create new value
            V value = null;
            try {
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));//1
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }

            return value;
}

這里最重要的邏輯是代碼1處硬贯,從valueFactory的apply里得到了value焕襟,也就是我們最終要得到的Class對(duì)象。那么我們?cè)賮?lái)看看這個(gè)valueFactory是什么饭豹。

 public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }

valueFactory是BiFunction接口的實(shí)現(xiàn)類(lèi)對(duì)象鸵赖,WeakCache初始化的時(shí)候被傳進(jìn)來(lái)

public class Proxy implements java.io.Serializable {

    private static final long serialVersionUID = -2222568056686623797L;

    /** parameter types of a proxy class constructor */
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

    /**
     * a cache of proxy classes
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

        ......
}

可以看出來(lái)是在Proxy類(lèi)中被初始化的,也就是ProxyClassFactory類(lèi)對(duì)象拄衰。這個(gè)ProxyClassFactory也實(shí)現(xiàn)了BiFunction它褪,那么上面的apply方法最終的實(shí)現(xiàn)邏輯就是在這個(gè)類(lèi)中:

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

           ......
                
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num;

                return generateProxy(proxyName, interfaces, loader, methodsArray,
                                     exceptionsArray);
            }
}

這個(gè)方法代碼很長(zhǎng),我們只需要關(guān)注這幾行就行了翘悉,可以看出茫打,這個(gè)方法設(shè)定了一個(gè)規(guī)則,生成一個(gè)字符串proxyName也就是我們的代理類(lèi)的類(lèi)名,最后的generateProxy方法實(shí)際上是調(diào)用了本地的一個(gè)方法老赤,就是生成Class對(duì)象的方法:

@FastNative
    private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
                                                 ClassLoader loader, Method[] methods,
                                                 Class<?>[][] exceptions);

那么到現(xiàn)在為止就把動(dòng)態(tài)代理怎么生成代理對(duì)象的機(jī)制分析完了饼煞。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市诗越,隨后出現(xiàn)的幾起案子砖瞧,更是在濱河造成了極大的恐慌,老刑警劉巖嚷狞,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件块促,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡床未,警方通過(guò)查閱死者的電腦和手機(jī)竭翠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)薇搁,“玉大人斋扰,你說(shuō)我怎么就攤上這事】醒螅” “怎么了传货?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)宏娄。 經(jīng)常有香客問(wèn)我问裕,道長(zhǎng),這世上最難降的妖魔是什么孵坚? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任粮宛,我火速辦了婚禮,結(jié)果婚禮上卖宠,老公的妹妹穿的比我還像新娘巍杈。我一直安慰自己,他們只是感情好扛伍,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布筷畦。 她就那樣靜靜地躺著,像睡著了一般蜒秤。 火紅的嫁衣襯著肌膚如雪汁咏。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天作媚,我揣著相機(jī)與錄音,去河邊找鬼帅刊。 笑死纸泡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播女揭,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蚤假,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了吧兔?” 一聲冷哼從身側(cè)響起磷仰,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎境蔼,沒(méi)想到半個(gè)月后灶平,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡箍土,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年逢享,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吴藻。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瞒爬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沟堡,到底是詐尸還是另有隱情侧但,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布航罗,位于F島的核電站俊犯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏伤哺。R本人自食惡果不足惜燕侠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望立莉。 院中可真熱鬧绢彤,春花似錦、人聲如沸蜓耻。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)刹淌。三九已至饶氏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間有勾,已是汗流浹背疹启。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蔼卡,地道東北人喊崖。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親荤懂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子茁裙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • java的動(dòng)態(tài)代理機(jī)制是在運(yùn)行期間為目標(biāo)對(duì)象生成一個(gè)代理對(duì)象,而將自己格外需要處理的業(yè)務(wù)邏輯進(jìn)行“插入”节仿,以達(dá)到運(yùn)...
    Sophie12138閱讀 328評(píng)論 0 0
  • https://blog.csdn.net/luanlouis/article/details/24589193 ...
    小陳阿飛閱讀 856評(píng)論 1 1
  • 《李鴻章傳》是由梁?jiǎn)⒊臅?shū)籍晤锥,從李鴻章的早年落拓,寫(xiě)到他參加鎮(zhèn)壓太平軍廊宪、甲午海戰(zhàn)矾瘾,創(chuàng)辦洋務(wù)運(yùn)動(dòng),周旋于世界外交...
    落揚(yáng)虛虛閱讀 1,153評(píng)論 1 4
  • 1.我們那邊的村落跟北方的村落不太一樣挤忙,北方那邊的村落都比較大霜威,而且各種姓氏的都有,也有很多同村落結(jié)婚的册烈,而我們那...
    安山1閱讀 117評(píng)論 0 1
  • 這個(gè)雨蒙蒙的夜戈泼,一部電影剛好契合我今晚的心情,慶幸的是赏僧,身邊的好友也有此共鳴大猛。????????? 對(duì)這部片子沒(méi)...
    亦水菲甜閱讀 166評(píng)論 0 0