探秘代理模式:核心原理与应用实践_动态代理

前言

说到代理你首选会想到什么:

不方便自己做的事,要不要找个代理?比如说:美国人的代理人战争

自己搞不定或者不擅长的事,要不要找个代理?比如说:代汽车上牌、房产中介、黄牛等;

没错,这就是代理。当然这篇文章主要是来盘一盘设计模式中的代理模式。

什么是代理模式

代理模式是一种设计模式,它为其他对象提供一种代理以控制对该对象的访问。即用户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。代理模式给某一个对象提供一个代理对象,并由代理对象控制原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。通过代理类,起到了中介隔离作用,代理类除了是客户类和委托类的中介隔离作用之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类,而不需要在修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转达给委托类,以及时候对返回结果的处理等。代理类本身不真正实现服务,而是通过调用委托类的相关方法,来提供特定的服务。例如,购买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点买。又比如找女朋友、找保姆、找工作等都可以通过找中介完成

代理模式的特点

  1. 代理模式能够隐藏真实对象的实现细节,使客户端无需知晓真实对象的工作方式和结构。
  2. 通过代理类来间接访问真实类,可以在不修改真实类的情况下对其进行扩展、优化或添加安全措施。
  3. 代理模式实现起来简单,易于扩展和维护,符合面向对象设计原则中的开闭原则。

代理模式的核心原理

代理模式的核心原理是通过引入一个代理对象来控制对另一个对象的访问。这种类型的结构型设计模式允许你提供一个替代品或者占位符,以便控制对这个对象的访问。

具体来说,代理模式包括四个主要角色:抽象角色、真实主题和代理和客户端。客户端通过调用代理来间接访问真实主题,从而在不直接访问真实主题的情况下完成相应的操作。代理可以在客户端和真实主题之间起到中介作用,也可以对真实主题的访问进行控制,例如在访问前后执行一些额外的操作。代理模式四个核心角色,UML类图与详细介绍如下:

  1. 抽象主题(Subject):定义了真实主题和代理主题的共同接口,这样代理对象可以替代真实对象。它通常是一个接口或抽象类。
  2. 真实主题(Real Subject):实现了抽象主题接口,是真正执行业务逻辑的类。
  3. 代理(Proxy):实现了抽象主题接口,持有对真实主题的引用。在需要时,它将请求传递给真实主题,并可以在请求前后执行额外的逻辑。
  4. 客户端:通过代理对象来访问真实对象。

探秘代理模式:核心原理与应用实践_代理模式_02

代理模式如何实现

根据代理的创建时期,代理模式分为静态代理和动态代理

  • 静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
  • 动态代理:在程序运行时,运用反射机制动态创建而成

静态代理

水浒传相信很多人都看过,这里就以西门庆通过王婆与潘金莲私会的情节举个例子,在这个情节中,王婆实际上都是充当一个代理人的角色,自己本身并不执行约会这件事,实际执行约会这件事的人是金莲;当然了,每次西门庆想与金莲玩耍一番,不会直接上金莲家的,而是到王婆家中,给王婆交上几两银子,然后王婆再把金莲叫来,一番玩耍后,王婆还会再假模假样的客气一番:“官人再来”之类的话。

如果画一个UML类图来表示这件事的话,如下:

探秘代理模式:核心原理与应用实践_代理模式_03

代码示例:

1、定义抽象的约会类(抽象主题);

2、 定义金莲(真实的主题);

3、定义王婆(代理);

4、定义西门庆(客户端);

/** 
* 约会 
*/
public interface YueHui {
    /**
     * 玩耍
     */
    void play();
}
/** 
* 金莲 
*/
public class PanJinLian implements YueHui{
    @Override
    public void play() {
        System.out.println("金莲:玩得很开心呢");
    }
}
/** 
* 王婆 
*/
public class WangPo implements YueHui {
    private  PanJinLian panJinLian;

    public WangPo(PanJinLian panJinLian) {
        this.panJinLian = panJinLian;
    }

    @Override
    public void play() {
        this.beforePlay();
        panJinLian.play();
        this.afterPlay();
    }

    private void beforePlay(){
        System.out.println("王婆:交五两银子");
    }

    private void afterPlay(){
        System.out.println("王婆:欢迎下次再来");
    }
}
/** 
* 西门庆 
*/
public class XiMenQing {
    public static void main(String[] args) {
        PanJinLian panJinLian = new PanJinLian();
        WangPo wangPo = new WangPo(panJinLian);
        wangPo.play();
    }
}


探秘代理模式:核心原理与应用实践_java_04

动态代理

动态代理:是在程序运行时,运用反射机制动态创建而成,从实现上又分为两类:

  • 基于接口的动态代理(如InvocationHandler),即JDK动态代理
  • 基于类的动态代理,使用CGLIB直接生成代理类;

这里主要分享一下JDK动态代理这种方式,另一种方式有兴趣的小伙伴可以自行再拓展一下。

还是上面的例子,用Java的动态代理再来实现一下;其实也很简单,上面的静态代理对象WangPo是在代码编译前已经实现好的,这里的动态代理对象panJinLianProxy则是通过Proxy类使用Java反射技术在程序运行时动态生成的;

public class WangPoHandler implements InvocationHandler {    private PanJinLian panJinLian;    public WangPoHandler(PanJinLian panJinLian) {        this.panJinLian = panJinLian;    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        this.beforePlay();
        Object result = method.invoke(panJinLian, args);
        this.afterPlay();
        return result;
    }
    private void beforePlay() {
        System.out.println("王婆:交五两银子");
    }
    private void afterPlay() {
        System.out.println("王婆:欢迎下次再来");
    }
}
public class XiMenQing2 {    
    public static void main(String[] args) {        
        PanJinLian panJinLian = new PanJinLian();       
        WangPoHandler wangPoHandler = new WangPoHandler(panJinLian);       
        YueHui panJinLianProxy = (YueHui) Proxy.newProxyInstance(WangPoHandler.class.getClassLoader(), PanJinLian.class.getInterfaces(), wangPoHandler);
        panJinLianProxy.play();
    }
}

代理模式在Spring中的应用

在Spring框架中,代理模式应用场景如下:

  1. AOP(面向切面编程):Spring中的AOP实现主要基于代理模式。Spring会自动扫描容器中的组件,对于需要进行AOP处理的组件,Spring会使用动态代理创建代理对象,并将其放入容器中。当业务方法被调用时,代理对象会捕获到调用,并在调用前后执行增强逻辑。
  2. RPC远程调用:在RPC远程调用中,代理模式可以用于处理网络通信和数据传输。通过使用代理模式,客户端可以像调用本地方法一样调用远程对象的方法,代理对象会负责将请求发送到远程服务器并等待响应。
  3. 注解对象获取:Spring框架的注解对象获取,比如通过@Autowired注解获取bean对象,实际上也是使用了代理模式。Spring会在启动时扫描所有的类,对于有@Autowired注解的属性或方法,Spring会自动为其生成代理对象,并注入相应的依赖。
  4. 日志框架、全局性异常处理、事务处理、权限管理、MyBatis的Mapper层接口等:在这些场景中,代理模式可以增强目标对象的功能,扩展其行为,或者在目标对象前后添加额外的操作,比如日志记录、事务处理等。

但是需要注意的是,Spring中的代理模式主要有两种实现方式:基于JDK的动态代理和基于CGLib的动态代理。其中,基于JDK的动态代理主要依赖于Java反射机制,通过为每个需要代理的类生成一个子类来实现代理;而基于CGLib的动态代理则通过在运行时创建子类来实现代理。

总结

优点:

  1. 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
  2. 代理对象可以扩展目标对象的功能;
  3. 代理模式能将客户端与目标对象分离,职责清晰,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

缺点:

  1. 代理模式会造成系统设计中类的数量增加
  2. 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
  3. 增加了系统的复杂度;