回顾一下基础之静态代理&动态代理

项目中常用到代理模式,本篇文章带大家回顾一下基本的操作以及动态代理生成的类。

1. 定义

代理模式: 为其它对象提供代理,带你对象挟持原对象类的引用,也称委托模式。
作用:可以在不修改原对象的功能前提下,对原对象在功能进行扩展。(通俗讲就是在当你原对象封装完毕或者你没办法修改,但是有一些增加的新的功能的时候,就可以在代理类上增加 符合“开闭原则”)

2. 代理的方式

  • 静态代理

使用:手动创建源代码,在对其编译。
缺点:代理类跟原对象类实现一样的接口,所以会有很多代理类。并且,当接口增加方法,目标对象与代理对象都要对应修改。(怎么解决?改用动态代理。)

  • 动态代理

使用:在程序运行时,利用反射机制动态创建。

3. 基本方式

  • 静态代理的使用方式
    (下面以高低端电脑的例子展示一下基本使用方式)
    image.png
  1. 定义接口
/**
 * Create by ldr
 * on 2019/12/10 15:06.
 * 定义接口,电脑接口
 */
public interface Computer {
    void screen();//显示器
    void memoryBank();//内存条
    void mainBoard();//主板
    void graphicsCard();//显卡
}
  1. 分别定义两个被代理对象
------------------------------------------高配版电脑---------------------------------------------------
/**
 * Create by ldr
 * on 2019/12/10 15:08.
 * 高配电脑
 */
public class HeightConfigurationComputer implements Computer{
    private static final String TAG = "HeightComp";
    @Override
    public void screen() {
        System.out.println("高配置电脑 -- 屏幕");
    }

    @Override
    public void memoryBank() {
        System.out.println("高配置电脑 -- 内存条");
    }

    @Override
    public void mainBoard() {
        System.out.println("高配置电脑 -- 主板");
    }

    @Override
    public void graphicsCard() {
        System.out.println("高配置电脑 -- 显卡");
    }
}

------------------------------------------低配版电脑---------------------------------------------------
/**
 * Create by ldr
 * on 2019/12/10 15:08.
 * 低配电脑
 */
public class LowConfigurationComputer implements Computer {
    private static final String TAG = "LowComputer";
    @Override
    public void screen() {
        System.out.println("低配置电脑 -- 屏幕");
    }

    @Override
    public void memoryBank() {
        System.out.println("低配置电脑 -- 内存条");
    }

    @Override
    public void mainBoard() {
        System.out.println("低配置电脑 -- 主板");
    }

    @Override
    public void graphicsCard() {
        System.out.println("低配置电脑 -- 显卡");
    }
}
  1. 创建静态代理类
/**
 * Create by ldr
 * on 2019/12/10 15:14.
 * 静态代理
 */
public class ComputerStaticPro implements Computer {

    private Computer mComputer;

    public void setProx(Computer computer){
        mComputer = computer;
    }

    @Override
    public void screen() {
        System.out.println("代理类可先做共用一些逻辑~");
        mComputer.screen();
    }

    @Override
    public void memoryBank() {
        System.out.println("代理类可先做共用一些逻辑~");
        mComputer.memoryBank();
    }

    @Override
    public void mainBoard() {
        System.out.println("代理类可先做共用一些逻辑~");
        mComputer.mainBoard();
    }

    @Override
    public void graphicsCard() {
        System.out.println("代理类可先做共用一些逻辑~");
        mComputer.graphicsCard();
    }
}

  1. 使用方式
       HeightConfigurationComputer heightConfigurationComputer = new HeightConfigurationComputer();
        ComputerStaticPro staticPro = new ComputerStaticPro();
        staticPro.setProx(heightConfigurationComputer);
        staticPro.graphicsCard();
        staticPro.mainBoard();
        staticPro.screen();
        staticPro.memoryBank();

        //低配电脑
        staticPro.setProx(new LowConfigurationComputer());
        staticPro.graphicsCard();
        staticPro.mainBoard();
        staticPro.screen();
        staticPro.memoryBank();
-----------------------打印出来的Log
代理类可先做共用一些逻辑~
高配置电脑 -- 显卡
代理类可先做共用一些逻辑~
高配置电脑 -- 主板
代理类可先做共用一些逻辑~
高配置电脑 -- 屏幕
代理类可先做共用一些逻辑~
高配置电脑 -- 内存条
代理类可先做共用一些逻辑~
低配置电脑 -- 显卡
代理类可先做共用一些逻辑~
低配置电脑 -- 主板
代理类可先做共用一些逻辑~
低配置电脑 -- 屏幕
代理类可先做共用一些逻辑~
低配置电脑 -- 内存条
  • 动态代理的使用方式
  1. 创建动态代理类
/**
 * Create by ldr
 * on 2019/12/10 15:55.
 * 动态代理
 */
public class ComputerDynamicPro implements InvocationHandler {
    //动态代理类需要被代理的实例
    private Computer mComputer;

    public ComputerDynamicPro(Computer computer){
        mComputer = computer;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //关键点1
        //执行被代理的方法的类  方法被调用时就会自动调用
        Object invoke =  method.invoke(mComputer,args);
        //关键点2 
        String name = method.getName();
        if (name.equals("screen")){
            System.out.println(mComputer.getClass().getSimpleName()+"  screen()" );
        }else if (name.equals("memoryBank")){
            System.out.println(mComputer.getClass().getSimpleName()+"  memoryBank()" );
        }else if (name.equals("mainBoard")){
            System.out.println(mComputer.getClass().getSimpleName()+"  mainBoard()" );
        }else if(name.equals("graphicsCard")){
            System.out.println(mComputer.getClass().getSimpleName()+"  graphicsCard()" );
        }
        return invoke;
    }
}

动态代理需要实现的是InvocationHandler接口,并重写invoke方法,同样也是要挟持被代理类的对象。
invoke方法中可以看到,使用的method.invoke(mComputer,args);传入对象跟args便可以实现方法被调用时自动调用。

在这个地方,其实你可以做的事情很多,比如你想知道方式的执行时间,你可以在method.invoke()关键点1关键点2,加上时间,根据两个时间差来计算一下这个方法的大致运行时间。(实现AOP(面向程序切面编程))

  1. 使用
//关键代码1
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//        //使用动态代理
//        //高配电脑
HeightConfigurationComputer heightConfigurationComputer = new HeightConfigurationComputer(); 
ClassLoader classLoader = heightConfigurationComputer.getClass().getClassLoader();
Class[] interfaces = heightConfigurationComputer.getClass().getInterfaces();
ComputerDynamicPro computerDynamicPro = new ComputerDynamicPro(heightConfigurationComputer);

//通过Proxy.newProxyInstance生成代理类对象
// 关键代码2
Object newProxyInstance = Proxy.newProxyInstance(classLoader,interfaces,computerDynamicPro);
Computer computer = (Computer) newProxyInstance;
computer.graphicsCard();
computer.mainBoard();
computer.memoryBank();
computer.screen();

关键代码1:是看人家网上说的可以将代理类生成的一句代码,等下再贴一下代理类的代码
关键代码2:这里可以看到Proxy.newProxyInstance传入了三个参数。

第一个参数ClassLoader loader:被代理的类的类加载器,通过它生成代理的class文件。

第二个参数Class<?>[] interfaces:被代理类实现的所有接口字节码,代理类需要知道并实现

第三个参数InvocationHandler h:调用处理程序调度方法调用,也就是第一步我们定义好的那个ComputerDynamicPro 实现了InvocationHandler接口的动态代理类。

我们来看一下关键代码1中为我们生成的代理类的代码吧


package com.sun.proxy;

import com.mzs.aptpro.Computer;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Computer {
    private static Method m1;
    private static Method m5;
    private static Method m2;
    private static Method m3;
    private static Method m6;
    private static Method m0;
    private static Method m4;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void graphicsCard() throws  {
        try {
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void mainBoard() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void screen() throws  {
        try {
            super.h.invoke(this, m6, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void memoryBank() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m5 = Class.forName("com.mzs.aptpro.Computer").getMethod("graphicsCard");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.mzs.aptpro.Computer").getMethod("mainBoard");
            m6 = Class.forName("com.mzs.aptpro.Computer").getMethod("screen");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m4 = Class.forName("com.mzs.aptpro.Computer").getMethod("memoryBank");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到上面class $Proxy0 extends Proxy implements Computer也是要实现我们的接口。在代码最下面可以看到静态代码块中,使用了反射将所有方法获取到。
每个方法中都调用super.h.invoke()这样的一个代码 并且传入三个参数this,反射获取的方法,参数数组
super.h实际上父类的h也是$Proxy0中的InvocationHandler,在构造函数的时候传递上去的public $Proxy0(InvocationHandler var1) throws { super(var1); }
这里你就可以明白了为什么我们要动态代理类要实现InvocationHandler并实现invoke方法,并且inovke方法的三个参数是什么时候传递进来的。

总结一下:

静态代理跟动态代理的区别:
静态代理:需要自己手动生成类文件,并且被代理类跟代理类都需要实现同样的接口,当接口改变时代理类跟被代理类都需要修改。
动态代理:只需实现InvocationHandler,并重写invoke方法。不需要知道也用不着实现接口,当接口改变的时候也只需要改变invoke中的逻辑便可以。

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