java 代理模式

120 阅读4分钟

介绍

当涉及到面向对象编程的设计模式时,代理模式是一个常见且强大的工具。在Java中,我们可以使用代理模式来实现在不修改原始类的情况下,拓展其功能,实现横切关注点的处理。

代理模式的概念和原理

代理模式是一种结构型设计模式,它通过创建一个代理对象来控制对原始对象的访问。代理对象持有对原始对象的引用,并在其上进行操作,以实现对原始对象的间接访问和控制。

也就是说代理模式的主要作用是扩展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一些自定义的操作。

在Java中,代理模式可以分为静态代理和动态代理两种。

  • 静态代理:在编译阶段就已经确定代理类的结构,代理类和被代理类是一对一的关系。静态代理需要手动为每个被代理方法编写代理逻辑。
  • 动态代理:在运行时动态生成代理类,代理类和被代理类是一对多的关系。动态代理使用Java的反射机制,通过在运行时生成代理类和方法来实现,无需手动编写每个被代理方法的代理逻辑。

静态代理

首先,我们来看一个静态代理的示例代码:

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 final 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;
    }
}

public class StaticProxyExample {
    public static void main(String[] args) {
        Calculator calculator = new CalculatorImpl();
        Calculator proxy = new CalculatorProxy(calculator);

        int result = proxy.add(2, 3);
        System.out.println("结果:" + result);
    }
}

在上述示例中,我们定义了一个接口Calculator和其实现类CalculatorImpl。然后我们创建了一个代理类CalculatorProxy,该类实现了与Calculator相同的接口。在add方法中,我们添加了代理的逻辑,比如在计算之前和之后输出了一些日志信息。在StaticProxyExample类中,我们创建了一个原始对象和一个代理对象,并通过代理对象进行方法调用。

静态代理虽然实现简单,功能强大。但也有很多缺点

  1. 代码冗余:使用静态代理需要手动为每个被代理方法编写代理逻辑,当被代理方法较多时,会导致代码冗余,增加维护难度。
  2. 扩展性差:静态代理在编译时确定代理类的结构,如果需求发生改变或新增代理方法,则需要手动修改代理类的代码,不符合开闭原则。
  3. 可复用性低:由于每个代理类只能代理一个特定的接口或类,所以无法实现对多个接口或类的复用。

动态代理

动态代理是指在编译时无法确定代理类的具体类型,而是在运行时根据需要动态生成代理类的技术。Java中的动态代理主要依靠两个核心类:ProxyInvocationHandler

  • Proxy类是Java提供的用于创建代理对象的工具类,通过调用newProxyInstance方法可以动态地生成代理对象。该方法接受三个参数:类加载器、要实现的接口和一个InvocationHandler对象。
  • InvocationHandler接口是用户自定义的代理逻辑处理器,在代理对象的方法被调用时,InvocationHandlerinvoke方法会被自动调用。开发者需要在该方法中编写对原始方法的增强逻辑。

动态代理的工作原理如下:

  1. 创建一个实现了InvocationHandler接口的类,该类包含对目标对象的引用和处理原始方法的逻辑。
  2. 使用Proxy类的newProxyInstance方法,传入类加载器、目标接口和InvocationHandler实例来创建代理对象。
  3. 当代理对象的方法被调用时,会委托给InvocationHandlerinvoke方法进行处理,从而实现对原始方法的增强。

接下来,我们来看一个动态代理的示例代码:

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

interface Calculator {
    int add(int a, int b);
}

class CalculatorImpl implements Calculator {
    @Override
    public int add(int a, int b) {
        return a + b;
    }
}

class LoggingHandler implements InvocationHandler {
    private final Object target;

    public LoggingHandler(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;
    }
}

public class DynamicProxyExample {
    public static void main(String[] args) {
        Calculator calculator = new CalculatorImpl();

        Calculator proxy = (Calculator) Proxy.newProxyInstance(
                calculator.getClass().getClassLoader(),
                calculator.getClass().getInterfaces(),
                new LoggingHandler(calculator));

        int result = proxy.add(2, 3);
        System.out.println("结果:" + result);
    }
}

上述示例中,我们使用动态代理技术,在代码运行过程中创建代理类,对代理对象进行增强。