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

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

項(xiàng)目地址:MethodInterceptProxy

一、什么是代理

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

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

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

二、靜態(tài)代理

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

public interface Star {

    void singSong();
}

然后建Star子類SuperStar

public class SuperStar implements Star {

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

}

最后我們創(chuàng)建SuperStar的代理類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();
    }

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

三、動態(tài)代理

靜態(tài)代理需要為每一個需要代理的類寫一個代理類,為每一個需要代理的方法重寫代理方法,如果有上百個類或者類里方法很多,那重復(fù)的工作量也是很可觀的。JDK提供了動態(tài)代理方式,可以簡單理解為在JVM可以在運(yùn)行時幫我們動態(tài)生成一系列的代理類,這樣我們就不需要手寫每一個靜態(tà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ù)一:類加載器,動態(tài)代理類,運(yùn)行時創(chuàng)建。一般情況:當(dāng)前類.class.getClassLoader()
參數(shù)二:interfaces:代表與目標(biāo)對象實(shí)現(xiàn)的所有的接口字節(jié)碼對象數(shù)組
參數(shù)三:具體的代理的操作,InvocationHandler接口

通過JDK提供的動態(tài)代理方式,我們可以很輕松的生成代理對象,但通過這種方式實(shí)現(xiàn)代理有個很大的限制就是:JDK的Proxy方式實(shí)現(xiàn)的動態(tài)代理,目標(biāo)對象必須有接口,沒有接口不能實(shí)現(xiàn)jdk版動態(tài)代理。

四、cglib

cglib是一個功能強(qiáng)大,高性能的代碼生成包。它為沒有實(shí)現(xiàn)接口的類提供代理,為JDK的動態(tài)代理提供了很好的補(bǔ)充。通??梢允褂肑ava的動態(tài)代理創(chuàng)建代理,但當(dāng)要代理的類沒有實(shí)現(xiàn)接口或者為了更好的性能,cglib是一個好的選擇。
但是但是但是,一個很致命的缺點(diǎn)是:cglib底層采用ASM字節(jié)碼生成框架,使用字節(jié)碼技術(shù)生成代理類,也就是生成的.class文件,而我們在android中加載的是優(yōu)化后的.dex文件,也就是說我們需要可以動態(tài)生成.dex文件代理類,cglib在android中是不能使用的。但后面我們會根據(jù)dexmaker框架來仿照動態(tài)生成.dex文件,實(shí)現(xiàn)cglib的動態(tài)代理功能。
好了,我們先來看下cglib的強(qiáng)大吧~
舉個例子,boss安排要實(shí)現(xià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,搞定~但是呢,需求是不斷變化的,過了幾天,boss又發(fā)話了,說不是每個人都可以使用這個增刪改查功能的,要指定人員才可以使用,那。。我們就改吧~最直接的方式,在每個方法上都加上判斷條件,但這么做多少有點(diǎn)太挫了,如果有五十個方法,那我們這么多方法就要都加一遍,用cglib我們可以這么做:

        final String name = "張si";
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(PeopleService.class);
        //目標(biāo)對象攔截器,實(shí)現(xiàn)MethodInterceptor 
        //Object object為目標(biāo)對象 
        //Method method為目標(biāo)方法 
        //Object[] args 為參數(shù), 
        //MethodProxy proxy CGlib方法代理對象 
        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("----對不起,您沒有權(quán)限----");
                }
                return obj;
            }
        });
        PeopleService ps = (PeopleService) enhancer.create();
        ps.add();

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

final String name = "張武";
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(PeopleService.class);
        //目標(biāo)對象攔截器,實(shí)現(xiàn)MethodInterceptor 
        //Object object為目標(biāo)對象 
        //Method method為目標(biāo)方法 
        //Object[] args 為參數(shù), 
        //MethodProxy proxy CGlib方法代理對象 
        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("----對不起,您沒有權(quán)限----");
                }
                return obj;
            }
        };
        //NoOp.INSTANCE:這個NoOp表示no operator,即什么操作也不做,代理類直接調(diào)用被代理的方法不進(jìn)行攔截
        enhancer.setCallbacks(new Callback[]{interceptor,NoOp.INSTANCE});
        enhancer.setCallbackFilter(new CallbackFilter() {
            //過濾方法 
            //返回的值為數(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();

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

        final String name = "張五";
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(PeopleService.class);
        //目標(biāo)對象攔截器,實(shí)現(xiàn)MethodInterceptor 
        //Object object為目標(biāo)對象 
        //Method method為目標(biāo)方法 
        //Object[] args 為參數(shù), 
        //MethodProxy proxy CGlib方法代理對象 
        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("----對不起,您沒有權(quán)限----");
                }
                return obj;
            }
        };
        //NoOp.INSTANCE:這個NoOp表示no operator,即什么操作也不做,代理類直接調(diào)用被代理的方法不進(jìn)行攔截
        enhancer.setCallbacks(new MethodInterceptor[]{interceptor,NoOp.INSTANCE});
        enhancer.setCallbackFilter(new CallbackFilter() {
            //過濾方法 
            //返回的值為數(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)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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