Java中的代理模式

因为Retrofit剖析源码的时候会用到ava中的代理模式,所以这篇就先回忆一下代理设计模式。
代理模式分为两种:
代理模式解释:为其他对象提供一种代理,用以控制对这个对象的访问。(直白一点就是代替别人去做一些我们想要做的事情)
1.静态代理(抽象类)


image.png

代码实现:

/**
 * 被目标对象和代理对象同时继承的抽象类
 */
public abstract class AbstractObject {
    protected abstract void operation(); // 经营预算
}

被代理对象:

/**
 * 目标对象 (被代理对象)
 */
public class RealObject extends AbstractObject{

    private static final String TAG = RealObject.class.getSimpleName();

    @Override
    public void operation() {
        Log.e(TAG,"do operation......");
    }
}

代理对象:

/**
 * 代理类
 */
public class ProxyObject extends AbstractObject {

    private static final String TAG = ProxyObject.class.getSimpleName();
    // 对目标类的引用
    private RealObject mRealObject;

    public ProxyObject(RealObject mRealObject) {
        this.mRealObject = mRealObject;
    }

    @Override
    public void operation() {
        Log.e(TAG,"do something before real operation......");
        if (mRealObject == null) {
            mRealObject = new RealObject();
        }
        mRealObject.operation();
        Log.e(TAG,"do something after real operation......");
    }
}

测试:

public static void main(String[] args) {
        ProxyObject proxyObject = new ProxyObject(new RealObject());
        proxyObject.operation();
    }

do something before real operation......
do operation......
do something after real operation......

2.动态代理(JDK实现/CJLib继承)

  • 概念解释:代理类在程序运行时创建的代理方式。
  • 相比于静态代理:动态代理可以很方便的去改变代理类的函数进行统一的处理,而不用去修改每一个代理类的函数,根据你相应的业务逻辑
  • 优点:无侵入式的扩展代码

JDK动态代理:Java内部的反射机制来实现的,这个机制在生成类的时候比较高效。(必须要针对接口进行代理)

  • 在不改变原来代码的情况下,在调用方法的前后,插入我们自己想要的逻辑。(例子)

  • 动态代理和静态代理最大的不同,动态代理的代理类是不需要手动生成的,是根据我们的配置在运行时动态生成的。(是在运行时生成的代理)

  • 需要用到InvocationHandler接口和Proxy类

代码示例:
被目标对象和代理对象都要实现的接口

/**
 * 动态代理
 * 被目标对象和代理对象都要实现的接口
 */
public interface InterfaceObject {
    void shopping();
}

被代理类 (目标类):

/**
 * 动态代理
 * 被代理类 (目标类)
 */
public class BeRepresent implements InterfaceObject {
    private static final String TAG = BeRepresent.class.getSimpleName();
    @Override
    public void shopping() {
        System.out.println("买点东西回来");
    }
}

代理类是由JDK动态创建的:

/**
 * 动态代理
 * 代理类:每个代理类的对象都会关联一个表示内部处理逻辑的InvocationHandler接口的实现
 * 当使用者调用了代理对象,所实现的代理对象的接口时,这个调用的信息就会传递到InvocationHandler
 * 中的invoke方法中
 */
public class ProxyF implements InvocationHandler {

    private static final String TAG = ProxyF.class.getSimpleName();
    private Object target; // 要代理的真实对象

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

    /**
     * 相当于一个拦截的方法
     * @param proxy   代理对象
     * @param method  代理方法
     * @param objects 代理方法中的参数
     * @return 返回值会返回给使用者
     * @throws Throwable 异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
        System.out.println("代理对象:" + proxy.getClass().getName());
        System.out.println("代理方法中的参数:" + (objects != null && objects.length > 0 ? objects[0] : null));
        System.out.println("代理方法:" + method);
        System.out.println(" before ");
        method.invoke(target,objects);
        System.out.println(" after ");
    }
}

测试:

public static void main(String[] args) {
        InterfaceObject mBeRepresent = new BeRepresent();
        ProxyF proxy = new ProxyF(mBeRepresent);
        // java.lang.reflect.Proxy.newProxyInstance(....)方法获得真实对象的代理对象 (反射)
        InterfaceObject mInterfaceObject = (InterfaceObject) Proxy.newProxyInstance(mBeRepresent.getClass().getClassLoader()
                , mBeRepresent.getClass().getInterfaces(), proxy);
        // 通过代理对象调用真实对象相关接口中实现的方法,这个时候就会跳转到这个代理对象所关联的InvocationHandler
        // 的invoke方法中
        mInterfaceObject.shopping();
        // 获得真实对象的代理对象所对应的class对象的名称,用字符串表示
        System.out.println(mInterfaceObject.getClass().getName());
}

结果:

代理对象:com.sun.proxy.$Proxy0
代理方法中的参数:null
代理方法:public abstract void dynamicproxy.InterfaceObject.shopping()
 before 
买点东西回来
 after 
com.sun.proxy.$Proxy0

注意:被代理对象的类必须自己定义时就实现接口,从该类的祖辈类上继承的接口是无效的。
总结:
JDK实现原理:

1.拿到被代理对象的引用,然后获取它实现的接口
2.JDK重新生成一个类,同时实现获取到被代理对象所实现的接口
3.在代理类的静态构造块中,代理类通过反射获取了被代理类的详细信息
4.重新生成一个class字节码
5.然后编译
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。

还有一种是CGlib实现的动态代理:
cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。

静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;

最后,抛出两个问题:

1.retrofit为什么要使用动态代理 ?
答案:
因为1个静态代理只服务1种类型的目标对象,若要服务多类型的目标对象,则需要为每种目标对象都实现一个静态代理对象 ,在目标对象较多的情况下,若采用静态代理,则会出现 静态代理对象量多、代码量大,从而导致代码复杂的问题,而动态代理只需要1个动态代理类就可以解决创建多个静态代理的问题,避免重复、多余代码 更强的灵活性设计动态代理类(DynamicProxy)时,不需要显式实现与目标对象类(RealSubject)相同的接口,而是将这种实现推迟到程序运行时由JVM来实现,在使用时(调用目标对象方法时)才会动态创建动态代理类 & 实例,不需要事先实例化。

2.动态代理的缺点是什么?
答案:
效率低
相比静态代理中直接调用目标对象方法,动态代理则需要先通过Java反射机制 从而 间接调用目标对象方法。

应用场景局限
因为Java 的单继承特性(每个代理类都继承了 Proxy 类),即只能针对接口 创建 代理类,不能针对类 创建代理类即只能动态代理 实现了接口的类。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • 原作者:frank909原博客链接:轻松学,Java 中的代理模式及动态代理 前几天我写了《秒懂,Java 注解 ...
    骇客与画家阅读 387评论 0 1
  • 代理模式 在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用...
    篱开罗阅读 449评论 0 5
  • 代理模式:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而...
    luweicheng24阅读 237评论 0 0
  • 本文动态代理部分内容大量引自:http://www.ibm.com/developerworks/cn/java/...
    端木轩阅读 406评论 0 0
  • 动态代理: 代理:本来应该自己做的事情,却请了别人来做,被请来的人就是代理对象例如:动态代理:在程序运行的过程中产...
    王魔王阅读 359评论 0 4