代理模式简介

概念

代理模式(Proxy Pattern)是一种结构型设计模式,它允许通过创建一个代理对象来控制对原始对象的访问。代理对象充当了客户端和目标对象之间的中介,可以在不改变目标对象的情况下增加额外的功能或限制访问。

特点

  1. 代理模式通过引入代理类来隐藏真实对象,并提供了与真实对象相同的接口,使得客户端无需直接与真实对象交互。
  2. 代理类可以在调用真实对象之前或之后执行额外的操作,如权限验证、缓存等。
  3. 客户端无法区分是否使用了代理,因为它们都遵循相同的接口。

优点

  1. 降低系统耦合度:将客户端和目标对象解耦,减少对目标类直接依赖。
  2. 提高安全性:通过代理进行权限验证和访问控制。
  3. 增强扩展性:可以在不修改源代码的情况下增加新功能。

缺点

  1. 增加复杂性:引入了额外的间接层,在一些简单场景下可能会增加代码复杂性。
  2. 对于某些涉及频繁调用的方法,由于需要经过多次转发导致性能降低。

适用场景

  1. 远程代理:用于在不同地址空间中访问对象,如远程方法调用(RPC)。
  2. 虚拟代理:用于按需加载大对象或延迟创建昂贵的对象,以提高系统性能。
  3. 安全代理:用于控制对敏感资源的访问权限。
  4. 智能引用代理:用于添加额外的操作,如缓存、日志记录等。

实现方式

静态代理

实现原理:

在编译时就已经确定了被代理类和具体的代理类,在代码中直接定义一个具体的实现类作为目标对象和一个对应的具体实现类作为代理对象。静态代理需要手动编写每个方法的转发逻辑,并且只能针对特定类型进行操作。

实现代码:

// 目标接口
interface Image {
    void display();
}

// 目标类
class RealImage implements Image {
    private String filename;

    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk();
    }

    private void loadFromDisk() {
        System.out.println("Loading image: " + filename);
    }

    @Override
    public void display() {
        System.out.println("Displaying image: " + filename);
    }
}

// 代理类
class ImageProxy implements Image {
    private String filename;
    private RealImage realImage;

    public ImageProxy(String filename) {
        this.filename = filename;
        this.realImage = null; // 初始时不加载真实对象
    }

    @Override
    public void display() {
        if (realImage == null) { // 懒加载,只有在需要时才创建真实对象
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

public class Main {

    public static void main(String[] args) {

        Image image1 = new ImageProxy("image1.jpg");//无输出
        // 使用代理显示图片,只有在调用 `display()` 方法时才会加载真实图片对象
        image1.display();//输出 Loading image: image1.jpg
                         //    Displaying image: image1.jpg

        
        Image image2 = new RealImage("image2.jpg");//输出Loading image: image2.jpg
        // 直接使用目标类显示图片,立即加载真实图片对象并显示
        image2.display();//输出 Displaying image: image2.jpg
    }
}

 在上述示例中,我们定义了一个目标接口 Image 和其具体实现类 RealImage。然后创建了一个代理类 ImagProxy来控制对目标对象的访问。当客户端创建代理类对象并不会马上加载图片,只有在调用display()时才会加载图片并展示图片。在创建目标对象时,会立刻加载图片。

动态代理:

动态代理是指在运行时动态生成代理类的过程,它不需要手动编写具体的代理类,而是通过 Java 的反射机制和字节码生成技术,在内存中创建一个新的代理类,并在运行时将方法调用转发给真实对象。

实现原理:

  1. 定义目标接口:首先需要定义一个目标接口,即被代理对象所遵循的接口。
  2. 实现 InvocationHandler 接口:创建一个实现 java.lang.reflect.InvocationHandler 接口的类,该类负责处理所有方法调用并执行相应操作。
  3. 创建 Proxy 对象:使用 java.lang.reflect.Proxy 类的静态方法 newProxyInstance() 来创建代理对象。此方法会返回一个实现了目标接口并由指定 InvocationHandler 处理方法调用的新对象。

实现代码:

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

// 目标接口
interface Image {
    void display();
}

// 目标类
class RealImage implements Image {
    private String filename;

    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk();
    }

    private void loadFromDisk() {
        System.out.println("Loading image: " + filename);
    }

    @Override
    public void display() {
        System.out.println("Displaying image: " + filename);
    }
}

// InvocationHandler 实现类
class ImageInvocationHandler implements InvocationHandler {
    private Object target;

    public ImageInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在方法调用前后可以执行一些额外操作,如权限验证、日志记录等
        System.out.println("Before method invocation");
        Object result = method.invoke(target, args);
        System.out.println("After method invocation");
        return result;
    }
}

public class Main {

    public static void main(String[] args) {

        RealImage realImage = new RealImage("image.jpg");

        // 创建代理对象并绑定目标对象和 InvocationHandler 对象
        Image proxyImage = (Image) Proxy.newProxyInstance(
                realImage.getClass().getClassLoader(),
                realImage.getClass().getInterfaces(),
                new ImageInvocationHandler(realImage)
        );

        // 通过代理对象调用方法,实际上会转发给真实对象的相应方法,并在前后执行额外操作
        proxyImage.display();
    }
}

在上述示例中,我们定义了一个目标接口 Image 和其具体实现类 RealImage。然后创建了一个实现了 java.lang.reflect.InvocationHandler 接口的类 ImagInvocatioHandler来处理所有方法调用。最后使用 java.lang.reflect.Proxy 类的静态方法 newProxInstance() 来创建代理对象,并将目标对象和 InvocationHandler 对象绑定在一起。当客户端通过代理对象调用方法时,实际上会转发给真实对象的相应方法,并在前后执行额外操作。使用了 Java 的反射机制和字节码生成技术,在运行时动态地创建代理类。

存在问题:

  1. 动态代理只能针对接口进行操作,无法直接对普通类进行操作。
  2. 由于每次都要通过反射来处理方法调用,因此性能上可能会有一些损耗。

你可能感兴趣的:(#,设计模式,代理模式)