代理模式:静态代理和动态代理(JDK动态代理原理)

代理模式:静态代理和动态代理以及JDK动态代理原理

  • 为什么要使用代理模式?
  • 静态代理
    • 代码实现
    • 优缺点
  • 动态代理
    • JDK动态代理
      • JDK动态代理原理
      • JDK动态代理为什么需要被代理的对象实现接口?
      • 优缺点
    • CGLIB动态代理
      • 优缺点
  • 代理模式的应用


为什么要使用代理模式?

当一个类不想让外部直接访问或者需要外部进行增强时,就可以使用代理模式生成一个代理对象,通过代理对象来访问目标对象或者对目标对象的一些功能进行增强。比如有一个大明星,他只想唱歌或者跳舞,但是又不想管理一些琐事,比如安排唱歌跳舞的场地还有收钱的一些事,那么这个明星就可以找一个经纪公司,让这个公司来做这些琐事,如果有粉丝想要点歌,这个公司收到后会安排场地,然后让这个明星来唱歌,这个公司完成一些其他事。在以上的案例中,明星就是一个被代理类,而代理公司是一个代理类,明星的一些行为只能由公司来调用,而公司可以对明星的行为进行增强。而代理模式又分为静态代理和动态代理我们展开讲讲。


静态代理

静态代理就是我们手动进行编译,写一个代理类并对被代理类的方法进行增强,静态代理是在编译时就生成代理类。

代码实现

被代理类:

public class BigStar {
    private String name;

    public BigStar(String name) {
        this.name = name;
    }
    public String sing(){
        System.out.println(name+"开始唱歌");
        return "谢谢大家";
    }
    public void dance(){
        System.out.println(name+"开始跳舞");
    }
}

手动编译的代理类:

public class StarProxy {
    private BigStar bigStar;
    public StarProxy(BigStar bigStar){
        this.bigStar=bigStar;
    }
    public String sing(){
        System.out.println("安排唱歌座位场地,收钱10w");
        return bigStar.sing();
    }
    public void dance(){
        System.out.println("安排唱歌座位场地,收钱50w");
        bigStar.dance();
    }
}

通过代理类调用被代理类的方法:

public class Client {
    public static void main(String[] args) {
        BigStar bigStar=new BigStar("坤坤");
        StarProxy starProxy=new StarProxy(bigStar);
        System.out.println(starProxy.sing());
        starProxy.dance();
    }
}

运行结果:
在这里插入图片描述

优缺点

优点:实现简单
缺点:
1、当要被代理的方法和类多时,手动编译麻烦
2、耦合度高。当被代理类修改方法时需要手动修改代理类的方法

动态代理

动态代理即运行时动态的生成代理对象,和静态代理在编译时就确定代理对象不同,其灵活度更高,并且耦合度更低,动态代理又分为JDK动态代理和CGLIB动态代理。

JDK动态代理

JDK动态代理是基于反射实现的

定义接口:

public interface Star {
    String sing();
    void dance();
}

生成代理对象并且对方法进行增强:


public class ProxyFactory {
    public static Star getProxy(BigStar bigStar){
        Star starProxy = (Star) Proxy.newProxyInstance(ProxyFactory.class.getClassLoader(), new Class[]{Star.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if ("sing".equals(method.getName())||"dance".equals(method.getName())){
                    System.out.println("安排场地座位,收钱");
                }
                return method.invoke(bigStar,args);
            }
        });
        return starProxy;
    }
}

测试:

public class Client {
    public static void main(String[] args) {
        BigStar bigStar=new BigStar("坤坤");
        Star proxy = ProxyFactory.getProxy(bigStar);
        System.out.println(proxy.sing());
        System.out.println("------");
        proxy.dance();

    }
}

JDK动态代理原理

ProxyFactory是一个生成代理类的工厂,而不是代理类,通过调用方法在运行时动态生成代理类,我们来查看这个在运行过程中生成的代理类,我对其进行了简化如下图:


public final class $Proxy0 extends Proxy implements Star {
    private static Method m4;
    private static Method m3;

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

    public final void dance() throws  {
            super.h.invoke(this, m4, (Object[])null);
        }

    public final String sing() throws  {
            return (String)super.h.invoke(this, m3, (Object[])null);
    }

    static {
            m4 = Class.forName("proxy.Star").getMethod("dance");
            m3 = Class.forName("proxy.Star").getMethod("sing");
    }
}

我们在测试时是通过生成的代理类也就是上图的类来调用方法的,因此我们需要搞懂这个生成代理类。
我们发现代理类在进行初始化时调用了父类的构造器,我们来查看父类Proxy的构造器

   protected InvocationHandler h;
    private Proxy() {
    }
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

易见,父类将子类传入的InvocationHandler 赋给了h,我们也因此知道了这个h是什么,就是生成了代理类的初始化时传入的InvocationHandler 参数,那这个代理类是怎么创建的呢?

在这里插入图片描述

我们查看这个Proxy的创建代理方法
在这里插入图片描述
我们发现,在这个方法中,首先通过传入的类加载器和接口获得class类,然后通过class类获得构造器,然后使用构造器传入我们通过匿名内部类创建的InvocationHandler对象就可以创建出一个代理对象
我们通过代理对象调用方法时就是以下的步骤:
在这里插入图片描述

JDK动态代理为什么需要被代理的对象实现接口?

因为我们在运行时生成代理类需要继承父类Proxy完成初始化并且使用反射来增强方法,而java是单继承多实现,又为了实现代理类和接口的关联也就是获取类的类信息来得到方法信息,需要实现接口。

优缺点

优点:
1、将类的所有方法都集中到一个处理器中处理
2、在运行时动态生成代理对象,比较灵活
缺点:需要被代理类实现接口

CGLIB动态代理

CGLIB动态代理是基于CGLIB工具类实现的,可以在运行时继承代理类根据代理类的类信息方法等生成代理类,因此被代理类不能被final修饰

public class ProxyFactory implements MethodInterceptor {
    private BigStar bigStar;
    public ProxyFactory (BigStar bigStar){
        this.bigStar=bigStar;
    }
    public  BigStar getProxy(){
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(BigStar.class);
        enhancer.setCallback(this);
        return (BigStar) enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("收钱收钱收钱");
        return method.invoke(bigStar,objects);
    }
}
public class test {
    public static void main(String[] args) {
        BigStar bigStar=new BigStar("坤坤");
        ProxyFactory proxyFactory=new ProxyFactory(bigStar);
        BigStar proxy = proxyFactory.getProxy();
        System.out.println(proxy.sing());

    }
}

在这里插入图片描述

优缺点

优点:
1、和JDK动态代理一样,可以集中处理需要被扩展的方法
2、弥补了没有实现接口的类也可以实现动态代理
缺点:
被代理的类不能被final修饰

代理模式的应用

经给以上的实现,代理模式的作用以及很明显了,防止直接访问目标对象,通过生成代理对象使用反射增强被代理对象的方法。实际上我们已经接触了不少的代理对象的实现了,例如Spring Aop就是基于动态代理实现的,我们下节再来讲讲SpringAop

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【AI技术赋能有限元分析应用实践】pycharm终端与界面设置导入Abaqus2024自带python开发环境

目录 一、具体说明1. **如何在 Windows 环境中执行 Abaqus Python 脚本**2. **如何在 PyCharm 中配置并激活 Abaqus Python 环境**3. **创建 Windows 批处理脚本自动执行 Abaqus Python 脚本**总结二、方法1:通过下面输出获取安装路径导入pycharm方法2:终端脚本执行批处理脚本…

【消息序列】详解(6):深入探讨缓冲区管理与流量控制机制

目录 一、概述 1.1. 缓冲区管理的重要性 1.2. 实现方式 1.2.1. HCI_Read_Buffer_Size 命令 1.2.2. HCI_Number_Of_Completed_Packets 事件 1.2.3. HCI_Set_Controller_To_Host_Flow_Control 命令 1.2.4. HCI_Host_Buffer_Size 命令 1.2.5. HCI_Host_Number_Of_Complete…

虚拟局域网PPTP配置与验证(二)

虚拟局域网PPTP配置与验证(二) windows VPN客户端linux 客户端openwrt客户端性能验证虚拟局域网PPTP配置与验证(一)虚拟局域网PPTP配置与验证(二) : 本文介绍几种客户端连接PPTP服务端的方法,同时对linux/windows/openwrt 操作系统及x86、arm硬件平台下PPTP包转发性能进…

uniapp中使用uni-forms实现表单管理,验证表单

前言 uni-forms 是一个用于表单管理的组件。它提供了一种简化和统一的方式来处理表单数据,包括表单验证、字段绑定和提交逻辑等。使用 uni-forms可以方便地创建各种类型的表单,支持数据双向绑定,可以与其他组件及API进行良好的集成。开发者可…

Hive构建日搜索引擎日志数据分析系统

1.数据预处理 根据自己或者学校系统预制的数据 使用less sogou.txt可查看 wc -l sogou.txt 能够查看总行数 2.数据扩展部分 我的数据位置存放在 /data/bigfiles 点击q退出 将一个文件的内容传递到另一个目录文件下 原数据在 /data/bigfiles ->传递 到/data/workspac…

网络安全的学习方向和路线是怎么样的?

最近有同学问我,网络安全的学习路线是怎么样的? 废话不多说,先上一张图镇楼,看看网络安全有哪些方向,它们之间有什么关系和区别,各自需要学习哪些东西。 在这个圈子技术门类中,工作岗位主要有以…

深入浅出分布式缓存:原理与应用

文章目录 概述缓存分片算法1. Hash算法2. 一致性Hash算法3. 应用场景Redis集群方案1. Redis 集群方案原理2. Redis 集群方案的优势3. Java 代码示例:Redis 集群数据定位Redis 集群中的节点通信机制:Gossip 协议Redis 集群的节点通信:Gossip 协议Redis 集群的节点通信流程Red…

Mysql的加锁情况详解

最近在复习mysql的知识点,像索引、优化、主从复制这些很容易就激活了脑海里尘封的知识,但是在mysql锁的这一块真的是忘的一干二净,一点映像都没有,感觉也有点太难理解了,但是还是想把这块给啃下来,于是想通…

论文模型设置与实验数据:scBERT

Yang, F., Wang, W., Wang, F. et al. scBERT as a large-scale pretrained deep language model for cell type annotation of single-cell RNA-seq data. Nat Mach Intell 4, 852–866 (2022). https://doi.org/10.1038/s42256-022-00534-z 论文地址:scBERT as a…

TCP三次握手的过程是怎样的?

一开始,客户端和服务端都处于CLOSE状态。先是服务端主动监听某个端口,处于LISTEN状态。 (1)第一次握手 客户端会随机初始化序号(client_isn),将此序号填入TCP首部的32位序号字段中&#xff0c…

Java核心知识详解:String类、StringBuffer、数组及日期时间的全面解析

🚀 作者 :“码上有前” 🚀 文章简介 :Java 🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬 标题 Java核心知识详解:String类、StringBuffer、数组及日期时间的全面解析 摘要 在Java中…

【MATLAB源码-第218期】基于matlab的北方苍鹰优化算法(NGO)无人机三维路径规划,输出做短路径图和适应度曲线.

操作环境: MATLAB 2022a 1、算法描述 北方苍鹰优化算法(Northern Goshawk Optimization,简称NGO)是一种新兴的智能优化算法,灵感来源于北方苍鹰的捕猎行为。北方苍鹰是一种敏捷且高效的猛禽,广泛分布于北…

SplatFormer: Point Transformer for Robust3D Gaussian Splatting 论文解读

目录 一、概述 二、相关工作 1、NVI新视角插值 2、稀疏视角重建 3、OOD-NVS 4、无约束重建下的正则化技术 5、基于学习的2D-to-3D模型 6、3D点云处理技术 三、SplatFormer 1、Point Transformer V3 2、特征解码器 3、损失函数 四、数据集 五、实验 一、概述 该论…

Azkaban部署

首先我们需要现在相关的组件,在这里已经给大家准备好了相关的安装包,有需要的可以自行下载。 只需要启动hadoop集群就可以,如果现在你的hive是打开的,那么请你关闭!!! 如果不关会造成证书冲突…

目标检测模型优化与部署

目录 引言数据增强 随机裁剪随机翻转颜色抖动 模型微调 加载预训练模型修改分类器训练模型 损失函数 分类损失回归损失 优化器算法思路 RPN (Region Proposal Network)Fast R-CNN损失函数 部署与应用 使用 Flask 部署使用 Docker 容器化 参考资料 引言 目标检测是计算机视觉…

Charles抓包工具-笔记

摘要 概念: Charles是一款基于 HTTP 协议的代理服务器,通过成为电脑或者浏览器的代理,然后截取请求和请求结果来达到分析抓包的目的。 功能: Charles 是一个功能全面的抓包工具,适用于各种网络调试和优化场景。 它…

java: itext8.05 create pdf

只能调用windows 已安装的字体,这样可以在系统中先预装字体,5.0 可以调用自配文件夹的字体文件。CSharp donetItext8.0 可以调用。 /*** encoding: utf-8* 版权所有 2024 ©涂聚文有限公司 言語成了邀功盡責的功臣,還需要行爲每日來值班…

Kafka 生产者优化与数据处理经验

Kafka:分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析:从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析&#xff1a…

C高级学习笔记

……接上文 硬链接和软连接(符号链接) 硬链接 硬链接文件可以理解为文件的副本(可以理解为复制粘贴) ln 根据Linux系统分配给文件的inode(ls -li)号进行建立,没有办法跨越文件系统 格式:ln 被链接的文件&am…

Java基于SpringBoot+Vue的藏区特产销售平台

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…