当前位置:首页 > CN2资讯 > 正文内容

JDK动态代理与CGLIB动态代理:原理、实现与对比

9小时前CN2资讯

一、动态代理概述

在Java开发中,代理模式是一种常用的设计模式,它允许我们为其他对象提供一个代理以控制对这个对象的访问。动态代理则是在运行时动态创建代理类及其对象的技术,无需手动编写代理类代码。Java中主要有两种动态代理实现方式:JDK动态代理和CGLIB动态代理。

二、JDK动态代理

1. 基本原理

JDK动态代理是Java原生支持的代理技术,基于接口实现。它主要依赖于java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。

核心特点:

  • 只能代理实现了接口的类
  • 通过反射机制实现
  • JDK原生支持,无需额外依赖

2. 实现案例

假设我们有一个用户服务接口和实现类:

// 用户服务接口 public interface UserService { void addUser(String username); void deleteUser(String username); } // 用户服务实现 public class UserServiceImpl implements UserService { @Override public void addUser(String username) { System.out.println("添加用户: " + username); } @Override public void deleteUser(String username) { System.out.println("删除用户: " + username); } }

创建代理处理器:

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class UserServiceInvocationHandler implements InvocationHandler { private Object target; // 被代理的对象 public UserServiceInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("【前置处理】方法 " + method.getName() + " 开始执行"); long start = System.currentTimeMillis(); // 执行目标方法 Object result = method.invoke(target, args); long end = System.currentTimeMillis(); System.out.println("【后置处理】方法 " + method.getName() + " 执行完成,耗时: " + (end - start) + "ms"); return result; } }

创建代理对象:

import java.lang.reflect.Proxy; public class JdkProxyDemo { public static void main(String[] args) { // 创建目标对象 UserService userService = new UserServiceImpl(); // 创建代理处理器 UserServiceInvocationHandler handler = new UserServiceInvocationHandler(userService); // 创建代理对象 UserService proxy = (UserService) Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), handler ); // 通过代理对象调用方法 proxy.addUser("张三"); proxy.deleteUser("李四"); } }

输出结果:

【前置处理】方法 addUser 开始执行 添加用户: 张三 【后置处理】方法 addUser 执行完成,耗时: 1ms 【前置处理】方法 deleteUser 开始执行 删除用户: 李四 【后置处理】方法 deleteUser 执行完成,耗时: 0ms

三、CGLIB动态代理

1. 基本原理

CGLIB(Code Generation Library)是一个强大的高性能代码生成库,它可以在运行时扩展Java类和实现接口。与JDK动态代理不同,CGLIB通过继承目标类来实现代理,因此不需要目标类实现接口。

核心特点:

  • 可以代理普通类,不要求实现接口
  • 通过继承方式实现代理
  • 需要引入CGLIB依赖
  • 通过字节码技术生成子类

2. 实现案例

首先添加Maven依赖:

<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>

假设我们有一个没有实现接口的商品服务类:

public class ProductService { public void addProduct(String productName) { System.out.println("添加商品: " + productName); } public void deleteProduct(String productName) { System.out.println("删除商品: " + productName); } }

创建方法拦截器:

import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class ProductServiceMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("【CGLIB前置处理】方法 " + method.getName() + " 开始执行"); long start = System.currentTimeMillis(); // 调用父类(即目标类)的方法 Object result = proxy.invokeSuper(obj, args); long end = System.currentTimeMillis(); System.out.println("【CGLIB后置处理】方法 " + method.getName() + " 执行完成,耗时: " + (end - start) + "ms"); return result; } }

创建代理对象:

import net.sf.cglib.proxy.Enhancer; public class CglibProxyDemo { public static void main(String[] args) { // 创建增强器 Enhancer enhancer = new Enhancer(); // 设置父类(目标类) enhancer.setSuperclass(ProductService.class); // 设置回调(拦截器) enhancer.setCallback(new ProductServiceMethodInterceptor()); // 创建代理对象 ProductService proxy = (ProductService) enhancer.create(); // 通过代理对象调用方法 proxy.addProduct("iPhone 13"); proxy.deleteProduct("MacBook Pro"); } }

输出结果:

【CGLIB前置处理】方法 addProduct 开始执行 添加商品: iPhone 13 【CGLIB后置处理】方法 addProduct 执行完成,耗时: 1ms 【CGLIB前置处理】方法 deleteProduct 开始执行 删除商品: MacBook Pro 【CGLIB后置处理】方法 deleteProduct 执行完成,耗时: 0ms

四、JDK动态代理与CGLIB动态代理的对比

特性

JDK动态代理

CGLIB动态代理

实现机制

基于接口,使用反射API

基于继承,使用字节码技术

目标类要求

必须实现至少一个接口

可以是普通类,不需要实现接口

性能

相对较慢

相对较快

依赖

JDK原生支持

需要引入CGLIB库

方法调用

通过反射调用

通过FastClass机制直接调用

代理类生成方式

运行时生成接口的实现类

运行时生成目标类的子类

final方法处理

无影响

无法代理final方法

创建速度

创建较快

创建较慢(需要生成字节码)

调用速度

调用较慢

调用较快

五、性能对比测试

让我们通过一个简单的性能测试来比较两种代理方式的差异:

public class ProxyPerformanceTest { public static void main(String[] args) { int count = 1000000; // JDK动态代理测试 UserService userService = new UserServiceImpl(); UserService jdkProxy = (UserService) Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), new UserServiceInvocationHandler(userService) ); long jdkStart = System.currentTimeMillis(); for (int i = 0; i < count; i++) { jdkProxy.addUser("testUser"); } long jdkEnd = System.currentTimeMillis(); System.out.println("JDK动态代理耗时: " + (jdkEnd - jdkStart) + "ms"); // CGLIB动态代理测试 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(ProductService.class); enhancer.setCallback(new ProductServiceMethodInterceptor()); ProductService cglibProxy = (ProductService) enhancer.create(); long cglibStart = System.currentTimeMillis(); for (int i = 0; i < count; i++) { cglibProxy.addProduct("testProduct"); } long cglibEnd = System.currentTimeMillis(); System.out.println("CGLIB动态代理耗时: " + (cglibEnd - cglibStart) + "ms"); } }

典型测试结果(100万次调用):

JDK动态代理耗时: 342ms CGLIB动态代理耗时: 198ms

六、使用场景建议

  • 使用JDK动态代理的场景
    • 目标对象已经实现了接口
    • 系统对性能要求不是特别高
    • 希望减少外部依赖
    • Spring AOP默认对接口实现使用JDK动态代理
  • 使用CGLIB动态代理的场景
    • 目标对象没有实现任何接口
    • 需要代理普通类
    • 对性能有较高要求
    • 需要代理的方法不是final方法
    • Spring AOP默认对非接口实现使用CGLIB

    七、Spring框架中的选择

    在Spring框架中,动态代理的选择遵循以下规则:

  • 如果目标对象实现了接口,默认使用JDK动态代理
  • 如果目标对象没有实现接口,使用CGLIB动态代理
  • 可以通过配置强制使用CGLIB:
  • <aop:config proxy-target-> <!-- other beans defined here... --> </aop:config>
  • 或使用注解:
  • @EnableAspectJAutoProxy(proxyTargetClass = true)

    八、总结

    JDK动态代理和CGLIB动态代理各有优缺点,在实际开发中应根据具体需求选择合适的代理方式。理解它们的实现原理和差异,有助于我们在设计系统时做出更合理的选择,也能在面试中更好地展示我们的技术深度。

    对于大多数现代应用,性能差异已经不再是决定性因素,代码的可维护性和清晰度往往更为重要。Spring等框架已经为我们做好了默认选择,但在需要定制化代理行为或解决特定问题时,深入理解这些技术就显得尤为重要了。

      你可能想看:

      扫描二维码推送至手机访问。

      版权声明:本文由皇冠云发布,如需转载请注明出处。

      本文链接:https://www.idchg.com/info/25045.html

      分享给朋友:

      “JDK动态代理与CGLIB动态代理:原理、实现与对比” 的相关文章

      探索VPS论坛:获取信息与技术交流的最佳平台

      在当今的数字时代,VPS(虚拟专用服务器)论坛扮演着一个重要的角色。作为技术爱好者和学习者交流的平台,这些论坛不仅提供了丰富的信息资源,还促成了用户之间的互动。我最喜欢的就是能在这样的社区中找到志同道合的朋友,讨论各种技术问题和经验。 VPS论坛的定义和特点非常明确。它们通常是一个集中讨论虚拟专用服...

      eno VPS:掌握网络接口命名规则与性能优化技巧

      在了解eno VPS之前,我们先来看看什么是eno命名规则。ena作为一种网络接口命名方式,通过特定的规则来表示Linux系统中的网络设备。这种规则帮助用户更容易地识别和管理各种网络接口。具体来说,eno采用的是eno[n|d]的格式,主要用于板载设备。而对于热插拔设备,则使用ens[f][n|d]...

      2024年如何获取免费VPS服务:开发者的最佳选择

      在解释什么是免费VPS之前,我想先来聊聊“VPS”这个概念。虚拟专用服务器(VPS)可以理解为一种在服务器上创建多个虚拟环境的技术。这些环境如同独立的服务器,用户可以在上面进行程序的开发和测试。而“免费VPS”则意味着用户可以在一定的限度内,无需付费地使用这些虚拟环境。对于初创公司或个人开发者而言,...

      Atlantic VPS:高性能、灵活性与安全性的理想选择

      什么是Atlantic VPS? Atlantic VPS由Atlantic.net提供,这是一家在VPS托管服务领域中的资深者,已经经营了近29年。创建之初,Atlantic.net就定位于高性能和灵活性,以满足企业和开发者日益增长的需求。他们的目标是提供一种可靠的解决方案,让用户在自己的业务上更...

      PacificRack低价VPS服务评测与用户体验分析

      在云计算和虚拟主机服务日益普及的今天,PacificRack作为QuadraNET旗下的全资子品牌,逐渐在低价VPS市场中崭露头角。它的主要定位是为那些对性能要求不高,且对价格敏感的用户提供解决方案。PacificRack通过严格的资源管理,致力于为用户提供一种经济实惠的选择,适合希望以最低成本体验...

      如何有效购买和使用代理IP服务,满足多种需求 - IP够卖指南

      在我们开始讨论“IP够卖”的购买与应用流程之前,理解购买渠道的多样性非常重要。很多人可能会问,究竟从哪里可以获取到合适的代理IP?实际上,有几种渠道可以选择,适合不同需求的用户。 首先,专业代理服务商无疑是最推荐的选择。这类服务商通常提供稳定且高质量的代理服务,能满足诸多使用场景,比如数据爬虫、电商...