ChatGPT解决这个技术问题 Extra ChatGPT

JDK动态代理和CGLib有什么区别?

如果是Proxy Design Pattern,那么JDK's Dynamic ProxyCGLib 等第三方动态代码生成API 有什么区别?

使用这两种方法有什么区别,什么时候应该更喜欢一种方法?

在此处获取代码:<gist.github.com/ksauzz/1563486>。在 cglib 中,您可以创建类代理和接口代理。 Spring 默认使用 CGlib,而 AspectJ 使用 Java 代理。也请阅读:jnb.ociweb.com/jnb/jnbNov2005.html ;)
@SubhadeepRay,我知道你的评论很老了,但我今天才看到。很抱歉纠正您,但您的说法“Spring 默认使用 CGlib 而 AspectJ 使用 Java 代理”是错误的,原因有两个:(1)AspectJ 根本不使用任何代理。 (2) Spring Core 默认使用 JDK 代理作为接口,CGLIB 代理作为类。或者,它也可以将 CGLIB 用于接口。由于配置类的不幸组合,Spring Boot 似乎总是使用 CGLIB,但 Boot 不是 Spring Core。

r
raphaëλ

JDK动态代理只能通过接口代理(所以你的目标类需要实现一个接口,然后代理类也实现)。

CGLIB(和 javassist)可以通过子类化创建代理。在这种情况下,代理成为目标类的子类。不需要接口。

所以 Java 动态代理可以代理:public class Foo implements iFoo CGLIB 可以代理:public class Foo

编辑:

我应该提一下,因为 javassist 和 CGLIB 通过子类化使用代理,这就是在使用依赖于此的框架时不能声明最终方法或使类成为最终方法的原因。这将阻止这些库允许子类化您的类并覆盖您的方法。


谢谢..!!但是,如果您能给我一个示例代码(或链接)来说明在某些情况下对另一个的用法,那将会很有帮助..!!!
请注意,JDK 代理实际上是为 IFoo 而不是任何类型的 Foo 的代理。这是一个相当重要的区别。此外,cglib 代理是完整的子类——利用它!使用过滤器仅代理您关心的方法并直接使用生成的类。
还应该注意的是,创建 CGLib 子类需要对超类有足够的了解,以便能够使用正确的 args 调用正确的构造函数。与不关心构造函数的基于接口的代理不同。这使得 CGLib 代理的使用不如 JDK 代理那么“自动”。另一个区别在于“堆栈”成本。 JDK 代理每次调用总是会产生额外的堆栈帧,而 CGLib 可能不会花费任何额外的堆栈帧。应用程序越复杂,这一点就越重要(因为堆栈越大,内存线程消耗的越多)。
cglib 不能代理 final 方法,但不会抛出异常 gist.github.com/mhewedy/7345403cfa52e6f47563f8a204ec0e80
是的,CGLIB 只是忽略了 final 方法。
R
Rafael Winterhalter

功能差异

JDK 代理允许在继承 Object 的同时实现任何一组接口。然后将任何接口方法 plusObject::hashCode、Object::equals 和 Object::toString 转发到 InvocationHandler。此外,还实现了标准库接口 java.lang.reflect.Proxy。

cglib 允许您在继承任何非最终类的同时实现任何一组接口。此外,可以选择性地覆盖方法,即并非所有非抽象方法都需要被截取。此外,存在实现方法的不同方式。它还提供了一个 InvocationHandler 类(在不同的包中),但它也允许通过使用更高级的拦截器(例如 MethodInterceptor)来调用超级方法。此外,cglib 可以通过像 FixedValue 这样的专门拦截来提高性能。我曾经为 cglib 写过不同拦截器的总结。

性能差异

JDK 代理的实现相当简单,只有一个拦截调度程序 InvocationHandler。这需要一个虚拟方法分派到一个不能总是内联的实现。 Cglib 允许创建有时可以提高性能的专用字节码。以下是使用 18 个存根方法实现接口的一些比较:

            cglib                   JDK proxy
creation    804.000     (1.899)     973.650     (1.624)
invocation    0.002     (0.000)       0.005     (0.000)

时间以纳秒为单位,括号中为标准偏差。您可以在 Byte Buddy's tutorial 中找到有关基准测试的更多详细信息,其中 Byte Buddy 是 cglib 的更现代替代品。另外,请注意 cglib 不再处于积极开发中。


鉴于后者的性能优势,为什么 Spring 文档偏爱 JDK 代理而不是 cglib? docs.spring.io/spring/docs/2.5.x/reference/…
Cglib 是一个外部依赖项,目前不受支持。依赖第三方软件总是一场赌博,所以当尽可能少的人依赖它时,它是最好的。
在您的博客中您说:“但是,在调用 InvocationHandler#invoke 方法附带的代理对象上的方法时应该小心。对此方法的所有调用都将使用相同的 InvocationHandler 进行调度,因此可能会导致无限循环。”你是什么意思?
如果您调用代理对象上的方法,任何调用都将通过我们的调用处理程序进行路由。如果任何调用处理程序调用委托给对象的调用,就会发生上述递归。
嗨,Rafael,与您的回答无关的消息,我正在向您发送有关 an edit made 5 years ago 的信息。由于 cglib 显然在 2019 年仍有提交,并且在其自述文件中没有显示任何 arrested development,因此我从标签摘录中removed your statement。如果有任何需要提及的内容,请随时改进标签描述/摘录。
P
Premraj

动态代理:使用 JDK 反射 API 在运行时动态实现接口。

示例:Spring 对事务使用动态代理,如下所示:

https://i.stack.imgur.com/CigPu.png

生成的代理位于 bean 之上。它为 bean 添加了跨国行为。这里代理使用 JDK 反射 API 在运行时动态生成。

当应用程序停止时,代理将被销毁,文件系统上将只有接口和 bean。

在上面的例子中,我们有接口。但是在大多数接口的实现中并不是最好的。所以 bean 没有实现接口,在这种情况下我们使用继承:

https://i.stack.imgur.com/fsOVh.png

为了生成这样的代理,Spring 使用了一个名为 CGLib 的第三方库。

CGLib(Code Generation Library)建立在ASM之上,主要用于生成代理扩展bean并添加代理方法中的 bean 行为。

Examples for JDK Dynamic proxy and CGLib

Spring ref


b
bernie

From Spring documentation

Spring AOP 使用 JDK 动态代理或 CGLIB 为给定的目标对象创建代理。 (当您有选择时,首选 JDK 动态代理)。如果要代理的目标对象至少实现一个接口,则将使用 JDK 动态代理。目标类型实现的所有接口都将被代理。如果目标对象没有实现任何接口,那么将创建一个 CGLIB 代理。如果您想强制使用 CGLIB 代理(例如,代理为目标对象定义的每个方法,而不仅仅是那些由其接口实现的方法),您可以这样做。但是,有一些问题需要考虑:不能建议最终方法,因为它们不能被覆盖。您将需要类路径中的 CGLIB 2 二进制文件,而动态代理可用于 JDK。当 Spring 需要 CGLIB 并且在类路径中找不到 CGLIB 库类时,Spring 会自动警告您。代理对象的构造函数将被调用两次。这是 CGLIB 代理模型的自然结果,其中为每个代理对象生成一个子类。对于每个代理实例,都会创建两个对象:实际的代理对象和实现通知的子类的实例。使用 JDK 代理时不会出现此行为。通常,调用代理类型的构造函数两次不是问题,因为通常只有赋值发生,构造函数中没有实现真正的逻辑。