AOP中的JDK动态代理与CGLIB动态代理:深度解析与实战模拟

简介: 【11月更文挑战第21天】面向切面编程(AOP,Aspect-Oriented Programming)是一种编程范式,它通过将横切关注点(cross-cutting concerns)与业务逻辑分离,以提高代码的可维护性和可重用性。在Java开发中,AOP的实现离不开动态代理技术,其中JDK动态代理和CGLIB动态代理是两种常用的方式。本文将从背景、历史、功能点、业务场景、底层逻辑等多个维度,深度解析这两种代理方式的区别,并通过Java示例进行模拟和比较。

引言

面向切面编程(AOP,Aspect-Oriented Programming)是一种编程范式,它通过将横切关注点(cross-cutting concerns)与业务逻辑分离,以提高代码的可维护性和可重用性。在Java开发中,AOP的实现离不开动态代理技术,其中JDK动态代理和CGLIB动态代理是两种常用的方式。本文将从背景、历史、功能点、业务场景、底层逻辑等多个维度,深度解析这两种代理方式的区别,并通过Java示例进行模拟和比较。

一、背景与历史

1.1 AOP的起源与发展

AOP的概念最早由Gregor Kiczales等人在1997年提出,旨在解决面向对象编程(OOP)中横切关注点(如日志记录、事务管理、安全控制等)与业务逻辑混合导致代码臃肿、难以维护的问题。AOP通过定义一个或多个切面(Aspect),将横切关注点从业务逻辑中分离出来,形成独立的模块,并通过动态代理技术在运行时将这些切面织入(Weaving)到目标对象中。

1.2 动态代理技术的发展

在Java中,动态代理技术是实现AOP的关键。Java的动态代理机制最早出现在JDK 1.3中,当时只支持基于接口的代理。随着Java的发展,动态代理技术也得到了不断的完善和优化。JDK 5引入了基于InvocationHandler和Proxy类的动态代理机制,使得开发者可以在运行时动态地创建代理对象,而无需手动编写代理类代码。而CGLIB(Code Generation Library)则提供了一种更为灵活的代理方式,它通过生成目标类的子类来实现代理,从而支持了对未实现接口的类的代理。

二、功能点对比

2.1 JDK动态代理

JDK动态代理是Java语言提供的一种基于接口的代理机制。它要求被代理的类必须实现至少一个接口,代理对象将实现这些接口,并将方法调用委托给目标对象。JDK动态代理主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口实现。

  • 核心接口InvocationHandler
  • 核心类Proxy
  • 特点
  • 只能代理实现了接口的类。
  • 创建代理对象时开销较小,但方法调用时性能略慢(因为涉及反射调用)。

2.2 CGLIB动态代理

CGLIB动态代理是一种基于类的代理机制,它通过生成目标类的子类来实现代理。与JDK动态代理不同,CGLIB不要求目标类必须实现接口,因此它适用于那些没有实现接口的类。CGLIB动态代理使用的是ASM字节码生成框架,通过修改目标类的字节码来生成代理类。

  • 核心接口MethodInterceptor
  • 核心类Enhancer
  • 特点
  • 可以代理未实现接口的类。
  • 生成代理类的开销较大,但方法调用时性能较高(因为通过字节码操作减少了反射调用的开销)。
  • 不能代理final类或final方法(因为CGLIB通过继承方式创建代理类)。

三、业务场景对比

3.1 JDK动态代理的应用场景

  • 接口驱动的编程:在接口驱动的编程模式中,业务逻辑通常是通过接口定义的。如果目标类实现了接口,那么使用JDK动态代理是首选方式。
  • 频繁创建和销毁代理对象:由于JDK动态代理在创建代理对象时开销较小,因此适合在需要频繁创建和销毁代理对象的场景中使用。
  • 性能要求不是特别高的场景:虽然JDK动态代理在方法调用时性能略慢于CGLIB动态代理,但在一些性能要求不是特别高的场景中,其性能差异是可以接受的。

3.2 CGLIB动态代理的应用场景

  • 代理未实现接口的类:如果目标类没有实现任何接口,但你又想对其进行代理以增强其功能或进行横切关注点管理,那么CGLIB动态代理是一个不错的选择。
  • 性能要求较高的场景:由于CGLIB动态代理在方法调用时性能较高,因此适合在性能要求较高的场景中使用。
  • 无法修改目标类:在某些情况下,你可能无法修改目标类的源代码(例如,目标类是第三方库中的类)。此时,你可以使用CGLIB动态代理来增强其功能而无需修改其源代码。

四、底层逻辑解析

4.1 JDK动态代理的底层逻辑

JDK动态代理的底层逻辑主要依赖于反射机制和InvocationHandler接口。当调用代理对象的方法时,代理类会拦截该方法调用,并通过InvocationHandler.invoke()方法执行额外的逻辑。具体步骤如下:

  1. 创建代理对象:使用Proxy.newProxyInstance()方法创建代理对象。该方法需要三个参数:类加载器、目标类实现的接口列表和一个实现了InvocationHandler接口的处理器对象。
  2. 拦截方法调用:当调用代理对象的方法时,代理类会拦截该方法调用,并将调用转发给InvocationHandlerinvoke()方法。
  3. 执行额外逻辑:在invoke()方法中,可以执行额外的逻辑(如日志记录、权限检查等),然后通过反射调用目标对象的方法来完成实际的业务逻辑。

4.2 CGLIB动态代理的底层逻辑

CGLIB动态代理的底层逻辑主要依赖于字节码生成技术和ASM框架。CGLIB通过生成目标类的子类来实现代理,并在子类中重写目标类的方法以插入额外的逻辑。具体步骤如下:

  1. 创建Enhancer对象Enhancer是CGLIB中用于创建代理类的主要类。通过调用其setSuperclass()方法设置目标类的超类。
  2. 设置回调逻辑:通过调用EnhancersetCallback()方法设置一个实现了MethodInterceptor接口的回调对象。该回调对象将负责拦截方法调用并执行额外的逻辑。
  3. 生成代理类:调用Enhancercreate()方法生成代理类的实例。在生成代理类的过程中,CGLIB会使用ASM框架修改目标类的字节码以插入代理逻辑。
  4. 拦截方法调用:当调用代理对象的方法时,该方法调用将被拦截并转发到回调对象的intercept()方法中。在intercept()方法中,可以执行额外的逻辑(如日志记录、权限检查等),然后通过调用MethodProxy.invokeSuper()方法执行目标类的原始方法。

五、Java示例模拟

5.1 JDK动态代理示例

下面是一个使用JDK动态代理的示例代码:

java复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定义一个接口
interface MyService {
void serve();
}
// 实现该接口的目标类
class MyServiceImpl implements MyService {
public void serve() {
        System.out.println("Serving...");
    }
}
// 实现InvocationHandler接口的处理器类
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
    }
@Override
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 JdkProxyDemo {
public static void main(String[] args) {
MyServiceImpl service = new MyServiceImpl();
MyService proxy = (MyService) Proxy.newProxyInstance(
            service.getClass().getClassLoader(),
            service.getClass().getInterfaces(),
new MyInvocationHandler(service)
        );
        proxy.serve();
    }
}

在这个示例中,MyService是一个接口,MyServiceImpl实现了这个接口。MyInvocationHandler实现了InvocationHandler接口,并在invoke()方法中添加了额外的逻辑(打印方法调用前后的信息)。通过Proxy.newProxyInstance()方法创建了MyService接口的代理对象proxy,并调用了其serve()方法。输出结果为:

复制代码
Before method: serve
Serving...
After method: serve

5.2 CGLIB动态代理示例

下面是一个使用CGLIB动态代理的示例代码:

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 MyService {
public void serve() {
        System.out.println("Serving...");
    }
}
// 实现MethodInterceptor接口的处理器类
class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
        System.out.println("After method: " + method.getName());
return result;
    }
}
// 测试类
public class CglibProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(MyService.class);
        enhancer.setCallback(new MyMethodInterceptor());
MyService proxy = (MyService) enhancer.create();
        proxy.serve();
    }
}

在这个示例中,MyService类没有实现任何接口。MyMethodInterceptor实现了MethodInterceptor接口,并在intercept()方法中添加了额外的逻辑(打印方法调用前后的信息)。通过Enhancer类创建了MyService类的代理对象proxy,并调用了其serve()方法。输出结果为:

复制代码
Before method: serve
Serving...
After method: serve

六、总结与对比

6.1 总结

  • JDK动态代理:适用于接口驱动的编程模式,创建代理对象时开销较小,但方法调用时性能略慢。
  • CGLIB动态代理:适用于未实现接口的类,创建代理对象时开销较大,但方法调用时性能较高。

6.2 对比

JDK动态代理 CGLIB动态代理
基于 接口
实现机制 反射机制 + InvocationHandler接口 字节码生成技术 + ASM框架
代理对象创建开销 较小 较大
方法调用性能 略慢(涉及反射调用) 较高(通过字节码操作减少反射调用开销)
适用场景 接口驱动的编程模式,频繁创建和销毁代理对象的场景 代理未实现接口的类,性能要求较高的场景
限制 只能代理实现了接口的类 不能代理final类或final方法

通过本文的深度解析和实战模拟,相信读者已经对JDK动态代理和CGLIB动态代理有了更深入的理解。在实际开发中,可以根据具体的业务场景和性能需求选择合适的代理方式来实现AOP。

相关文章
|
2月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
7月前
|
人工智能 API 开发者
HarmonyOS Next~鸿蒙应用框架开发实战:Ability Kit与Accessibility Kit深度解析
本书深入解析HarmonyOS应用框架开发,聚焦Ability Kit与Accessibility Kit两大核心组件。Ability Kit通过FA/PA双引擎架构实现跨设备协同,支持分布式能力开发;Accessibility Kit提供无障碍服务构建方案,优化用户体验。内容涵盖设计理念、实践案例、调试优化及未来演进方向,助力开发者打造高效、包容的分布式应用,体现HarmonyOS生态价值。
329 27
|
7月前
|
数据采集 JSON 数据可视化
JSON数据解析实战:从嵌套结构到结构化表格
在信息爆炸的时代,从杂乱数据中提取精准知识图谱是数据侦探的挑战。本文以Google Scholar为例,解析嵌套JSON数据,提取文献信息并转换为结构化表格,通过Graphviz制作技术关系图谱,揭示文献间的隐秘联系。代码涵盖代理IP、请求头设置、JSON解析及可视化,提供完整实战案例。
436 4
JSON数据解析实战:从嵌套结构到结构化表格
|
7月前
|
Java Spring
JDK动态代理和CGLIB动态代理的区别
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理: ● JDK动态代理只提供接口的代理,不支持类的代理Proxy.newProxyInstance(类加载器, 代理对象实现的所有接口, 代理执行器) ● CGLIB是通过继承的方式做的动态代理 , 如果某个类被标记为final,那么它是无法使用 CGLIB做动态代理的。Enhancer.create(父类的字节码对象, 代理执行器)
|
7月前
|
数据采集 机器学习/深度学习 存储
可穿戴设备如何重塑医疗健康:技术解析与应用实战
可穿戴设备如何重塑医疗健康:技术解析与应用实战
238 4
|
7月前
|
机器学习/深度学习 人工智能 Java
Java机器学习实战:基于DJL框架的手写数字识别全解析
在人工智能蓬勃发展的今天,Python凭借丰富的生态库(如TensorFlow、PyTorch)成为AI开发的首选语言。但Java作为企业级应用的基石,其在生产环境部署、性能优化和工程化方面的优势不容忽视。DJL(Deep Java Library)的出现完美填补了Java在深度学习领域的空白,它提供了一套统一的API,允许开发者无缝对接主流深度学习框架,将AI模型高效部署到Java生态中。本文将通过手写数字识别的完整流程,深入解析DJL框架的核心机制与应用实践。
373 3
|
7月前
|
监控 Java API
JDK动态代理和CGLIB动态代理
Java动态代理允许在运行时创建代理对象,增强或拦截目标类方法的执行。主要通过两种方式实现:JDK动态代理和CGLIB动态代理。JDK动态代理基于接口,利用`java.lang.reflect.Proxy`类和`InvocationHandler`接口;CGLIB则通过字节码技术生成目标类的子类作为代理,适用于未实现接口的类。两者均用于在方法执行前后添加额外逻辑,如日志记录、权限控制等,广泛应用于AOP框架中。
215 2
|
7月前
|
缓存 监控 搜索推荐
【实战解析】smallredbook.item_get_video API:小红书视频数据获取与电商应用指南
本文介绍小红书官方API——`smallredbook.item_get_video`的功能与使用方法。该接口可获取笔记视频详情,包括无水印直链、封面图、时长、文本描述、标签及互动数据等,并支持电商场景分析。调用需提供`key`、`secret`和`num_iid`参数,返回字段涵盖视频链接、标题、标签及用户信息等。同时,文章提供了电商实战技巧,如竞品监控与个性化推荐,并列出合规注意事项及替代方案对比。最后解答了常见问题,如笔记ID获取与视频链接时效性等。
|
3月前
|
存储 Ubuntu 安全
在Ubuntu 16.04上安装openjdk-6/7/8-jdk的步骤
在整个安装过程中,你可能需要管理员权限,因此你可能要使用 `sudo` 来获取必要的权限。记得做完每一个步骤后,都要检查输出,以确保没有发生错误,并且每项操作都成功完成。如果在安装过程中遇到问题,查看 `/var/log/` 下的日志文件对于问题的解决可能是有帮助的。
166 21
|
3月前
|
IDE Ubuntu Java
在Ubuntu18.04安装兼容JDK 8的Eclipse集成开发环境的指南。
完成以上步骤后,您将在Ubuntu 18.04系统上成功安装并配置了Eclipse IDE,它将与JDK 8兼容,可以开始进行Java开发工作。如果遇到任何问题,请确保每一步骤都正确执行,并检查是否所有路径都与您的具体情况相匹配。
123 11

推荐镜像

更多
  • DNS