Java代理模式

  • 代理模式:使用代理对象代替对真实对象的访问,可以在不修改真实对象的前提下提供额外功能。
  • 代理模式分类:
    • 静态代理
    • 动态代理

1. 静态代理

  • 静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的class文件。我们对每个对象的增强都是手动完成的(对每一个目标类的增强都需要单独写一个代理类),非常不灵活。

  • 实现步骤:

    • 定义一个接口及实现类
    • 创建一个代理类同样实现这个接口
    • 将目标对象注入进代理类,然后再代理类的对应方法调用目标类中的对应方法。
  • 简单来说:B是A的代理类,B中持有A的实例,并且B和A实现同一个接口。当调用B的方法时,B会在方法中调用A的方法。

  • 示例:

  1. 定义发送短信的接口
    1
    2
    3
    public interface SmsService {
    String send(String message);
    }
  2. 实现接口的类
    1
    2
    3
    4
    5
    6
    7
    public class SmsServiceImpl implements SmsService {
    @Override
    public String send(String message) {
    System.out.println("send message: " + message);
    return message;
    }
    }
  3. 代理类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class SmsServiceProxy implements SmsService {
    private final SmsService smsService;

    public SmsServiceProxy(SmsService smsService) {
    this.smsService = smsService;
    }

    @Override
    public String send(String message) {
    // 调用方法之前添加其他操作
    System.out.println("Before sending message");
    String result = smsService.send(message);
    // 调用方法之后添加其他操作
    System.out.println("After sending message");
    return result;
    }
    }
  4. 使用代理类
    1
    2
    3
    4
    5
    6
    7
    public class Main {
    public static void main(String[] args) {
    SmsService smsService = new SmsServiceImpl();
    SmsService proxy = new SmsServiceProxy(smsService);
    proxy.send("Hello, World!");
    }
    }
  5. 输出
    1
    2
    3
    Before sending message
    send message: Hello, World!
    After sending message

2. 动态代理

  • 动态代理是在运行时动态生成类字节码,并加载到JVM中。
  • Java提供了两种动态代理方式:
    • JDK动态代理:只能代理实现了接口的类。
    • CGLIB动态代理:可以代理没有实现接口的类。

2.1 JDK动态代理

  • JDK动态代理中的核心:

    • InvocationHandler接口:用于自定义代理逻辑。其定义了一个方法invoke(),用于处理代理实例上的方法调用。
    1
    2
    3
    4
    5
    6
    public interface InvocationHandler {
    // proxy: 动态生成的代理对象
    // method: 与代理类对象调用的方法相应
    // args: 调用method传入的参数
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    }
    • Proxy类:提供了创建动态代理实例的方法。其中使用频率最高的方法是newProxyInstance(),用于生成一个代理对象。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // loader: 类加载器用于加载代理对象
    // interfaces: 代理对象需要实现的接口列表
    // h: 实现了InvocationHandler接口的对象
    public static Object newProxyInstance(ClassLoader loader,
    Class<?>[] interfaces,
    InvocationHandler h)
    throws IllegalArgumentException
    {
    ......
    }
  • 动态代理过程:使用Proxy.newProxyInstance()创建代理对象,该代理对象在调用方法时会调用InvocationHandlerinvoke()方法,在invoke()方法中可以添加额外的逻辑(如日志、权限检查等)。

  • 实现步骤:

    • 定义一个接口类及其实现类
    • 自定义InvocationHandler并重写invoke方法,在invoke方法中我们会调用原生方法并自定义处理逻辑
    • 通过Proxy.newProxyInstance()方法创建代理对象
  • 示例:

  1. 定义发送短信的接口
    1
    2
    3
    public interface SmsService {
    String send(String message);
    }
  2. 实现接口
    1
    2
    3
    4
    5
    6
    7
    public class SmsServiceImpl implements SmsService {
    @Override
    public void send(String message) {
    System.out.println("send message: " + message);
    return message;
    }
    }
  3. 自定义JDK动态代理类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.InvocationTargetException;

    public class MyInvocationHandler implements InvocationHandler {
    private final Object target;

    // 构造方法
    public MyInvocationHandler(Object target) {
    this.target = target;
    }

    // 重写invoke方法,自定义处理逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 方法调用前的处理逻辑
    System.out.println("Before send message");
    Object result = method.invoke(target, args);
    // 方法调用后的处理逻辑
    System.out.println("After send message");
    return result;
    }
    }
  4. 获取代理对象的工厂类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class ProxyFactory {
    public static Object getProxy(Object target) {
    return Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new MyInvocationHandler(target)
    )
    }
    }
  5. 使用动态代理
    1
    2
    SmsService smsService = (SmsService)ProxyFactory.getProxy(new SmsServiceImpl());
    smsService.send("Hello, World!"); // 直接调用原生方法
  6. 输出结果:
    1
    2
    3
    Before send message
    send message: Hello, World!
    After send message

2.2 CGLIB动态代理

  • CGLIB(Code Generation Library)是一个功能强大的高性能代码生成库,可以在运行时动态生成被代理类的子类来实现代理。与JDK动态代理相比,CGLIB可以代理没有实现接口的类。

  • CGLIB核心:

    • MethodInterceptor接口:用于自定义代理逻辑。其定义了一个方法intercept(),用于拦截增强被代理类的方法。
    1
    2
    3
    4
    5
    6
    7
    public interface MethodInterceptor {
    // obj: 被代理的对象
    // method: 被拦截的方法
    // args: 方法入参
    // proxy: 用于调用原始方法
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;
    }
    • Enhancer类:用于创建代理对象。通过设置父类和回调函数来生成代理对象。
  • 实现步骤:

    1. 定义一个类
    2. 自定义MethodInterceptor并重写intercept方法,intercept方法和invoke方法类似
    3. 通过Enhancer类的create()创建代理类
  • 详见Java 代理模式详解

3. 静态代理和动态代理的区别

对比维度 静态代理(Static Proxy) 动态代理(Dynamic Proxy)
代理关系确定时机 编译时(编译后生成固定的.class字节码文件) 运行时
实现方式 对于每个目标类都需要一一实现对应的代理类 无需手动编写代理类,通过Handler/Intercept封装增强逻辑,一对多复用
接口依赖 必须实现接口 支持代理接口或直接代理实现类
代码量与维护性 代码量大,维护成本高,难以维护 代码量少,维护性好;与接口解耦
核心优势 实现简单,逻辑直观 灵活性强、代码复用性高
典型应用场景 简单的装饰器模式、少量固定类的增强需求 SpringAOP、RPC框架、ORM框架