设计模式 - 结构型模式考点篇:代理模式(静态代理、JDK 动态代理、CGLIB 动态代理)

news2024/10/5 14:19:57

目录

一、代理模式

一句话概括

1.1、代理模式概述

1.2、静态代理

1.3、JDK 动态代理

1.4、CGLIB 动态代理

1.5、对比三种代理

1.5.1、jdk 代理 VS CGLIB 代理

1.5.2、动态代理 VS 静态代理

1.6、优缺点

1.7、使用场景


一、代理模式


一句话概括

教你将类和对象结合再一起形成一个更强大的结构.

1.1、代理模式概述

由于一些原因,我们不能直接访问目标对象,这时候需要提供一个目标对象的代理,通过代理来实现对目标对象的访问.

比如,我要去买火车票,就需要在火车触发的前几天坐车去火车站买票,相当的麻烦.  但是还有一种方式,就是我们可以直接在 12306 网站上直接进行买票,十分方便,这里的  12306 网站就相当于 代理.

代理模式分为以下三种角色:

  • 抽象主题类:通过接口或者抽象类声明真实主题要干的事情(业务).
  • 真实主题类:实现了抽象主题中的具体业务.
  • 代理类:内部包含 真实主题类 的实例对象,可以访问和扩展真实主题的功能.

1.2、静态代理

静态代理表示在编译期间就创建好了代理对象.

这里还是使用刚刚提到的火车站买票的案例来实现.

/**
 * 抽象主题:卖票接口
 */
public interface SellTickets {

    void sell();

}
/**
 * 真实主题类:火车站
 */
public class TrainStation implements SellTickets{

    @Override
    public void sell() {
        System.out.println("火车站卖票");
    }

}
/**
 * 代理类:代售点(例如 12306 网站)
 */
public class ProxyPoint {

    //在编译时期对象就创建好了,因此是静态代理
    private TrainStation trainStation = new TrainStation();

    public void sell() {
        System.out.println("方便了你,我自然要收取代理费用");
        trainStation.sell();
    }

}
/**
 * 测试类
 */
public class Client {

    public static void main(String[] args) {
        //直接就可以通过代理类买到票,不用再去火车站了
        ProxyPoint proxyPoint = new ProxyPoint();
        proxyPoint.sell();
    }

}

这里就可以看出测试类是通过 ProxyPoint 类访问到的目标方法,同时也对 sell 方法进行了增强(例如:收取代理费用).

Ps:JDK 动态代理要求必须要定义接口,因为他就是对接口进行代理的.

1.3、JDK 动态代理

动态代理就是在程序运行期间,动态的在内存中创建出代理类(静态代理是在编译期间创建出代理类).

Java 中提供类一个动态代理类 Proxy(并不是 静态代理 中所说的代理对象类),提供了创建代理对象的静态方法(Proxy.newProxyInstance 方法)来获取代理对象.

这里继续使用买火车票案例

/**
 * 抽象主题:卖票接口
 */
public interface SellTickets {

    void sell();

}
/**
 * 真实主题类:火车站
 */
public class TrainStation implements SellTickets {

    @Override
    public void sell() {
        System.out.println("火车站卖票");
    }

}
/**
 * 代理类工厂(不是代理类):用来创建代理对象
 */
public class ProxyFactory {

    private TrainStation station = new TrainStation();

    public SellTickets getProxyObject() {
        //使用 Proxy 获取代理对象
        //newProxyInstance 参数如下:
        //ClassLoader loader: 类加载器,此处填写真实对象的类加载器,是用来加载代理类的.
        //Class<?>[] interfaces: 填写真实对象实现的接口
        //InvocationHandler h: 代理对象要处理的业务逻辑
       SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(
               station.getClass().getClassLoader(),
               station.getClass().getInterfaces(),
               new InvocationHandler() {
                   @Override
                   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                       //proxy: 代理对象,此方法中一般不用
                       //method: 对应于在代理对象上调用的接口方法的 Method 实例
                       //args: 代理对象调用接口方法时传递的实际参数

                       System.out.println("收取代理费用(JDK 动态代理)");
                       //执行真实对象
                       Object result = method.invoke(station, args);
                       return result;
                   }
               }
       );
       return sellTickets;
    }

}
    public static void main(String[] args) {
        ProxyFactory factory = new ProxyFactory();
        //获取代理对象
        SellTickets proxyObject = factory.getProxyObject();
        proxyObject.sell();
    }

执行流程如下:

  1. 在测试类中通过代理对象调用 sell 方法.
  2. 此处执行的是代理类中的 sell 方法,因为此方法的对象来自于 JDK 的 Proxy.newProxyInstance 方法生成的.
  3. 代理类中的 sell 方法有调用了 InvocationHandler 接口的子实现类对象的 invoke 方法.
  4. invoke 方法底层(这里的源码可以通过阿里巴巴开源的诊断工具 Arthas 看到代理类的结构)通过反射执行了真实主题类中的 sell 方法.

1.4、CGLIB 动态代理

同样是买火车票案例,如果没有定义  SellTickets 这个 抽象主题类,只定义了 TrainStation 火车列类,很显然 JDK 代理就无法使用了,因为 JDK 动态代理要求必须定义接口,对接口进行代理.

CGLIB 是一个功能强大,高性能的代码生成包. 他为没有实现接口的类提供了代理,给 JDK 动态代理提供了很好的补充.

CGLIB 是第三方提供的包,因此需要引入 jar 包.

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>

具体实现如下:

/**
 * 火车站
 */
public class TrainStation {

    public void sell() {
        System.out.println("火车站卖票");
    }

}
public class ProxyFactory implements MethodInterceptor {

    private TrainStation station = new TrainStation();

    public TrainStation getProxyObject() {
        //1.创建 Enhancer 对象,类似于 JDK 动态代理 Proxy 类
        Enhancer enhancer = new Enhancer();
        //2.设置父类(代理类的父类就是被代理类)的字节码对象
        enhancer.setSuperclass(station.getClass());
        //3.设置回调函数
        enhancer.setCallback(this);
        //4.创建代理对象
        TrainStation obj = (TrainStation) enhancer.create();
        return obj;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //这里的参数就和 JDK 动态代理的 invoke 很像了
        //o: 代理对象
        //method: 真实主题中的方法的 Method 实例
        //objects: 实际参数
        //methodProxy: 代理对象中的 method 实例

        System.out.println("收取代理费用(CGLIB 动态代理)");
        Object result = method.invoke(station, objects);
        return result;
    }

}
public class Client {

    public static void main(String[] args) {
        //创建代理类工厂
        ProxyFactory factory = new ProxyFactory();
        //获取代理对象
        TrainStation proxyObject = factory.getProxyObject();
        //调用代理对象中的 sell 方法
        proxyObject.sell();
    }

}

1.5、对比三种代理

1.5.1、jdk 代理 VS CGLIB 代理

  1. CGLIB 底层使用 ASM 字节码生成框架(这个不用去了解),使用字节码技术生成代理类,在 JDK 1.7 之前要比 Java 反射效率要高.  但是 CGLIB 不能对声明为 final 的类和方法进行处理,因为 CGLIB 的原理就是动态生成被代理类(真实主题类)的子类.
  2. 在 JDK 1.8 之后,JDK 的代理效率要高于 CGLIB.  因此只要是有接口的,就是用 JDK 动态代理,没有接口的使用 CGLIB 代理.

1.5.2、动态代理 VS 静态代理

  1. 动态代理最大的好处就是 抽象主题类(接口) 中声明的所有方法都被转移到了一个地方集中处理(InvocationHandler.invoke).  这样,当接口的方法数目比较多的时候,就不需要像静态代理那样每一个方法都需要在代理类中再中转一次.
  2. 将来如果接口增加一个方法,静态代理除了所有实现类(真实主题类)需要实现这个方法外,还需要在代理类中实现此方法. 提高了代码的维护成本. 静态代理就不会出现此问题.

1.6、优缺点

优点

中介,保护:代理模式在客户端和目标对象之间起到一个中介的作用和保护目标对象的作用.

扩展:代理对象可以扩展目标对象的功能.

解耦合:代理模式对客户端和目标对象进行解耦.

缺点:

实现起来相对复杂.

1.7、使用场景

  1. 远程代理:在远程调用时,为了良好的代码设计和可维护性,可以将网络通信部分隐藏起来,只暴露非本地服务器一个接口(代理),通过这接口即可访问到远程服务提供的功能,不必关心过多的通信实现细节.
  2. 防火墙代理:当浏览器配置成代理功能时,防火墙就会将浏览器的请求转给网络,当网络返回相应时吗,代理服务器再把他转给你的浏览器.
  3. 权限代理:控制一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限.

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

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

相关文章

Mac mov转mp4,详细转换步骤

Mac mov转mp4怎么转&#xff1f;视频文件格式为.mov是由Apple公司所开发的特殊格式。因其只能在苹果设备上播放&#xff0c;与他人分享时就会变得困难。为此&#xff0c;我们通常会选择使用MP4这种最受欢迎的视频格式。在日常使用中&#xff0c;MP4成为了大家首选的视频格式。而…

【Docker】 docker中apt-get update过慢,这样配置瞬间提速!

docker中apt-get update过慢&#xff0c;这样配置瞬间提速&#xff01; 源官网全球镜像站 源 今天办公地点的网络出奇的差&#xff0c;看电影看小说打游戏完全没影响&#xff0c;只要更新就蜗速前进&#xff0c;只能从网上翻下&#xff0c;看看有没有网速快的下载源。 碰巧看到…

模范思路,单基因泛癌+实验轻松拿捏sci8+。

今天给同学们分享一篇单基因泛癌单细胞实验的生信文章“Pan-cancer analysis of Krppel-like factor 3 and its carcinogenesis in pancreatic cancer”&#xff0c;这篇文章于2023年8月3日发表在Front Immunol期刊上&#xff0c;影响因子为8.786。 Krppel-like factor 3&#…

vue.js处理数组对象中某个字段是否变为两个字段

一、场景&#xff1a; 产品要求做一个时间步骤条&#xff0c;使用目前后端已返回的数据进行操作实现。时间步骤条要求日期和时间分开显示且相同日期只显示第一个日期。 图左边为实现效果&#xff0c;右边为后台返回的接口。接口中current字段表示当前到达第几步&#xff0c;从…

2023年软件测试工具总结 —— 接口测试工具

接口测试的全称是应用程序编程接口&#xff08;API&#xff09;测试&#xff0c;从原理上来说&#xff0c;接口测试是模拟客户端向服务器端发送请求&#xff0c;然后检查能否获得正确的返回信息。接口测试用于测试RESTful API、SOAP Web服务&#xff0c;这些服务可以通过HTTP、…

Java基础知识总结(2023版)

目录 一、Java基础1、Java 基础2、Java 进阶3、Java8新特性4、Java集合5、Java高并发6、Java代码实例 二、MySQL数据库三、Spring Boot框架&#xff08;35天&#xff09;四、Redis中间件五、MongoDB数据库六、Netty网络编程七、23种设计模式八、微服务九、JavaScript零基础入门…

Aurora中的策略模式和模板模式

Aurora中的策略模式和模板模式 在aurora中为了方便以后的扩展使用了策略模式和模板模式实现图片上传和搜索功能&#xff0c;能够在配置类中设置使用Oss或者minio上传图片&#xff0c;es或者mysql文章搜索。后续有新的上传方式或者搜索方式只需要编写对应的实现类即可&#xff…

批量对比文件夹下文件

软件 产品 | Beyond Compare 中文官方网站 软解破解 Beyond Compare 4密钥过期&#xff0c;解决办法&#xff0c;超实用 批量文件对比 最后显示红色的文件&#xff0c;即为文件两者内容不同的

面了10家却收不到1个offer,自我介绍你踩雷了吗?

每一次离职&#xff0c;都是为了重新开始&#xff1b;每一次寻找新的工作&#xff0c;都面临着巨大的竞争。找工作就像找对象&#xff0c;除了需要一定的缘分外&#xff0c;也需要掌握一定的技巧。 有的人爱恋TA的人排到长城&#xff0c;有的人却一直母胎单身&#xff1b;有的人…

Spring源码解析(十一):spring事务配置类源码

Spring源码系列文章 Spring源码解析(一)&#xff1a;环境搭建 Spring源码解析(二)&#xff1a;bean容器的创建、默认后置处理器、扫描包路径bean Spring源码解析(三)&#xff1a;bean容器的刷新 Spring源码解析(四)&#xff1a;单例bean的创建流程 Spring源码解析(五)&…

vue3使用echarts实现地图撒点、飞线等功能

echarts地图配置参考链接 链接2 vue3使用echarts map.vue <template><div class"echart-demo" id"demo"></div> </template><script setup lang"ts"> //引入echart和json数据 import * as echarts from echarts…

ffmpeg ts 关于av_seek_frame

1 ffmpeg命令行 一般对视频文件的裁剪 我们通过一行 ffmpeg命令行即可实现&#xff0c;比如 ffmpeg -ss 0.5 - t 3 - i a.mp4 vcodec copy b.mp4 其中 -ss 放置较前 开启精准seek定位 对于mp4而言 seek将从moov中相关索引表查找 0.5s时刻附近最近的关键帧 &#xff08;此描述…

systemd服务日志重定向到文件

方式一&#xff08;centos7&#xff09; [Service] ExecStart"/usr/bin/sh test.sh >> info.log 2>&1"方式二&#xff08;centos8&#xff09; StandardOutput 和 StandardError&#xff0c;用于重定向标准输出和标准错误输出 [Service] StandardOut…

k8s containerd查看镜像

直接查看crictl image会报错&#xff1a; 1) crictl config runtime-endpoint unix:///run/containerd/containerd.sock 2) vi /etc/crictl.yaml 3) systemctl daemon-reload 此时&#xff0c;再查看image:

Kettle连接数据库[MySQL]报错

在连接数据库页面填写完成后点击“测试” 报错信息&#xff1a; 错误连接数据库 [ETLqiangzi] : org.pentaho.di.core.exception.KettleDatabaseException: Error occurred while trying to connect to the databaseDriver class org.gjt.mm.mysql.Driver could not be found…

nssm nginx window 部署和开机启动服务

部署 去到Nginx官网&#xff1a;nginx news &#xff0c;然后点击“download” 在nginx的配置文件是conf目录下的nginx.conf nginx.exe http://localhost 在cmd命令窗口里面输入nginx命令(快速停止nginx) &#xff1a; nginx -s stop 或者使用(完整有序的停止nginx)命…

elasticsearch深度分页问题

一、深度分页方式from size es 默认采用的分页方式是 from size 的形式&#xff0c;在深度分页的情况下&#xff0c;这种使用方式效率是非常低的&#xff0c;比如我们执行如下查询 1 GET /student/student/_search 2 { 3 "query":{ 4 "match_all":…

航拍飞行器经营商城小程序的作用是什么

航拍人群越来越越多&#xff0c;一款靠谱的装备往往能达到预期效果&#xff0c;随着互联网信息传播度加深&#xff0c;也吸引了大批同样的爱好者加入航拍序列。 对航拍飞行器企业/经营商来说&#xff0c;市场增幅下也带来了不少商机&#xff0c;然在实际销售及客户赋能方面还是…

必备的常见芯片封装

-网友&#xff1a;这什么破封装&#xff0c;这么难焊&#xff01; -工程师&#xff1a;你才焊过几种芯片封装呀&#xff0c;SOT封装都觉得难&#xff1f; 我们常见的芯片封装&#xff1a; 第一种&#xff0c;DIP封装&#xff0c;DIP即双列直插式封装&#xff0c;引脚从芯片两…

vue-2

一、文章内容概括 1.指令补充 指令修饰符v-bind对样式增强的操作v-model应用于其他表单元素 2.computed计算属性 基础语法计算属性vs方法计算属性的完整写法成绩案例 3.watch侦听器 基础写法完整写法 4.综合案例 &#xff08;演示&#xff09; 渲染 / 删除 / 修改数量 …