23种设计模式之代理模式


在这里插入图片描述

一、代理模式简介

1.1 概念


  代理模式(Proxy Pattern)属于结构型设计模式,其核心思想是为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介的作用,并可以在不改变客户端代码的情况下增强或控制对象的访问。

1.2 模式类型


结构性

1.3 代理模式的类别


代理模式主要分为三种类型:

  • 静态代理 :由程序员创建或第三方工具生成代理类的源代码,再进行编译。 其代理类和委托类在编译期间就确定下来。
  • 动态代理 : 代理类在程序运行时通过反射等机制动态生成,无需程序员显式地创建代理类。
    • JDK 动态代理
    • CGLib 动态代理
  • 虚拟代理 : 当有一个需要创建开销较大的对象时,通过虚拟代理来存放实例化需要较长时间的真实对象。

1.4 优点


  • 控制访问:代理模式可以控制对对象的访问,可以在访问对象之前或之后执行一些额外的操作。例如:日志记录、功能增强、权限验证、性能监控等。
  • 保护对象:代理模式可以对真实对象进行保护,只允许特定的客户端访问真实对象,从而提供了一定的安全性。
  • 延迟加载:代理模式可以延迟加载真实对象,当需要真实对象时才进行创建和初始化,从而提高了系统的性能和资源利用率。
  • 简化客户端:代理模式隐藏了真实对象的复杂性,客户端可以通过代理对象来完成操作,无需直接与真实对象交互。

1.5 缺点


  1. 增加复杂性:因为引入了代理对象,使得对对象的管理和维护更加复杂。
  2. 性能损耗:由于代理模式在访问对象时引入了额外的间接层,可能会导致性能上的损耗。每次通过代理访问对象都需要经过额外的处理步骤,可能会对系统的响应时间产生影响。

二、代理模式的模式动机


  代理模式的模式动机是为了提供一个中间层(代理对象)来控制对另一个对象的访问,以满足一些特定的需求。

三、模式结构


代理模式主要包括以下角色:

  • 抽象主题角色(Subject):通过接口或抽象类声明真实主题和代理的公共方法。
  • 真实主题角色(Proxy):实现抽象主题角色,定义代理所代表的真实对象。
  • 代理主题角色(RealSubject):包含对真实主题的引用,从而可以操作真实主题对象;同时可以提供额外的功能来扩展真实主题的行为。

在这里插入图片描述

四、代理模式的实现

在这里插入图片描述

4.1 静态代理


  1. 定义抽象接口:视频播放器接口Player
    public interface Player {
        void loadVideo(String filename);
        void playVideo(String filename);
    }
    
  2. 定义真实对象:RealPlayer
    public class RealPlayer implements Player {
    
        @Override
        public void loadVideo(String filename) {
            System.out.println("加载MP4视频文件: " + filename);
        }
    
        @Override
        public void playVideo(String filename) {
            System.out.println("播放MP4视频文件: " + filename);
        }
    
    }
    
  3. 定义代理对象:PlayerProxy
    public class PlayerProxy implements Player{
    
        private RealPlayer realPlayer;
    
        public PlayerProxy(RealPlayer realPlayer){
            this.realPlayer = realPlayer;
        }
    
        @Override
        public void loadVideo(String filename) {
            realPlayer.loadVideo(filename);
        }
    
        @Override
        public void playVideo(String filename) {
            realPlayer.playVideo(filename);
        }
    
    }
    
  4. 客户端
    public class Client {
    
        public static void main(String[] args) {
            // 直接调用
            RealPlayer realPlayer = new RealPlayer();
            realPlayer.playVideo("《西游记》.mp4");
            System.out.println("===========================");
    
            // 代理方式调用,代理真实对象
            PlayerProxy playerProxy = new PlayerProxy(realPlayer);
            playerProxy.loadVideo("《三国演义》.mp4");
            playerProxy.playVideo("《三国演义》.mp4");
    
        }
    
    }
    
  5. 结果
    在这里插入图片描述

4.2 JDK 动态代理🚀🚀🚀


  JDK 动态代理可以在运行时动态生成一个代理对象,是 Java 标准库中提供的一种代理方法,代理对象实现和原始类一样的接口,并将方法调用转发给被代理对象,同时还可以在方法调用前后执行额外的增强处理。JDK 动态代理通过反射机制实现代理功能。

4.2.1 原理:


  1. 创建实现 InvocationHandler 接口的代理类工厂:在调用 Proxy 类的静态方法 newProxyInstance 时,会动态生成一个代理类。该代理类实现了目标接口,并持有一个 InvocationHandler 类型的引用。
  2. InvocationHandler 接口:InvocationHandler 是一个接口,并且只有一个方法 invoke。在代理对象的方法被调用时,JVM 会自动调用代理类的 invoke 方法,并将调用的方法名、参数等信息传递给该方法。
  3. 调用代理对象的方法:当代理对象的方法被调用时,JVM 会自动调用代理类的 invoke 方法。可以根据需要执行各种逻辑,比如添加日志、性能统计、事物管理等;
  4. invoke 方法调用: 在 invoke 方法中,通过反射机制调用目标对象的方法,并返回方法的返回值。在调用目标对象的方法前后,可以执行额外的逻辑。

4.2.2 实现:


  1. 创建代理工厂类

    public class JDKProxyFactory implements InvocationHandler {
        // 需要被代理的对象
        private Object object;
    
        public JDKProxyFactory(Object object) {
            this.object = object;
        }
    
        @SuppressWarnings("unchecked")
        public <T> T getProxy() {
            return (T) Proxy.newProxyInstance(
                    // 当前线程的上下文类加载器
                    Thread.currentThread().getContextClassLoader(),
                    // 被代理对象的接口
                    object.getClass().getInterfaces(),
                    // 处理器自身
                    this
            );
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = null;
            // 进行方法匹配,调用对应方法名的方法
            if ("loadVideo".equals(method.getName())) {
                result = method.invoke(object, args);
            }
    
            if ("playVideo".equals(method.getName())) {
                System.out.println("前置增强");
                result = method.invoke(object, args);
                System.out.println("后置增强");
            }
            return result;
        }
        
    }
    
  2. 客户端

    public class Client {
        public static void main(String[] args) {
            RealPlayer realPlayer = new RealPlayer();
            // 通过代理工厂类获取代理对象,代理 realPlayer
            Player proxy = new JDKProxyFactory(realPlayer).getProxy();
            proxy.loadVideo("《红楼梦》.mp4");
            proxy.playVideo("《红楼梦》.mp4");
        }
    }
    
  3. 结果
    在这里插入图片描述

4.3 CGLib 动态代理


  CGLib (Code Generation Library)是一个基于 ASM(Java 字节码操作框架)实现的代理生成库,它可以在运行时动态生成目标类的子类作为代理类,并覆盖其中的方法来实现代理功能。与 Java 自带的JDK 动态代理不同,CGlib 动态代理可以代理没有实现接口的类。

4.3.1 原理:


  1. 创建 Enhancer 对象:Enhancer 是 CGLib 库中用于动态生成子类的主要类。通过创建 Enhancer 对象并设置需要代理的目标类、拦截器等参数,可以生成一个代理类。
  2. 设置回调拦截器:在生成代理类时,需要指定拦截器。拦截器是实现代理逻辑的关键,它会在代理类的方法被调用时拦截调用,并执行相应的逻辑。在 CGLib 中,拦截器需要实现 MethodInterceptor 接口。
  3. 创建代理对象:通过调用 Enhancer 对象的 create 方法,可以生成一个代理对象。代理对象会继承目标类的方法,并且在调用地阿里对象的方法时会先调用拦截器的 interceptor 方法,再执行目标方法。
  4. 调用代理对象:通过调用代理对象的方法,会触发拦截器的 interceptor 方法。在 interceptor 方法中,可以根据需要执行各种逻辑,比如添加日志、性能统计、事物管理等。

4.3.2 步骤


  1. 定义一个真实对象 RealPlayer,注意这块的 RealPlayer 没有实现接口

    public class RealPlayer  {
    
        public void loadVideo(String filename) {
            System.out.println("加载MP4视频文件: " + filename);
        }
    
        public void playVideo(String filename) {
            System.out.println("播放MP4视频文件: " + filename);
        }
    
    }
    
  2. 创建CGLib代理工厂

    public class CglibProxyFactory implements MethodInterceptor {
    
        public <T> T getProxy(Class<T> clazz) {
            Enhancer en = new Enhancer();
            // 设置代理的父类
            en.setSuperclass(clazz);
            // 设置方法回调
            en.setCallback(this);
            // 创建代理实例
            return (T) en.create();
        }
    
        /**
         * @param obj    目标对象
         * @param method 目标对象的方法
         * @param args   目标对象的参数
         * @param proxy  方法代理
         * @return 结果
         * @throws Throwable
         */
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            Object result = null;
    
            if ("loadVideo".equals(method.getName())) {
                // 通过继承的方法实现代理,因此这里调用 invokeSuper
                result = proxy.invokeSuper(obj, args);
            }
            if ("playVideo".equals(method.getName())) {
                result = proxy.invokeSuper(obj, args);
            }
            return result;
        }
        
    }
    
  3. 客户端

    public class Client {
        public static void main(String[] args) {
            RealPlayer realPlayer = new RealPlayer();
            RealPlayer proxy = new CglibProxyFactory().getProxy(realPlayer.getClass());
            // 验证代理类的父类
            System.out.println("代理类的父类 : " + proxy.getClass().getSuperclass().getSimpleName());
            System.out.println();
    
            proxy.loadVideo("自由飞翔.mp4");
            proxy.playVideo("自由飞翔.mp4");
        }
    }
    
  4. 结果
    在这里插入图片描述

五、代理模式的应用场景

5.1 适用场景


  1. 远程代理(Remote Proxy):为位于不同地址空间的对象提供本地代表,使得客户端可以透明地调用远程对象的方法,就像调用本地对象一样。这常用于分布式系统中,减少网络通信的复杂度。
  2. 虚拟代理(Virtual Proxy):当创建一个对象的开销很大或者需要很长时间时,可以使用虚拟代理来延迟对象的创建,直到真正需要它的时候。例如,上面的代码片段中,如果 RealPlayer 加载视频非常耗时,可以使用代理在真正播放前才加载视频。
  3. 保护代理(Protective Proxy):控制对敏感对象的访问,例如,基于用户权限限制对某些资源的访问。代理可以添加额外的安全检查逻辑。
  4. 缓存代理(Caching Proxy):为结果被频繁请求的对象提供缓存,以减少重复的计算或访问开销。例如,如果视频播放器经常重复播放同一视频,代理可以缓存视频数据,避免重复加载。
  5. 日志代理(Logging Proxy):在方法调用前后自动记录日志,方便调试和监控。例如,在调用loadVideo和playVideo方法前后打印日志信息。
  6. 懒加载代理(Lazy Loading Proxy):推迟对象的初始化直到真正需要使用它的时候,这可以提升程序启动速度和节省资源。
  7. 增强功能代理(Enhancement Proxy):在不修改原有对象的基础上,为对象添加额外的功能,比如事务管理、异常处理等,如代码中的 CglibProxyFactory 可能为 RealPlayer 添加了额外的逻辑。

5.2 应用实例🚀🚀🚀


  1. SpringAOP
  2. RPC:RPC远程代理)框架也可以看作一种代理模式,通过远程代理,将网络通信、数据编码解码等细节隐藏起来。客户端在使用 RPC 服务的时候,就像使用本地函数一样,无需了解根服务器交互的细节。除此之外,RPC服务的开发者也只需要开发业务逻辑,就像开发本地使用的函数一样,不需要关注跟客户端的交互细节。
  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JAVA开发区

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值