设计模式(五)—— 代理模式

1 代理模式简介

代理(Proxy)模式或者叫委托模式是一种结构型设计模式,提供了对目标对象另外的访问方式—即通过代理对象访问目标对象。这样做的好处是可以在目标对象实现的基础上,扩展目标对象的功能。

现实生活中有好多类似代理的长江,比如所代购,打官司等。

proxy [ˈprɑːksi] 代理权,代表权;代理人

代理模式

代理模式主要涉及的三个角色:

  • 接口(Subject):目标类和代理类都应该实现的接口;
  • 目标类(Real Subject)是真正用来完成业务功能的类;
  • 代理类(Proxy):持有对目标类的引用,自身的请求用目标类来实现(在其所实现的接口方法中调用目标类中相应的接口方法执行),代理类并不真正的去做业务功能;

以下是代理模式的结构图:

代理模式结构图

通过以下代码来说明代理模式。比如说有一个歌手对象是 Singer,这个对象有一个唱歌方法是 sing():

public class Singer {
  public void sing() {
    System.out.println("sing a song");
  }
}

假如这个时候,希望通过某种方式创建的歌手对象,在唱歌前后还要向观众问好和答谢,也就是对目标对象的 sing() 方法进行功能扩展:

public void sing() {
  System.out.println("say hello");
  System.out.println("sing a song");
  System.out.println("say thanks");
}

这个时候如果不能对源代码进行修改,就可以考虑代理模式。

2 静态代理

静态代理在程序运行前就已经将代理类的源码编译成字节码文件,代理类和委托类的关系在运行前就已经确定了。

静态代理需要先定义接口,被代理的对象和代理对象一起实现相同的接口,代理类持有目标类的引用,然后通过调用相同的方法来调用目标对象的方法。 以下代码是静态代理的实现方式:

public interface ISinger {
    void sing();
}

public class Singer implements ISinger {
    @Override
    public void sing() {
        System.out.println("sing a song");
    }
}

public class SingerProxy implements ISinger {

    private Singer singer;
    
    @Override
    public void sing() {
        System.out.println("say hello");
        if (singer == null) {
            singer = new Singer();
        }
        singer.sing();
        System.out.println("say thanks");
    }
}

class SingTest {
    public static void main(String[] args) {
        SingerProxy singerProxy = new SingerProxy();
        singerProxy.sing();
    }
}

// say hello
// sing a song
// say thanks

目标类 Singer 和代理类 SingerProxy 都实现接口 ISinger。在代理类 SingerProxy.sing() 方法中调用了目标类 Singer.sing() 方法,并做了拓展。

静态代理相当于多写了一个代理类,在调用的时候调用的是代理类中的相关逻辑。这样做的缺点是:需要为每个被代理的对象创建一个代理类。

3 动态代理

从编码的角度来看,代理模式分为静态代理和动态代理,上面的例子是静态代理,在代码运行前就已经存在了代理类 .class 的编译文件,而动态代理则是在代码运行时通过反射来动态地生成代理类的对象,来确定到底代理谁,也就是在编码阶段不需要知道代理谁,而是在代码运行时决定的。

Java 提供了动态的代理接口 InvocationHandler,实现该接口需要重写 invoke 方法。和静态代理一样,是对代理类的扩展。由于 Java 底层封装了实现细节,格式基本上是固定的。调用 Proxy 类的静态方法 newProxyInstance 即可,该方法会返回代理类对象:

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

其中的三个参数依次为:

  • ClassLoader loader:指定当前目标对象使用类加载器,写法固定;
  • Class<?>[] interfaces:目标对象实现的接口类型,写法固定;
  • InvocationHandler h:事件处理接口,需要传入一个实现类,一般直接使用匿名内部类;

invocation [ˌɪnvəˈkeɪʃn](计算机)调用,启用;(法权的)行使

以下是动态代理的相关代码:

class SingTest {
  public static void main(String[] args) {
    Singer singer = new Singer();
    ISinger proxy = (ISinger) Proxy.newProxyInstance(singer.getClass().getClassLoader(), singer.getClass().getInterfaces(), new InvocationHandler() {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("say hello");
        Object returnValue = method.invoke(singer, args);
        System.out.println("say thanks");
        return returnValue;
      }
    });
    proxy.sing();
  }
}

动态代理类是在程序运行期间由 JVM 根据反射等机制动态生成的,所以不会有代理类的字节码文件,代理类和委托类的关系是在程序运行时确定的。

参考

https://www.cnblogs.com/baorantHome/p/16136213.html
https://www.jb51.net/article/222323.htm
https://wenku.baidu.com/view/d21555f130d4b14e852458fb770bf78a65293a91.html
https://blog.csdn.net/qq_41931364/article/details/122662857

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值