2

一、写在前面

代理模式是常用的结构型设计模式之一、当我们直接访问某些对象存在问题时可以通过代理模式来间接访问,为了保证客户端使用的透明性、所访问的真实对象和代理对象都必须实现同一个接口。

二、代理模式动机与定义

某人要找对象(大部分是程序员),但是由于某些原因(996)不能直接去找。

于是就委托一个中介机构去帮自己完成找女朋友的过程,如婚姻中介所、某某社交软件等。

在这里婚姻介绍所或者某某社交软件就是一个代理,帮你找美女。

在我们生活中代理无处不在、比如房屋中介、职业中介(某某招聘网)等它们充当的都是一个代理角色。

所谓的代理就是一个人或者一个机构代表另一个人或者机构采取行动。

二.一 模式动机

在某些情况下,一个客户不想或者不能直接应用另一个对象,可以通过一个称之为‘代理’的第三者来实现间接的引用。

代理对象在客户和目标对象之间起到一个中介的作用,并且可以通过代理对象去掉某些客户不能看到的内容或者服务,同时也可以添加客户需要的额外服务。

二.二 模式的定义

代理模式(Proxy Patten)定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

代理对象英文名称叫做 Peoxy 或者 Surrogate 他是一种对象结构模式。

三、代理模式结构与分析

代理模式结构比较简单、其核心就是一个代理类,下面我们来分析下其模式结构

三.一 模式结构

代理模式UML图

图片描述

代理模式包含以下三个角色

  1. Subject(抽象主题角色)
    抽象主题角色申明了真实主题和代理主题的共同接口、这样以来任何使用真实主题的地方可以都使用代理主题、客户端需要针对抽象主题角色来编程
  2. Proxy(代理主题角色)
    代理主题角色包含了对真实主题的引用,从而可以在任何时候操作真实主题角色。
  3. RealSubject(真实主题角色)
    真实主题角色定义了代理主题角色所代表的具体对象,真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色来间接的调用真实主题角色中定义的方法。

三.二模式分析

代理模式的示意图结构比较简单、一般可以简化如下图所示。
但是在现实中要复杂的多。

图片描述

典型的代理类代码如下:

public class Proxy implements Subject {

    private RealSubject realSubject = new RealSubject();

    public void preRequest() {
        System.out.println("---pre---");
    }

    public void request() {
        preRequest();

        realSubject.request();

        postRequest();

    }

    public void postRequest() {
        System.out.println("----post-----");
    }

}

在真实应用中,代理类的实现好比着复杂的多,它需要一套自己的方式去访问真实对象,以便作为真实对象的代理。

四、代理模式的实例

代理模式在我应用开发中一般就分为静态代理和动态代理两类。
下面我们来一个例子来具体的理解下代理模式

四.一 静态代理

下面我们以我们最熟悉的Spring AOP 处理事务的方式来实现,废话不多说直接上代码吧!

抽象角色

public interface TrancationSubject {
    
    void request();

}

具体角色

public class TrancationRealSubject implements TrancationSubject {

    public void request() {
        System.err.println("---执行事务方法---");
    }

代理角色

public class TrancationProxy implements TrancationSubject {

    private TrancationRealSubject realSubject;

    public TrancationProxy(TrancationRealSubject realSubject) {
        super();
        this.realSubject = realSubject;
    }

    public void preRequest() {
        System.out.println("---open trancation---");
    }

    public void request() {
        preRequest();

        realSubject.request();

        postRequest();

    }

    public void postRequest() {
        System.out.println("----commit/rollback-----");
    }

}

client端

public class Client {

    public static void main(String[] args) {

        TrancationProxy proxy = new TrancationProxy(new TrancationRealSubject());
        proxy.request();
    }

}

执行结果

---open trancation---
---执行事务方法---

----commit/rollback

上述静态代理其优点就不多说了,继承了代理模式的优点,但是其缺点就不得不唠叨下了。

缺点:
1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

由于这两种致命的缺点到这在真实的应用环境中很难看到静态代理的使用。

下面我们来说说其高配版。

四.二 动态代理

动态代理是一种高级代理模式,最典型的引用场景就是Spring AOP 。

Java动态到了实现相关包主要位于java.lang.reflect包下面主要涉及两个类

(1) InvocationHandler 接口,它是代理实例的调用处理程序实现的接口,该结构定义了如下方法。

Object invoke(Object proxy,Method method,Object[] args) throws Throwable

在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。

参数:

  1. proxy - 在其上调用方法的代理实例
  2. method - 对应于在代理实例上调用的接口方法的 Method 实例。 Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
  3. args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。

(2) Proxy 类,该类即为动态代理类,最常用的方法为

newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 

返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

参数:

  1. loader - 定义代理类的类加载器
  2. interfaces - 代理类要实现的接口列表
  3. h - 指派方法调用的调用处理程序

对上面动态代理一些主要方法做下了解,下面我们该早上面代理类。

public class DynamicProxy implements InvocationHandler {

    private Object obj;

    public DynamicProxy(Object obj) {
        super();
        this.obj = obj;
    }

    public void preRequest() {
        System.out.println("---open trancation---\n");
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        preRequest();

        Object invoke = method.invoke(obj, args);

        postRequest();

        return invoke;
    }

    public void postRequest() {
        System.out.println("----commit/rollback-----");
    }
}

client

public class Client {

    public static void main(String[] args) {

        DynamicProxy invocationHandler = new DynamicProxy(new TrancationRealSubject());

        TrancationSubject newProxyInstance = (TrancationSubject) Proxy.newProxyInstance(
                TrancationSubject.class.getClassLoader(), new Class[] { TrancationSubject.class }, invocationHandler);
        
        newProxyInstance.request();

    }

}

在这执行结果就不贴了。

动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。
这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强。

五、总结

  1. 在代理模式中,要求给某一个对象提供一个代理,并由代理对象控制对原对象的访问,其英文为proxy 是一种结构型模式。
  2. 代理模式包含三种角色,抽象主题角色、代理主题角色、真实主题角色。
  3. 代理模式的优点在于协调调用者和被调用者,在一定的程度上降低了系统的耦合性。其缺点在调用者和被调用者中间增加了代理层,因此有些类型的代理模式可能会造成请求处理速度变慢,并且代理模式需要额外的工作,有些代理模式的实现非常复杂。

阅历笔记
279 声望37 粉丝

喷泉之所以漂亮是因为她有了压力;