Bootstrap

Java 代理模式详解

如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。

  1. 代理模式的定义
    • 代理模式是一种设计模式,它为其他对象提供一种代理,以控制对这个对象的访问。代理对象可以在客户端和目标对象之间起到中介的作用,它可以在不改变目标对象接口的情况下,增强目标对象的功能或者对目标对象的访问进行控制。
  1. 代理模式的结构
    • 主题(Subject)接口:定义了真实对象和代理对象共同的接口,客户端通过这个接口来访问真实对象和代理对象。例如,在一个文件读取系统中,主题接口可以是FileReaderSubject,它定义了读取文件内容的抽象方法,如readFile(String fileName)
    • 真实主题(Real Subject):实现了主题接口,是真正完成业务逻辑的对象。比如,在文件读取系统中,真实主题可以是FileReaderImpl,它实现了FileReaderSubject接口,具体实现了从文件系统中读取文件内容的方法。
    • 代理(Proxy):同样实现了主题接口,代理对象内部包含了真实主题对象的引用。它可以在调用真实主题对象的方法之前或之后,添加额外的逻辑,如权限验证、日志记录等。在文件读取系统中,代理对象FileReaderProxy可以在调用FileReaderImplreadFile方法之前,先检查用户是否有读取文件的权限。
  1. 代理模式的类型
    • 静态代理
// 接口定义
interface Calculator {
    int add(int a, int b);
}
// 真实主题(被代理类)
class CalculatorImpl implements Calculator {
    @Override
    public int add(int a, int b) {
        return a + b;
    }
}
// 代理类
class CalculatorProxy implements Calculator {
    private Calculator calculator;
    public CalculatorProxy(Calculator calculator) {
        this.calculator = calculator;
    }
    @Override
    public int add(int a, int b) {
        System.out.println("在加法运算前进行日志记录");
        int result = calculator.add(a, b);
        System.out.println("在加法运算后进行日志记录");
        return result;
    }
}
      • 定义:静态代理是在编译时期就已经确定了代理类和被代理类的关系。代理类和被代理类都需要实现同一个接口,代理类通过持有被代理类的实例来调用其方法,并在调用前后添加自己的逻辑。
      • 示例:假设我们有一个接口Calculator,它有一个方法add(int a, int b)用于计算两数之和。
      • 在这个例子中,CalculatorProxyCalculatorImpl的代理类。当客户端调用CalculatorProxyadd方法时,它会先打印日志,然后调用CalculatorImpladd方法,最后再打印日志。
    • 动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 动态代理的调用处理器
class CalculatorInvocationHandler implements InvocationHandler {
    private Object target;
    public CalculatorInvocationHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("在动态代理方法调用前进行日志记录");
        Object result = method.invoke(target, args);
        System.out.println("在动态代理方法调用后进行日志记录");
        return result;
    }
}
Calculator calculator = new CalculatorImpl();
Calculator proxy = (Calculator) Proxy.newProxyInstance(
        Calculator.class.getClassLoader(),
        new Class[]{Calculator.class},
        new CalculatorInvocationHandler(calculator));
int result = proxy.add(1, 2);
      • 定义:动态代理是在运行时动态地生成代理类。在Java中,主要通过java.lang.reflect.Proxy类和InvocationHandler接口来实现动态代理。动态代理不需要像静态代理那样为每个被代理类都编写一个代理类,它可以根据需要动态地创建代理对象。
      • 示例:还是以上面的Calculator接口为例,我们可以这样实现动态代理:
      • 客户端使用动态代理的代码如下:
      • 在这个动态代理的例子中,CalculatorInvocationHandler是实现了InvocationHandler接口的调用处理器。Proxy.newProxyInstance方法会在运行时动态地生成一个代理对象,这个代理对象实现了Calculator接口。当客户端调用代理对象的方法时,会触发InvocationHandlerinvoke方法,在这个方法中可以添加代理逻辑,如日志记录,然后再调用真实对象(CalculatorImpl)的方法。
  1. 代理模式的应用场景
    • 远程代理:用于访问远程对象,就好像访问本地对象一样。例如,在分布式系统中,客户端通过代理对象来调用远程服务器上的对象。代理对象负责处理网络通信等细节,如Java RMI(Remote Method Invocation)就使用了远程代理。
    • 虚拟代理:当创建一个对象的代价比较大(如加载大型资源文件、初始化复杂对象等)时,可以使用虚拟代理。虚拟代理先创建一个简单的代理对象来代替真实对象,当真正需要使用真实对象时,再进行创建。例如,在网页加载图片时,可以先显示一个占位符(代理),当图片真正加载完成后(真实对象创建完成),再显示真实的图片。
    • 安全代理:用于控制对敏感对象的访问。代理对象可以在访问真实对象之前,对用户的权限进行验证。例如,在企业级应用中,对某些敏感数据的访问,只有具有特定权限的用户才能通过代理访问到真实的数据。

;