深入浅出设计模式 - 代理模式

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 深入浅出设计模式 - 代理模式

博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家✌

Java知识图谱点击链接:体系化学习Java(Java面试专题)

💕💕 感兴趣的同学可以收藏关注下不然下次找不到哟💕💕

1687840829117.jpg

1、什么是代理模式

代理模式是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理对象充当了客户端和目标对象之间的中介,客户端通过代理对象访问目标对象,从而实现了对目标对象的控制。代理模式主要解决了直接访问某些对象时可能存在的问题,例如对象的创建和销毁成本过高、对象的访问需要额外的控制等。代理模式分为静态代理和动态代理两种方式,其中静态代理需要在编译期间就确定代理对象,而动态代理则在运行时动态生成代理对象。代理模式在实际应用中广泛使用,例如远程代理、虚拟代理、安全代理、缓存代理等。

2、代理模式有什么优缺点

代理模式的优点:

  1. 代理模式能够实现客户端和目标对象之间的解耦,客户端不需要知道目标对象的具体实现细节,只需要通过代理对象来访问目标对象即可。

  2. 代理模式可以提高系统的安全性,代理对象可以在访问目标对象之前进行权限检查等操作,从而保证系统的安全性。

  3. 代理模式可以提高系统的性能,例如使用缓存代理可以避免频繁访问目标对象,从而提高系统的性能。

  4. 代理模式可以实现懒加载,即在需要访问目标对象时才创建目标对象,从而避免了不必要的资源浪费。

代理模式的缺点:

  1. 代理模式会增加系统的复杂度,引入了额外的代理类,增加了系统的结构复杂度。
  1. 代理模式可能会降低系统的性能,特别是在使用远程代理时,由于网络通信的开销,可能会导致系统的性能下降。

    1. 代理模式可能会导致代码的维护难度增加,特别是在使用动态代理时,由于代码的生成是在运行时进行的,可能会导致程序的调试和维护难度增加。

      3、代理模式的应用场景

代理模式的应用场景有很多,以下是一些常见的应用场景:

  1. 远程代理:当客户端需要访问远程对象时,可以使用远程代理来实现,远程代理负责与远程对象进行通信,并将结果返回给客户端。
  1. 虚拟代理:当创建一个对象的代价很大时,可以使用虚拟代理来延迟对象的创建,只有在真正需要使用对象时才创建对象。

  2. 安全代理:当需要控制对对象的访问时,可以使用安全代理来限制对对象的访问,例如只允许特定的用户访问对象。

  3. 缓存代理:当需要缓存对象的结果时,可以使用缓存代理来缓存对象的结果,从而避免重复计算。

  4. 日志代理:当需要记录对象的访问日志时,可以使用日志代理来记录对象的访问日志,例如记录对象的访问时间、访问次数等信息。

  5. 延迟加载代理:当需要延迟加载对象时,可以使用延迟加载代理来实现,延迟加载代理负责在需要访问对象时加载对象,并将结果返回给客户端。

  6. 权限代理:当需要对对象进行权限控制时,可以使用权限代理来限制对对象的访问,例如只允许特定的用户或角色访问对象。

代理模式适用于需要在访问对象时增加一些额外的处理逻辑的场景,例如控制访问、缓存结果、记录日志、延迟加载等。

4、代理模式的结构

代理模式的结构包含以下几个角色:

  1. 抽象主题(Subject):定义了真实主题和代理主题的公共接口,客户端通过该接口访问真实主题和代理主题。

  2. 真实主题(Real Subject):定义了代理所代表的真实对象,是代理模式中被代理的对象,客户端最终访问的就是真实主题。

  3. 代理主题(Proxy):保存一个引用使得代理可以访问真实主题,并提供一个与真实主题相同的接口,这样代理就可以用来代替真实主题。

代理模式的结构图如下所示:

|  Subject  |<---------|   Client   |
            +----------+          +------------+
                 /|\                          |
                  |                           |
                  |                           |
            +------------+          +------------+
            | RealSubject|          |    Proxy   |
            +------------+          +------------+

在这个结构中,客户端通过访问抽象主题接口来访问真实主题和代理主题,代理主题保存了一个对真实主题的引用,可以通过该引用来访问真实主题。客户端可以通过代理主题来访问真实主题,代理主题可以在访问真实主题前后进行一些额外的处理,例如控制访问、缓存结果、记录日志、延迟加载等。

5、3种代理模式

代理模式有以下三种常见的实现方式:

  1. 静态代理模式

静态代理模式是指由程序员创建代理类或特定工具自动生成源代码来实现代理的方式。在静态代理模式中,代理类和真实主题类都实现了相同的接口或继承了相同的父类,客户端通过代理类来访问真实主题类,代理类在调用真实主题类的方法前后可以进行一些额外的处理。静态代理模式的缺点是代理类和真实主题类需要实现相同的接口或继承相同的父类,这样会增加代码的复杂度。

  1. JDK 动态代理模式

动态代理模式是指在程序运行时根据需要动态创建代理类的方式。在动态代理模式中,代理类不需要实现与真实主题类相同的接口或继承相同的父类,代理类通过反射机制来调用真实主题类的方法,并在调用前后进行一些额外的处理。动态代理模式的优点是可以在程序运行时动态创建代理类,不需要编写额外的代码,但是需要了解反射机制。

  1. Cglib 代理模式

Cglib 代理模式是指使用 Cglib 库来动态生成代理类的方式。在 Cglib 代理模式中,代理类是真实主题类的子类,代理类通过重写真实主题类的方法来调用真实主题类的方法,并在调用前后进行一些额外的处理。Cglib 代理模式的优点是不需要实现相同的接口或继承相同的父类,可以对任意类进行代理,但是需要引入 Cglib 库。

6、静态代理模式的代码案例

静态代理模式是一种代理模式的实现方式,它通过创建一个代理类来包装真实的对象,从而实现对真实对象的控制和管理。以下是一个简单的静态代理模式的代码案例:

首先,定义一个公共接口 Subject ,它包含了真实对象和代理对象都需要实现的方法:

package com.pany.camp.design.principle.proxy.statics;

/**
 *
 * @description:  公共接口
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 12:47
 */
public interface Subject {
   
   
    void request();
}

然后,定义一个真实对象类 RealSubject ,它实现了 Subject 接口中的 request() 方法:

package com.pany.camp.design.principle.proxy.statics;

/**
 *
 * @description:  真实对象类
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 12:47
 */
public class RealSubject implements Subject {
   
   
    @Override
    public void request() {
   
   
        System.out.println("RealSubject: handling request.");
    }
}

接下来,定义一个代理类 ProxySubject ,它也实现了 Subject 接口,并包含一个真实对象的引用:

package com.pany.camp.design.principle.proxy.statics;

/**
 *
 * @description:  代理类
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 12:48
 */
public class ProxySubject implements Subject {
   
   

    private RealSubject realSubject;

    public ProxySubject(RealSubject realSubject) {
   
   
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
   
   
        System.out.println("ProxySubject: handling request.");
        realSubject.request();
    }
}

在 ProxySubject 类中,实现了 request() 方法,并在其中调用了真实对象的 request() 方法。通过这种方式,代理对象可以在调用真实对象的方法前后,执行一些额外的操作,从而实现对真实对象的控制和管理。

最后,可以编写一个测试类来测试静态代理模式的实现:

package com.pany.camp.design.principle.proxy.statics;

/**
 *
 * @description:  客户端
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 12:48
 */
public class Client {
   
   

    public static void main(String[] args) {
   
   
        RealSubject realSubject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(realSubject);
        proxySubject.request();
    }
}

输出结果如下:

Connected to the target VM, address: '127.0.0.1:57082', transport: 'socket'
ProxySubject: handling request.
RealSubject: handling request.
Disconnected from the target VM, address: '127.0.0.1:57082', transport: 'socket'

Process finished with exit code 0

在测试类中,首先创建一个真实对象 RealSubject ,然后创建一个代理对象 ProxySubject ,并将真实对象作为参数传递给代理对象。最后,调用代理对象的 request() 方法,即可执行代理对象和真实对象的方法。

通过上述代码案例,可以看出静态代理模式的实现方式。静态代理模式的优点是实现简单,易于理解和维护,同时可以在代理对象中添加额外的操作。缺点是需要手动编写代理类,如果要代理多个真实对象,代码量会增加,并且不利于代码的复用。

7、JDK 动态代理模式的代码案例

JDK 动态代理模式是一种代理模式的实现方式,它通过在运行时动态生成代理类来包装真实的对象,从而实现对真实对象的控制和管理。以下是一个简单的 JDK 动态代理模式的代码案例:

首先,定义一个公共接口 Subject ,它包含了真实对象和代理对象都需要实现的方法:

package com.pany.camp.design.principle.proxy.jdk;

/**
 *
 * @description:  公共接口
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 12:50
 */
public interface Subject {
   
   
    void request();
}

然后,定义一个真实对象类 RealSubject ,它实现了 Subject 接口中的 request() 方法:

package com.pany.camp.design.principle.proxy.jdk;

/**
 *
 * @description:  真实对象类
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 12:51
 */
public class RealSubject implements Subject {
   
   

    @Override
    public void request() {
   
   
        System.out.println("RealSubject: handling request.");
    }
}

接下来,定义一个代理类 ProxySubject ,它也实现了 Subject 接口,并包含一个真实对象类引用:

package com.pany.camp.design.principle.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @description: 代理类
 * @copyright: @Copyright (c) 2022
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0
 * @createTime: 2023-06-27 12:52
 */
public class ProxySubject implements InvocationHandler {
   
   
    private Object realSubject;

    public ProxySubject(Object realSubject) {
   
   
        this.realSubject = realSubject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   
   
        System.out.println("ProxySubject: handling request.");
        Object result = method.invoke(realSubject, args);
        return result;
    }
}

在 ProxySubject 类中,实现了 InvocationHandler 接口,并重写了其中的 invoke() 方法。在 invoke() 方法中,先执行代理对象的一些额外操作,然后再调用真实对象的方法,并将返回值返回给调用者。

最后,可以编写一个测试类来测试 JDK 动态代理模式的实现:

package com.pany.camp.design.principle.proxy.jdk;

import java.lang.reflect.Proxy;

/**
 *
 * @description:  客户端
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 12:52
 */
public class Client {
   
   
    public static void main(String[] args) {
   
   
        RealSubject realSubject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(realSubject);
        Subject proxyInstance = (Subject) Proxy.newProxyInstance(
            realSubject.getClass().getClassLoader(),
            realSubject.getClass().getInterfaces(),
            proxySubject
        );
        proxyInstance.request();
    }
}

输出结果如下:

Connected to the target VM, address: '127.0.0.1:64018', transport: 'socket'
ProxySubject: handling request.
RealSubject: handling request.
Disconnected from the target VM, address: '127.0.0.1:64018', transport: 'socket'

Process finished with exit code 0

在测试类中,首先创建一个真实对象 RealSubject ,然后创建一个代理对象 ProxySubject ,并将真实对象作为参数传递给代理对象。最后,通过 Proxy.newProxyInstance() 方法动态生成代理类,并将代理对象转换成 Subject 接口类型,即可执行代理对象和真实对象的方法。

通过上述代码案例,可以看出 JDK 动态代理模式的实现方式。JDK 动态代理模式的优点是不需要手动编写代理类,可以在运行时动态生成代理类,同时可以代理多个真实对象,代码量相对较少,并且便于代码的复用。缺点是只能代理接口,无法代理类,且需要真实对象实现接口。

8、 Cglib 代理模式的代码案例

Cglib 代理模式是一种代理模式的实现方式,它通过继承的方式来生成代理类,从而实现对真实对象的控制和管理。以下是一个简单的 Cglib 代理模式的代码案例:
首先,引入 Cglib 相关依赖:

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

然后,定义一个公共接口 Subject ,它包含了真实对象和代理对象都需要实现的方法:

package com.pany.camp.design.principle.proxy.cglib;

/**
 *
 * @description:  公共接口
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 12:55 
 */
public interface Subject {
   
   
    void request();
}

接下来,定义一个真实对象类 RealSubject ,它实现了 Subject 接口中的 request() 方法:

package com.pany.camp.design.principle.proxy.cglib;

/**
 *
 * @description:  真实对象类
 * @copyright: @Copyright (c) 2022
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0
 * @createTime: 2023-06-27 12:56
 */
public class RealSubject implements Subject {
   
   
    @Override
    public void request() {
   
   
        System.out.println("RealSubject: handling request.");
    }
}

然后,定义一个代理类 ProxySubject ,它继承了 Cglib 的 MethodInterceptor 类,并包含一个真实对象引用:

package com.pany.camp.design.principle.proxy.cglib;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 *
 * @description:  代理类
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-27 12:57
 */
public class ProxySubject implements MethodInterceptor {
   
   

    private Object realSubject;

    public ProxySubject(Object realSubject) {
   
   
        this.realSubject = realSubject;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
   
   
        System.out.println("ProxySubject: handling request.");
        Object result = method.invoke(realSubject, objects);
        return result;
    }
}

在 ProxySubject 类中,实现了 MethodInterceptor 接口,并重写了其中的 intercept() 方法。在 intercept() 方法中,先执行代理对象的一些额外操作,然后再调用真实对象的方法,并将返回值返回给调用者。
最后,可以编写一个测试类来测试 Cglib 代理模式的实现:

package com.pany.camp.design.principle.proxy.cglib;

import org.springframework.cglib.proxy.Enhancer;

/**
 *
 * @description: 客户端
 * @copyright: @Copyright (c) 2022
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0
 * @createTime: 2023-06-27 12:58
 */
public class Client {
   
   

    public static void main(String[] args) {
   
   
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubject.class);
        enhancer.setCallback(new ProxySubject(new RealSubject()));
        RealSubject proxyInstance = (RealSubject) enhancer.create();
        proxyInstance.request();
    }
}

输出结果如下:

ProxySubject: handling request.
RealSubject: handling request.

Process finished with exit code 0

在测试类中,首先创建一个 Enhancer 对象,然后设置其父类为真实对象 RealSubject ,设置回调对象为代理对象 ProxySubject ,最后通过 enhancer.create() 方法生成代理对象。通过代理对象调用真实对象的方法,即可执行代理对象和真实对象的方法。
通过上述代码案例,可以看出 Cglib 代理模式的实现方式。Cglib 代理模式的优点是可以代理类,无需真实对象实现接口,同时可以在运行时动态生成代理类,代码量相对较少,并且便于代码的复用。缺点是生成代理类的过程比较耗时,且对于 final 类和 final 方法无法进行代理。

1686494501743.jpg

💕💕 本文由激流原创,首发于CSDN博客,博客主页 https://blog.csdn.net/qq_37967783?spm=1010.2135.3001.5421
💕💕喜欢的话记得点赞收藏啊

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
5月前
|
设计模式 缓存 监控
【设计模式系列笔记】代理模式
代理模式是一种结构型设计模式,它允许一个对象(代理对象)控制另一个对象的访问。代理对象通常充当客户端和实际对象之间的中介,用于对实际对象的访问进行控制、监控或其他目的。
80 1
|
5月前
|
设计模式 缓存 安全
小谈设计模式(8)—代理模式
小谈设计模式(8)—代理模式
|
22天前
|
设计模式 缓存 安全
设计模式——代理模式
静态代理、JDK动态代理、Cglib 代理
设计模式——代理模式
|
10天前
|
设计模式 Java 数据安全/隐私保护
Java设计模式-代理模式(7)
Java设计模式-代理模式(7)
|
5月前
|
设计模式 Java
Java一分钟之-设计模式:装饰器模式与代理模式
【5月更文挑战第17天】本文探讨了装饰器模式和代理模式,两者都是在不改变原有对象基础上添加新功能。装饰器模式用于动态扩展对象功能,但过度使用可能导致类数量过多;代理模式用于控制对象访问,可能引入额外性能开销。文中通过 Java 代码示例展示了两种模式的实现。理解并恰当运用这些模式能提升代码的可扩展性和可维护性。
52 1
|
5月前
|
设计模式 Java 数据库连接
【重温设计模式】代理模式及其Java示例
【重温设计模式】代理模式及其Java示例
|
2月前
|
设计模式 缓存 Java
【十一】设计模式~~~结构型模式~~~代理模式(Java)
文章详细介绍了代理模式(Proxy Pattern),这是一种对象结构型模式,用于给对象提供一个代理以控制对它的访问。文中阐述了代理模式的动机、定义、结构、优点、缺点和适用环境,并探讨了远程代理、虚拟代理、保护代理等不同代理形式。通过一个商务信息查询系统的实例,展示了如何使用代理模式来增加身份验证和日志记录功能,同时保持客户端代码的无差别对待。此外,还讨论了代理模式在分布式技术和Spring AOP中的应用,以及动态代理的概念。
【十一】设计模式~~~结构型模式~~~代理模式(Java)
|
2月前
|
设计模式
设计模式的基础问题之代理模式在工作中的问题如何解决
设计模式的基础问题之代理模式在工作中的问题如何解决
|
3月前
|
设计模式 算法 Go
iLogtail设计模式问题之代理模式在iLogtail中是如何应用的
iLogtail设计模式问题之代理模式在iLogtail中是如何应用的
|
3月前
|
设计模式 缓存 JavaScript
js设计模式【详解】—— 代理模式
js设计模式【详解】—— 代理模式
27 0