高级java每日一道面试题-2024年9月18日-设计模式篇-JDK动态代理,CGLIB代理,AspectJ区别?
如果有遗漏,评论区告诉我进行补充
面试官: JDK动态代理,CGLIB代理,AspectJ区别?
我回答:
在Java开发中,代理(Proxy)是一种常用的设计模式,它允许开发者在不修改原有类代码的情况下,通过代理类来控制对原有类的访问,并可以添加额外的功能。Java提供了几种实现代理的方式,包括JDK动态代理、CGLIB代理和AspectJ(虽然AspectJ更多用于面向切面编程,但也可以看作是一种代理技术)。下面分别详细解释这三种代理技术。
1. JDK动态代理
原理:
- JDK动态代理主要利用
java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口来实现。它只能代理实现了接口的类。
使用场景
- 当目标对象实现了至少一个接口时。
- 需要对方法进行拦截和增强,但不需要修改原始代码。
步骤:
- 定义接口:定义业务逻辑接口。
- 实现接口:编写具体的业务逻辑实现类。
- 创建代理类:使用
Proxy.newProxyInstance()
方法创建代理实例,需要提供三个参数:- 类加载器(
ClassLoader
) - 代理类实现的接口列表
InvocationHandler
实例
- 类加载器(
- 实现
InvocationHandler
接口:在这个接口的invoke
方法中编写代理逻辑。
优点:
- 简单易用,不需要修改原有类的代码。
- 代理类是基于接口的,因此具有更好的解耦性。
缺点:
- 只能代理实现了接口的类。
- 相对于CGLIB,性能稍差(因为是通过反射实现的)。
2. CGLIB代理
原理:
- CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,它可以在运行时扩展Java类和实现接口。CGLIB通过继承被代理的类来创建动态代理。
使用场景
- 当目标对象没有实现任何接口时。
- 需要对方法进行拦截和增强,但不需要修改原始代码。
步骤:
- 引入CGLIB库。
- 创建一个实现了
MethodInterceptor
接口的类,并重写intercept
方法。 - 使用
Enhancer
类来创建代理对象,需要设置父类(即被代理的类)和MethodInterceptor
实例。
优点:
- 可以代理没有实现接口的类。
- 性能比JDK动态代理好(因为是通过继承实现的,避免了反射的开销)。
缺点:
- 由于是通过继承实现的,因此不能代理final类。
- 增加了类的复杂度,因为会生成大量的类文件。生成的代理类数量较多,可能会增加内存开销。
3. AspectJ
原理:
AspectJ是一个全面的AOP框架,提供了比Spring AOP更强大的AOP功能。AspectJ支持多种类型的切面,包括基于方法的、基于构造函数的、基于字段的等。AspectJ可以在编译时、类加载时或运行时织入切面。
使用场景
- 需要全面的AOP支持,包括对方法、构造函数、字段等的切面。
- 需要在编译时、类加载时或运行时织入切面。
实现方式
- 编译时织入:使用AspectJ编译器(ajc)在编译阶段织入切面。
- 类加载时织入:使用AspectJ的LTW(Load-Time Weaving)功能,在类加载时织入切面。
- 运行时织入:使用AspectJ的RTW(Runtime Weaving)功能,在运行时织入切面。
优点:
- 提供了全面的AOP支持。
- 支持多种织入时机,灵活性高。
- 性能较好,尤其是在编译时织入的情况下。
缺点:
- 学习曲线较陡峭。
- 需要引入额外的工具和配置。
- 在某些情况下可能会影响代码的可读性和维护性。
总结
- JDK动态代理适用于目标类实现了接口的情况,简单易用,但只能代理实现了接口的类。
- CGLIB代理适用于目标类没有实现接口的情况,性能较好,但需要引入额外的库,并且不能代理final类或final方法。
- AspectJ提供了最全面的AOP支持,适用于复杂的AOP需求,支持多种织入时机,但学习成本较高,配置也相对复杂。
在面试中,能够清晰地解释这三种代理机制的工作原理、适用场景以及各自的优缺点,将有助于展示你对AOP技术和代理模式的深入理解。此外,实际项目经验也是面试官非常看重的部分,因此准备好相关的项目案例来说明你的实践经历也是非常有帮助的。