java 代理

一、Java 中的动态代理类型

Java 中常见的动态代理有两种主要类型:

  1. JDK 动态代理
  2. CGLIB 动态代理

二、JDK 动态代理

✨ 实现方式:

使用 Java 标准库中的 java.lang.reflect.ProxyInvocationHandler

✅ 优点:

  • 不依赖第三方库。

  • 简单轻量,标准库支持。

  • 非常适合接口驱动设计的系统(如 DAO 接口)。

❌ 缺点:

  • 只能代理接口(不能代理普通类)。

✅ 使用场景:

  • 接口型编程,如 Spring 中的基于接口的 AOP。
  • MyBatis Mapper 接口。

流程图

sequenceDiagram participant Client as 客户端代码 participant Proxy as 动态代理对象 (proxy) participant Handler as InvocationHandler 实例 participant Target as 真实目标对象 Client->>Proxy: 调用 proxy.method() Proxy->>Handler: 调用 invoke(proxy, method, args) Handler->>Handler: 前置增强(日志、权限等) Handler->>Target: 反射调用目标方法 method.invoke(target, args) Target-->>Handler: 返回结果 Handler->>Handler: 后置增强(日志、事务等) Handler-->>Proxy: 返回结果 Proxy-->>Client: 返回最终结果### **Mermaid**

示例代码:

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 定义一个接口,表示服务的功能
interface UserService {
    // 接口方法:打招呼
    void sayHello(String name);
}

// 实现接口的类,提供具体的功能实现
class UserServiceImpl implements UserService {
    // 实现接口中的方法
    public void sayHello(String name) {
        System.out.println("Hello, " + name); // 打印问候语
    }
}

  
// InvocationHandler 的实现类,用于增强目标对象的方法
class LoggingHandler implements InvocationHandler {
    private final Object target; // 被代理的目标对象
    // 构造函数,接收目标对象
    public LoggingHandler(Object target) {
        this.target = target;
    }
    // invoke 方法会在代理对象调用方法时被触发
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在方法调用前打印日志信息
        System.out.println("Before method: " + method.getName()); // 打印方法名
        // 调用目标对象的实际方法,并传递参数
        Object result = method.invoke(target, args);
        // 在方法调用后打印日志信息
        System.out.println("After method: " + method.getName()); // 打印方法名
        return result; // 返回方法执行的结果
    }
}

// 主类,用于测试动态代理功能
public class Main {
    public static void main(String[] args) {
        // 创建目标对象(实际的服务实现)
        UserService realService = new UserServiceImpl();
        // 使用 Proxy.newProxyInstance 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            realService.getClass().getClassLoader(), // 目标对象的类加载器
            new Class[]{UserService.class},          // 目标对象实现的接口数组
            new LoggingHandler(realService)         // 自定义的 InvocationHandler 实现
        );
        // 调用代理对象的方法,实际会触发 LoggingHandler 中的 invoke 方法
        proxy.sayHello("Alice");
    }
}

输出结果

sql 复制代码
Before method: sayHello 
Hello, Alice 
After method: sayHello

三、CGLIB 动态代理

✨ 实现方式:

通过继承目标类,重写方法来实现代理。底层使用字节码生成技术(如 ASM)动态创建子类。

常用库:cglib(如在 Spring AOP 中用于非接口代理)。

✅ 优点:

  • 可以代理普通类(非接口)

  • 性能比 JDK 动态代理略高(尤其是大量方法调用场景)。

❌ 缺点:

  • 被代理类 不能是 final 类

  • 被代理方法 不能是 final/static/private

  • 依赖第三方库(如 CGLIB 或 ByteBuddy)。

✅ 使用场景:

  • 没有接口的类。
  • 需要代理已有类(如 Controller、Service 等 Bean)。

流畅图

sequenceDiagram participant Client as 客户端代码 participant Proxy as CGLIB 生成的子类对象 participant Interceptor as MethodInterceptor 实例 participant SuperClass as 目标类(被代理对象) Client->>Proxy: 调用 proxy.method() Proxy->>Interceptor: 调用 intercept(proxy, method, args, methodProxy) Interceptor->>Interceptor: 前置增强逻辑(日志、事务等) Interceptor->>SuperClass: methodProxy.invokeSuper(proxy, args) SuperClass-->>Interceptor: 返回原始方法执行结果 Interceptor->>Interceptor: 后置增强逻辑 Interceptor-->>Proxy: 返回最终结果 Proxy-->>Client: 返回最终结果

示例代码:

java 复制代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 目标类,不需要实现接口
class UserService {
    // 目标方法:打招呼
    public void sayHello(String name) {
        System.out.println("Hello, " + name); // 打印问候语
    }
}

// MethodInterceptor 的实现类,用于增强目标类的方法
class LoggingInterceptor implements MethodInterceptor {
    /**
     * intercept 方法会在代理对象调用方法时被触发
     * @param obj 代理对象本身
     * @param method 被拦截的方法对象
     * @param args 方法参数
     * @param proxy 方法代理对象,用于调用父类方法
     * @return 方法执行的结果
     * @throws Throwable 抛出异常
     */

    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 在方法调用前打印日志信息
        System.out.println("Before method: " + method.getName()); // 打印方法名
        // 使用 MethodProxy 调用父类(目标类)的实际方法
        Object result = proxy.invokeSuper(obj, args);
        // 在方法调用后打印日志信息
        System.out.println("After method: " + method.getName()); // 打印方法名
        return result; // 返回方法执行的结果
    }
}

// 主类,用于测试 CGLIB 动态代理功能
public class Main {
    public static void main(String[] args) {
        // 创建 Enhancer 对象,用于生成代理类
        Enhancer enhancer = new Enhancer();
        // 设置目标类作为父类,CGLIB 会动态生成该类的子类,注意这里是调用 invokeSuper 而不是 invoke,否则死循环,invokeSuper方法会直接调用目标类(即被代理的类)的方法实现,而不会再次触发代理逻辑。这是因为在 CGLIB 中,代理类是目标类的子类,`invokeSuper` 实际上调用了父类的方法
        enhancer.setSuperclass(UserService.class);
        // 设置回调函数,使用自定义的 MethodInterceptor 实现
        enhancer.setCallback(new LoggingInterceptor());
        // 创建代理对象,实际是目标类的子类实例
        UserService proxy = (UserService) enhancer.create();
        // 调用代理对象的方法,实际会触发 LoggingInterceptor 中的 intercept 方法
        proxy.sayHello("Bob");
    }
}

输出结果

sql 复制代码
Before method: sayHello 
Hello, Alice 
After method: sayHello

四、JDK 与 CGLIB 的对比总结

对比项 JDK 动态代理 CGLIB 动态代理
代理对象 接口
是否要求接口
底层实现 反射 + Proxy 字节码生成(ASM)
性能 较低 较高
限制 只能代理接口 类不能是 final,方法不能是 final/private ,通过继承
框架支持 Java 标准库 需引入第三方库
使用示例 MyBatis Mapper、Spring AOP Spring AOP、Hibernate、RPC 框架等

五、其他动态代理方式(了解)

  1. ByteBuddy

    • 更现代、灵活的字节码操作库(替代 CGLIB)。
    • 用于 JDK 代理或更复杂的类构建。
    • Spring Boot 2.x+ 使用 ByteBuddy 生成代理类。
  2. Javassist

    • 操作 Java 字节码,生成新类或修改已有类。
  3. ASM

    • 底层字节码编辑器,CGLIB 的基础。
    • 性能高,使用复杂,一般用于框架开发。

六、动态代理的常见使用场景总结

  • AOP 切面编程:日志、事务、权限校验。
  • 远程调用代理(RPC) :如 Dubbo。
  • 懒加载与缓存:Hibernate。
  • 拦截器链实现
  • Mock 框架:如 Mockito 生成 mock 对象。
相关推荐
蓝斯4974 小时前
[原创]《C#高级GDI+实战:从零开发一个流程图》第章:增加贝塞尔曲线,上、下、左、右连接点
java·c#·流程图
前端工作日常4 小时前
我学习到的Java 的 Service 分层:itf 和 impl 到底是什么?
java·后端
都叫我大帅哥5 小时前
Spring @Transactional 注解完全指南
java·spring
Java成神之路-5 小时前
G1 垃圾回收器 :SATB + TAMS核心机制深度解析
java·jvm
前端工作日常5 小时前
我学习到的JIT即时编译与机器码缓存失效
java·后端
带刺的坐椅5 小时前
Solon 的 10 种 HTTP 服务器:改一行依赖,换一个引擎
java·solon·jetty·undertow·mcp-server·htttp
犀利豆5 小时前
Claude Code Tools 研究系列(三)ExitPlanMode
人工智能·后端
星栈6 小时前
oh-my-pi工程级AI编码工使用体验
人工智能·后端·agent
xcLeigh6 小时前
Go入门:变量声明的五种方式详解
java·开发语言·golang
AI大模型-小华6 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro