从源码解析代理模式

news2024/10/7 4:35:24

大纲

代理模式(结构型设计模式)通过代理类去访问实现类中的方法,使用场景比如:已有接口和实现类的情况下,想要在已实现的方法基础上扩展更多的功能的场景。

代理模式里的主要类:

  1. 接口

  1. 实现类,需实现接口,用来完成真正的业务。(网上资料也叫做委托类、被代理类)

  1. 代理类,也需要实现接口,调用实现类的方法,本身不处理业务。

使用代理模式的好处:可以扩展实现类的功能,在实现类基本功能基础上增加一些额外的操作。

静态代理模式

图示:

要想做到在原来实现方法的基础上多增加功能,代理类同样也需要实现接口,并且需要持有一个实现类,在自己实现的方法中扩展功能,并且调用实现类的方法去处理真正的业务:

public class TV {
    String name;
    String model;

    public String getModel() {
        return model;
    }

    public String getName() {
        return name;
    }

    public void setModel(String model) {
        this.model = model;
    }

    public void setName(String name) {
        this.name = name;
    }

    public TV(String name,String model){
        this.name = name;
        this.model = model;
    }

    @Override
    public String toString() {
        return "TV{" +
            "name='" + name + '\'' +
            ", model='" + model + '\'' +
            '}';
    }
}

实体类TV.java

public interface ICompany {
    TV produceTV();
}

接口ICompany.java

实现类需要实现ICompany接口:

/**
* 实现类
*/
public class CompanyFactory implements ICompany{
    @Override
    public TV produceTV() {
        System.out.println("生产商生产一台电视");
        return new TV("TCL","40寸");
    }
}

代理类也需要实现ICompany接口:

/**
* 代理类
* 代理类和实现类都要实现接口
*/
public class TVProxy implements ICompany {
    ICompany company;

    @Override
    public TV produceTV() {
        System.out.println("代理收到一笔订单");
        if (Objects.isNull(company)) {
            company = new CompanyFactory();
        }
        return company.produceTV();
    }
}

CompanyFactory.java

运行时构建代理类,调用代理类的方法同时也调用了实体类的实现方法:

public class MainClass {
    public static void main(String[] args) {
        ICompany company = new TVProxy();
        TV tv = company.produceTV();
        System.out.println(tv.toString());
    }
}

总结:

静态代理通过创建代理类去实现接口,在实现方法里添加额外功能,从而实现不改变原有实现方法下做到功能扩展。

动态代理模式

静态代理有什么缺点?(先别看下面内容思考一下)

静态代理的局限在于,如果接口新增了方法,则代理类必须实现该方法,而使用动态JDK代理,代理类不用实现接口。

实体类TV不变,接口增加维修方法,如果是静态代理,接口新增方法,则对应的实现类和代理类都要实现这个方法,而动态代理则在代码编写层面不用实现接口的所有方法:

图示:

接口新增repairTV方法:

public interface ICompany {
    TV produceTV();

    void repairTV(String name); //新增维修电视的方法
}

实现类需要实现这个方法:

public class CompanyFactory implements ICompany {
    @Override
    public TV produceTV() {
        System.out.println("生产商生产一台电视");
        return new TV("TCL", "40寸");
    }

    @Override
    public void repairTV(String name) {
        System.out.println(String.format("生产商维修%s电视",name));
    }
}

这里是动态代理类,可以看到,这里并没有实现ICompany接口了

public class TVDynamicProxy {
    Object factory;

    public TVDynamicProxy(Object factory) {
        this.factory = factory;
    }

    
    public <T> T getProxy(Class<T> clazz) {
        //newProxyInstance实际上是返回了一个实现了接口的代理类实例,这个实例是存在在内存中的,动态生成的:
        return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //回调
                        System.out.println("动态代理执行:接收到一笔订单");
                        Object tv = method.invoke(factory, args); //这里通过反射调用了factory里实现的方法
                        System.out.println("动态代理结束");
                        return tv;
                }
        });
    }

}

TVDynamicProxy.java

上面方法中newProxyInstance实际上是返回了一个实现了接口的代理类实例,这个实例是存在在内存中的,动态生成的

public class MainClassB {
    public static void main(String[] args) {
        ICompany company = new CompanyFactory();
        //将生成的代理类写到本地:
        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); //将内存中的$Proxy0.class写到本地
        ICompany company1 = new TVDynamicProxy(company).getProxy(ICompany.class);
        TV tv = company1.produceTV();
        company1.repairTV(tv.name);
    }
}

代码运行查看结果

运行后断点可以看到,这里实际上返回了$Proxy0

$Proxy0实现了接口,并实现了接口里定义的方法。

所以先前我们说,是在代码编写层面上不用去实现接口,实际是JDK帮我们自动实现了。

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

    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 TV produceTV() throws  {
        try {
            return (TV)super.h.invoke(this, m3, (Object[])null); //这里回调了InvocationHandler的invoke方法
        } 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 repairTV(String var1) throws  {
        try {
            super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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);
        }
    }
    ...
}

$Proxy0.class

这个$Proxy0生成到了我们的根目录下面

总结:

动态代理在运行后会在内存中创建一个实现了接口的代理类,通过调用代理类的实现方法,回调到InvocationHandler接口的invoke方法,通过反射拿到实现类的方法,并进行调用。

思考:

如果我新建一个工厂类,不实现ICompany接口,那是否可以代理成功?

public class FactoryB {
    public TV produceTVB() {
        System.out.println("B工厂生产电视");
        return new TV("华为电视机", "南京");
    }
}

上面问题可以使用Cglib代理解决。有兴趣的同学可以看给出的参考链接自行拓展。

参考资料:

https://segmentfault.com/a/1190000040407024

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/179482.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构 第三章 栈和队列(栈)

天空之外&#xff1a;点击收听 1 基本知识点 1、栈顶是指允许进行插入和删除操作的一端&#xff0c;另外一端称为栈底 2、进栈是指在栈顶位置插入元素(也叫入栈或者压栈)&#xff0c;出栈是指删除栈顶元素(也叫弹栈或者退栈) 3、栈溢出是指&#xff1a; 当栈满的时候&#x…

led和白炽灯哪个对眼睛好?分享光线舒适的LED护眼灯

最近对于白炽灯与LED灯哪个更护眼的话题受到很多人关注&#xff0c;经过综合考虑&#xff0c;LED灯更适合家庭使用的。 LED灯是电致发光的半导体芯片&#xff0c;抗震性能好&#xff0c;内置三基色荧光粉&#xff0c;让光线更加柔和&#xff0c;做到使用寿命长达10万小时&#…

通信原理简明教程 | 基本概念

文章目录1 通信及通信系统1.1 通信系统的基本组成模型1.2 通信系统的分类1.3 模拟通信和数字通信系统2 调制和解调2.1 调制解调的基本概念2.2 调制解调的分类2.3 调制解调的作用3 通信系统的质量指标3.1 模拟通信系统的质量指标3.2 数字通信系统的质量指标4 总结1 通信及通信系…

算法导论(二):渐进符号、递归及解法

渐近符号 基本的渐近符号&#xff1a; O 表示上界&#xff0c;即小于等于 ≤ Ω 表示下界&#xff0c;即大于等于 ≥ Θ 表示渐近等于 &#xff08;上一集也有使用这个符号&#xff09; 还有几个严格符号&#xff1a; o 表示小于 < ω 表示大于 > 渐近符号O 主要详细讲…

Latex中给图表添加中英文标题及生成相关目录

通常我们都是用\caption{这里是标题}的方式给图表添加对应的标题&#xff0c;如果我们需要同时给出两个标题呢&#xff1f;&#xff08;例如某些毕业论文中要求同时给出中英文标题&#xff09;如果我们还要生成对应的图表目录呢&#xff1f;这些问题都可以利用bicaption这个包来…

【论文翻译】A simple yet effective baseline for 3d human pose estimation

【论文】https://arxiv.org/abs/1705.03098v2 【pytorch】weigq/3d_pose_baseline_pytorch: A simple baseline for 3d human pose estimation in PyTorch. (github.com) 【tensorflow】https://github.com/una-dinosauria/3d-pose-baseline 摘要 随着深度卷积网络的成功&am…

手把手教你如何在项目中使用阿里字体图标IconFont

阿里图标官网地址&#xff1a;IconFont-阿里巴巴矢量图标库 一、注册账号 要使用阿里图标&#xff0c;首先你要在它的官网注册一个账号&#xff0c;注册的方式有多种&#xff08;手机号&#xff0c;Github&#xff0c;微博&#xff0c;阿里域账号&#xff09;&#xff0c;根据…

【CSDN的2022与2023】普普通通的三年,从懵懂、焦虑到坚定、奋进,破除焦虑努力成为更好的自己

大家好&#xff0c;我是黄小黄&#xff01;一名普通的软件工程在读学生。最近终于闲下来了一丢丢&#xff01;借着休息之余&#xff0c;来写一篇年度总结散散心~与其说是年度总结&#xff0c;不如说是给大学生活与莽莽撞撞的自己一个交代叭&#xff01; 这些都是小标题~碎碎念1…

行为型模式-观察者模式

1.概述 定义&#xff1a;又被称为发布-订阅&#xff08;Publish/Subscribe&#xff09;模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时&#xff0c;会通知所有的观察者对象&#xff0c;使他们能…

深度卷积对抗神经网络 基础 第四部分 可控制的GANs(Controllable GANs)

不同的生成模型定义 深度卷积对抗神经网络包含两种不同的生成模型&#xff0c; 条件生成模型 和非条件生成模型。非条件生成模型就像是一个彩票机或者赌博机&#xff0c;你输入一个任意数字的硬币数量&#xff0c;而输出则是随机的彩球。这样的系统&#xff0c;我们不能控制输…

第九层(3):STL之vector类

文章目录前情回顾vrctor类vrctor类的功能vector与普通数组的区别vector的迭代器vector类内的构造函数vector类内的赋值操作vector类内对容器和大小操作vector类内的插入操作vector类内的删除操作vector类内的单个访问vector类内的交换函数vector类内的预留空间下一座石碑&#…

goto语句——“C”

各位CSDN的uu你们好啊&#xff0c;好久不见&#xff0c;甚是想念。今天小雅兰要带大家学习的内容是一个小知识点——goto语句&#xff0c;好啦&#xff0c;就让我们进入goto语句的世界吧 C语言中提供了可以随意滥用的goto语句和标记跳转的标号。 从理论上 goto语句是没有必要…

Python爬虫教你爬取视频内容

前面介绍了基本的数据爬取&#xff0c;图片爬取的相关案例前面文章也有涉及&#xff0c;关于有些案例网站不能登录的问题&#xff0c;可以再找些别的网站&#xff0c;因为道理既然明白了&#xff0c;其实什么网站都一样&#xff0c;它有反爬机制&#xff0c;自然有应对它的办法…

安装mysql 5.7.24

官网 https://downloads.mysql.com/archives/community/ 安装 安装好后解压有如下内容 配置电脑环境变量 MYSQL_HOME mysql安装目录 PATH %MYSQL_HOME%\bin 配置mysql相关信息 &#xff08;1&#xff09;新建配置文件my.ini 配置的是字符集类信息与存储引擎相关信息 &…

(Java高级教程)第四章必备前端基础知识-第二节2:CSS属性

文章目录一&#xff1a;CSS属性一览表二&#xff1a;常用属性详解&#xff08;1&#xff09;字体属性&#xff08;2&#xff09;文本属性&#xff08;3&#xff09;背景属性一&#xff1a;CSS属性一览表 W3C&#xff1a;元素属性 A&#xff1a; align-content规定弹性容器内…

[Android开发练习1] 绘制国旗

前言 本题主要在于熟练使用线性布局&#xff0c;了解其布局特点学会横向与纵向排列控件&#xff0c;以及认识TextView控件&#xff0c;同时学会使用对控件赋予不同的权重值来布局&#xff0c;在布局中使用了权重的控件的宽度就要设置成0dp。另外&#xff0c;了解到如何应对xml代…

Linux常见命令 25 - RPM包安装、升级、卸载、查询、校验、提取

目录 1. 包名与包全名 2. RPM安装 3. RPM包升级 4. RPM包卸载 5. 查询是否安装RPM包 6. RPM包校验 7. RPM包中文件提取 1. 包名与包全名 包全名&#xff1a;操作的包是没有安装的软件包时&#xff0c;使用包全名&#xff0c;而且要注意路径包名&#xff1a;操作已经安…

【进击的算法】基础算法——怎么优雅地控制边界范围

学习范围 &#xff1a; ✔️数组 ✔️边界控制本文作者 &#xff1a; 蓝色学者i 边界控制的艺术前言一、为什么需要控制边界&#xff1f;二、怎么优雅地控制边界&#xff1f;三、令人抓狂的二分查找3.1 题目概述3.2解题思路3.3 解决方案方案一&#xff1a;边界都有效方案二&…

Python3 循环语句

本章节将为大家介绍 Python 循环语句的使用。 Python 中的循环语句有 for 和 while。 Python 循环语句的控制结构图如下所示&#xff1a; while 循环 Python 中 while 语句的一般形式&#xff1a; while 判断条件(condition)&#xff1a;执行语句(statements)…… 执行流程…

【数据结构之二叉树系列】万字深剖普通二叉树的遍历+分治算法思想

目录前言一、背景知识二、前序遍历三、中序遍历四、后序遍历五、求二叉树中结点的个数1. 遍历计数&#xff08;1&#xff09;前序遍历计数&#xff08;2&#xff09;中序遍历计数&#xff08;3&#xff09;后序遍历计数2.分治算法思想&#xff08;推荐&#xff09;敬请期待前言…