將cglib動(dòng)態(tài)代理思想帶入Android開(kāi)發(fā)

動(dòng)態(tài)代理在Android實(shí)際開(kāi)發(fā)中用的并不是很多,但在設(shè)計(jì)框架的時(shí)候用的就比較多了屠缭,最近在看J2EE一些東西箍鼓,像Spring,Hibernate等都有通過(guò)動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)方法增強(qiáng)呵曹、方法攔截等需要袄秩,通過(guò)代理的方式優(yōu)雅的實(shí)現(xiàn)AOP編程。我們今天來(lái)看看這個(gè)代理究竟是什么樣子,在Android開(kāi)發(fā)中如何使用它之剧,以及將cglib動(dòng)態(tài)代理思想在Android中看看如何實(shí)現(xiàn)郭卫。

項(xiàng)目地址:MethodInterceptProxy

一、什么是代理

通常我們說(shuō)的代理背稼,在生活中就像中介贰军、經(jīng)紀(jì)人的角色。

目標(biāo)對(duì)象/被代理對(duì)象 ------ 房主:真正的租房的方法
代理對(duì)象 ------- 黑中介:有租房子的方法(調(diào)用房主的租房的方法)
執(zhí)行代理對(duì)象方法的對(duì)象 ---- 租房的人

流程:我們要租房----->中介(租房的方法)------>房主(租房的方法)
抽象:調(diào)用對(duì)象----->代理對(duì)象------>目標(biāo)對(duì)象

二蟹肘、靜態(tài)代理

先看看比較常見(jiàn)的靜態(tài)代理词疼,也就是裝飾設(shè)計(jì)模式:
首先建一個(gè)Star接口:

public interface Star {

    void singSong();
}

然后建Star子類(lèi)SuperStar

public class SuperStar implements Star {

    @Override
    public void singSong() {
        System.out.println("唱歌啦--------");
    }

}

最后我們創(chuàng)建SuperStar的代理類(lèi)SuperStarProxy

public class SuperStarProxy implements Star {

    private Star star;
    
    SuperStarProxy(Star star){
        this.star = star;
    }
    
    @Override
    public void singSong() {
        System.out.println("before-------------");
        star.singSong();
        System.out.println("after-------------");
    }
}

    public static void main(String[] args) {
        Star star = new SuperStarProxy(new SuperStar());
        star.singSong();
    }

我們將需要代理的對(duì)象傳進(jìn)來(lái)生成代理對(duì)象,之后只需要使用代理對(duì)象來(lái)處理相關(guān)業(yè)務(wù)就可以了帘腹。

三贰盗、動(dòng)態(tài)代理

靜態(tài)代理需要為每一個(gè)需要代理的類(lèi)寫(xiě)一個(gè)代理類(lèi),為每一個(gè)需要代理的方法重寫(xiě)代理方法阳欲,如果有上百個(gè)類(lèi)或者類(lèi)里方法很多舵盈,那重復(fù)的工作量也是很可觀(guān)的。JDK提供了動(dòng)態(tài)代理方式球化,可以簡(jiǎn)單理解為在JVM可以在運(yùn)行時(shí)幫我們動(dòng)態(tài)生成一系列的代理類(lèi)秽晚,這樣我們就不需要手寫(xiě)每一個(gè)靜態(tài)的代理類(lèi)了。

        final Star star = new SuperStar();
        Star st = (Star) Proxy.newProxyInstance(Star.class.getClassLoader(), star.getClass().getInterfaces(), new InvocationHandler() {
            
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                System.out.println("before---------");
                Object object = method.invoke(star, args);
                System.out.println("after---------");

                return object;
            }
        });
        st.singSong();

newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

參數(shù)一:類(lèi)加載器筒愚,動(dòng)態(tài)代理類(lèi)赴蝇,運(yùn)行時(shí)創(chuàng)建。一般情況:當(dāng)前類(lèi).class.getClassLoader()
參數(shù)二:interfaces:代表與目標(biāo)對(duì)象實(shí)現(xiàn)的所有的接口字節(jié)碼對(duì)象數(shù)組
參數(shù)三:具體的代理的操作巢掺,InvocationHandler接口

通過(guò)JDK提供的動(dòng)態(tài)代理方式句伶,我們可以很輕松的生成代理對(duì)象,但通過(guò)這種方式實(shí)現(xiàn)代理有個(gè)很大的限制就是:JDK的Proxy方式實(shí)現(xiàn)的動(dòng)態(tài)代理陆淀,目標(biāo)對(duì)象必須有接口熄阻,沒(méi)有接口不能實(shí)現(xiàn)jdk版動(dòng)態(tài)代理。

四倔约、cglib

cglib是一個(gè)功能強(qiáng)大,高性能的代碼生成包坝初。它為沒(méi)有實(shí)現(xiàn)接口的類(lèi)提供代理浸剩,為JDK的動(dòng)態(tài)代理提供了很好的補(bǔ)充。通出郏可以使用Java的動(dòng)態(tài)代理創(chuàng)建代理绢要,但當(dāng)要代理的類(lèi)沒(méi)有實(shí)現(xiàn)接口或者為了更好的性能,cglib是一個(gè)好的選擇拗小。
但是但是但是重罪,一個(gè)很致命的缺點(diǎn)是:cglib底層采用ASM字節(jié)碼生成框架,使用字節(jié)碼技術(shù)生成代理類(lèi),也就是生成的.class文件剿配,而我們?cè)赼ndroid中加載的是優(yōu)化后的.dex文件搅幅,也就是說(shuō)我們需要可以動(dòng)態(tài)生成.dex文件代理類(lèi),cglib在android中是不能使用的呼胚。但后面我們會(huì)根據(jù)dexmaker框架來(lái)仿照動(dòng)態(tài)生成.dex文件茄唐,實(shí)現(xiàn)cglib的動(dòng)態(tài)代理功能。
好了蝇更,我們先來(lái)看下cglib的強(qiáng)大吧~
舉個(gè)例子沪编,boss安排要實(shí)現(xiàn)一個(gè)人員管理的增刪改查功能,那這個(gè)簡(jiǎn)單年扩,三兩下就搞定:

public class PeopleService {

    public void add(){
        System.out.println("add-----------");
    }
    public void delete(){
        System.out.println("delete-----------");
    }
    public void update(){
        System.out.println("update-----------");
    }
    public void select(){
        System.out.println("select-----------");
    }
}

OK,搞定~但是呢蚁廓,需求是不斷變化的,過(guò)了幾天厨幻,boss又發(fā)話(huà)了相嵌,說(shuō)不是每個(gè)人都可以使用這個(gè)增刪改查功能的,要指定人員才可以使用克胳,那平绩。。我們就改吧~最直接的方式漠另,在每個(gè)方法上都加上判斷條件捏雌,但這么做多少有點(diǎn)太挫了,如果有五十個(gè)方法笆搓,那我們這么多方法就要都加一遍性湿,用cglib我們可以這么做:

        final String name = "張si";
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(PeopleService.class);
        //目標(biāo)對(duì)象攔截器,實(shí)現(xiàn)MethodInterceptor 
        //Object object為目標(biāo)對(duì)象 
        //Method method為目標(biāo)方法 
        //Object[] args 為參數(shù)满败, 
        //MethodProxy proxy CGlib方法代理對(duì)象 
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object object, Method method, Object[] args,
                    MethodProxy proxy) throws Throwable {
                Object obj = null;
                if(name.equals("張三")){
                     obj = proxy.invokeSuper(object, args); ;
                }else{
                    System.out.println("----對(duì)不起肤频,您沒(méi)有權(quán)限----");
                }
                return obj;
            }
        });
        PeopleService ps = (PeopleService) enhancer.create();
        ps.add();

只需添加一個(gè)攔截器,就可以攔截所有增刪改查操作算墨,從切面進(jìn)行統(tǒng)一管理宵荒,代碼量也不多。
又過(guò)了幾天净嘀,boss又發(fā)話(huà)了报咳,我們的增刪改查不是都要有限制,只有查找才對(duì)特定人員有限制挖藏,那我們就繼續(xù)改嘍~
這個(gè)時(shí)候我們就可以加入過(guò)濾器CallbackFilter:

final String name = "張武";
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(PeopleService.class);
        //目標(biāo)對(duì)象攔截器暑刃,實(shí)現(xiàn)MethodInterceptor 
        //Object object為目標(biāo)對(duì)象 
        //Method method為目標(biāo)方法 
        //Object[] args 為參數(shù), 
        //MethodProxy proxy CGlib方法代理對(duì)象 
        MethodInterceptor interceptor = new MethodInterceptor() {
            @Override
            public Object intercept(Object object, Method method, Object[] args,
                    MethodProxy proxy) throws Throwable {
                Object obj = null;
                if(name.equals("張三")){
                     obj = proxy.invokeSuper(object, args); ;
                }else{
                    System.out.println("----對(duì)不起膜眠,您沒(méi)有權(quán)限----");
                }
                return obj;
            }
        };
        //NoOp.INSTANCE:這個(gè)NoOp表示no operator岩臣,即什么操作也不做溜嗜,代理類(lèi)直接調(diào)用被代理的方法不進(jìn)行攔截
        enhancer.setCallbacks(new Callback[]{interceptor,NoOp.INSTANCE});
        enhancer.setCallbackFilter(new CallbackFilter() {
            //過(guò)濾方法 
            //返回的值為數(shù)字,代表了Callback數(shù)組中的索引位置架谎,要到用的Callback 
            @Override
            public int accept(Method method) {
                if(method.getName().equals("select")){
                    return 0;
                }
                return 1;
            }
        });
        PeopleService ps = (PeopleService) enhancer.create();
        ps.add();

我們沒(méi)有修改一行原有的增刪改查代碼炸宵,通過(guò)傳遞代理對(duì)象的方式輕松解決方法攔截、方法增強(qiáng)的業(yè)務(wù)需求狐树。
但遺憾的是焙压,cglib不支持android平臺(tái)。抑钟。涯曲。那我們就自己實(shí)現(xiàn)咯~在dexmaker和cglib-for-android庫(kù)的基礎(chǔ)上,修改部分代碼后形成我們的類(lèi)似cglib框架 MethodInterceptProxy 在塔,實(shí)現(xiàn)上面需求只需這樣寫(xiě)幻件,和cglib寫(xiě)法一致:

        final String name = "張五";
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(PeopleService.class);
        //目標(biāo)對(duì)象攔截器,實(shí)現(xiàn)MethodInterceptor 
        //Object object為目標(biāo)對(duì)象 
        //Method method為目標(biāo)方法 
        //Object[] args 為參數(shù)蛔溃, 
        //MethodProxy proxy CGlib方法代理對(duì)象 
        MethodInterceptor interceptor = new MethodInterceptor() {
            @Override
            public Object intercept(Object object, Object[] args, MethodProxy methodProxy) throws Throwable {
                Object obj = null;
                if(name.equals("張三")){
                     obj = methodProxy.invokeSuper(object, args); ;
                }else{
                    System.out.println("----對(duì)不起绰沥,您沒(méi)有權(quán)限----");
                }
                return obj;
            }
        };
        //NoOp.INSTANCE:這個(gè)NoOp表示no operator,即什么操作也不做贺待,代理類(lèi)直接調(diào)用被代理的方法不進(jìn)行攔截
        enhancer.setCallbacks(new MethodInterceptor[]{interceptor,NoOp.INSTANCE});
        enhancer.setCallbackFilter(new CallbackFilter() {
            //過(guò)濾方法 
            //返回的值為數(shù)字徽曲,代表了Callback數(shù)組中的索引位置,要到用的Callback 
            @Override
            public int accept(Method method) {
                if(method.getName().equals("select")){
                    return 0;
                }
                return 1;
            }
        });
        PeopleService ps = (PeopleService) enhancer.create();
        ps.add();

項(xiàng)目地址:MethodInterceptProxy

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末麸塞,一起剝皮案震驚了整個(gè)濱河市秃臣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哪工,老刑警劉巖奥此,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異雁比,居然都是意外死亡稚虎,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)偎捎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蠢终,“玉大人,你說(shuō)我怎么就攤上這事茴她⊙胺鳎” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵败京,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我梦染,道長(zhǎng)赡麦,這世上最難降的妖魔是什么朴皆? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮泛粹,結(jié)果婚禮上遂铡,老公的妹妹穿的比我還像新娘。我一直安慰自己晶姊,他們只是感情好扒接,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著们衙,像睡著了一般钾怔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蒙挑,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天宗侦,我揣著相機(jī)與錄音,去河邊找鬼忆蚀。 笑死矾利,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的馋袜。 我是一名探鬼主播男旗,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼欣鳖!你這毒婦竟也來(lái)了察皇?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤观堂,失蹤者是張志新(化名)和其女友劉穎让网,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體师痕,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡溃睹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了胰坟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片因篇。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖笔横,靈堂內(nèi)的尸體忽然破棺而出竞滓,到底是詐尸還是另有隱情,我是刑警寧澤吹缔,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布商佑,位于F島的核電站,受9級(jí)特大地震影響厢塘,放射性物質(zhì)發(fā)生泄漏茶没。R本人自食惡果不足惜肌幽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望抓半。 院中可真熱鬧喂急,春花似錦、人聲如沸笛求。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)探入。三九已至狡孔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間新症,已是汗流浹背步氏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留徒爹,地道東北人荚醒。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像隆嗅,于是被迫代替她去往敵國(guó)和親界阁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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