Java 动态代理技术

动态代理技术主要有两种实现:JDK、CGLib

特点:无侵入式地给代码增加额外的功能

  • JDK 动态代理只能代理接口
  • 如果需要代理类而非接口,需要使用 CGLIB 等第三方库
  • 动态代理是实现 AOP(面向切面编程)的基础技术之一

1、JDK 实现

特点:需要被代理对象是某个接口的实现类

实现核心:新建代理对象Proxy.newProxyInstance(),这是一个静态方法

获取某个对象的接口还可以通过:obj.getClass().getInterfaces()

① 定义接口(包含多个方法)

interface Subject {

    void sayHello(String name);

    int add(int a, int b);

    String concatenate(String s1, String s2);
}

② 接口实现类

class RealSubject implements Subject {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello, " + name + "!");
    }

    @Override
    public int add(int a, int b) {
        return a + b;
    }

    @Override
    public String concatenate(String s1, String s2) {
        return s1 + s2;
    }
}

③ 自定义 InvocationHandler(代理逻辑)

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

class MyInvocationHandler implements InvocationHandler {
    private final Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result;

        // 前置处理
        System.out.println("Before calling method: " + method.getName());

        // 根据方法名执行不同的逻辑
        if (method.getName().equals("sayHello")) {
            System.out.println("Executing sayHello with name: " + args[0]);
        } else if (method.getName().equals("add")) {
            System.out.println("Adding " + args[0] + " and " + args[1]);
        } else if (method.getName().equals("concatenate")) {
            System.out.println("Concatenating " + args[0] + " and " + args[1]);
        }

        // 调用实际方法
        result = method.invoke(target, args);

        // 后置处理
        System.out.println("After calling method: " + method.getName());
        System.out.println();

        return result;
    }
}

④ 测试类

public class DynamicProxyDemo {
    public static void main(String[] args) {
        // 创建真实对象
        Subject realSubject = new RealSubject();

        // 创建InvocationHandler
        InvocationHandler handler = new MyInvocationHandler(realSubject);

        // 创建代理对象
        Subject proxySubject = (Subject) java.lang.reflect.Proxy.newProxyInstance(
            Subject.class.getClassLoader(),
            new Class<?>[]{Subject.class},
            handler
        );

        // 调用多个方法
        proxySubject.sayHello("Alice");
        int sum = proxySubject.add(3, 5);
        String combined = proxySubject.concatenate("Java", "Proxy");

        System.out.println("Add result: " + sum);
        System.out.println("Concatenated result: " + combined);
    }
}

⑤ 运行结果

Before calling method: sayHello
Executing sayHello with name: Alice
Hello, Alice!
After calling method: sayHello

Before calling method: add
Adding 3 and 5
After calling method: add

Before calling method: concatenate
Concatenating Java and Proxy
After calling method: concatenate

Add result: 8
Concatenated result: JavaProxy

动态代理同时实现多个接口:

特性说明
多接口支持通过 Proxy.newProxyInstance() 的第二个参数传入多个接口
方法拦截统一所有接口的方法调用都会经过 invoke() 方法
接口识别可通过 method.getDeclaringClass() 判断方法来源接口

2、CGLib 实现

特点:实现对没有接口的类的代理,是 Spring AOP 在目标类无接口时的底层实现方式之一

① 引入 CGLIB 依赖 

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

② 定义目标类(没有接口)

public class RealService {
    public void doSomething() {
        System.out.println("RealService: Performing action");
    }

    public String process(String input) {
        System.out.println("Processing input: " + input);
        return "Processed: " + input;
    }
}

③ 实现 MethodInterceptor(代理逻辑)

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class MyMethodInterceptor implements MethodInterceptor {

    private final Object target;

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

    // Object obj, 代理对象自己
    // Method method, 当前代理对象执行的方法
    // Object[] args, 方法执行时的入参
    // MethodProxy proxy 方法对象,和 method 有区别
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 前置增强
        System.out.println("Before method: " + method.getName());

        // 执行目标方法
        Object result = proxy.invoke(target, args); // 或 method.invoke(target, args)

        // 后置增强
        System.out.println("After method: " + method.getName());
        System.out.println();

        return result;
    }
}

为什么是:或 method.invoke(target, args) 有什么区别?

特性method.invokeproxy.invoke(Spring 用这种)
底层实现Java 反射CGLIB 快速调用(FastClass/FastMethod)
性能较慢快(通常反射的 20–50 倍)
使用场景任意反射场合仅限 CGLIB 代理场景
代码依赖无额外依赖依赖 CGLIB 生成的 MethodProxy
可读性直观略显“黑盒”

④ 创建代理对象并测试

import net.sf.cglib.proxy.Enhancer;

public class CglibProxyDemo {
    public static void main(String[] args) {
        // 创建目标对象
        RealService realService = new RealService();

        // 创建 Enhancer 对象
        Enhancer enhancer = new Enhancer();

        // 设置目标类
        enhancer.setSuperclass(RealService.class);

        // 设置 MethodInterceptor
        enhancer.setCallback(new MyMethodInterceptor(realService));

        // 创建代理对象
        RealService proxyService = (RealService) enhancer.create();

        // 调用方法
        proxyService.doSomething();
        String result = proxyService.process("Hello");
        System.out.println("Result: " + result);
    }
}

⑤ 输出结果

Before method: doSomething
RealService: Performing action
After method: doSomething

Before method: process
Processing input: Hello
After method: process

Result: Processed: Hello

总结:CGLIB 的特点

特性说明
代理方式通过生成目标类的 子类 实现代理
接口要求无需接口,可代理具体类
方法限制不能代理 final 类或 final 方法
性能略优于 JDK 动态代理(早期版本),JDK 1.8 后两者性能差距缩小
典型使用场景Spring AOP(当目标类无接口时)、工具类、POJO
底层实现使用 ASM 字节码操作库生成字节码

注意事项:

  • 目标类不能是 final:CGLIB 通过继承实现代理,final 类无法被继承。
  • 目标方法不能是 finalfinal 方法无法被重写,CGLIB 无法代理。

静态代理,是通过继承方式调用被代理对象,是一种编译期就生成字节码的方式。而动态代理是在运行时动态生成字节码的。

This article was updated on