简体   繁体   English

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

[英]What is the difference between JDK dynamic proxy and CGLib?

In case of the Proxy Design Pattern , What is the difference between JDK's Dynamic Proxy and third party dynamic code generation API s such as CGLib ? 代理设计模式的情况下, JDK的动态代理和第三方动态代码生成API(例如CGLib)之间有什么区别

What is the difference between using both the approaches and when should one prefer one over another? 使用这两种方法之间有什么区别?何时应该优先选择另一种方法?

JDK Dynamic proxy can only proxy by interface (so your target class needs to implement an interface, which is then also implemented by the proxy class). JDK动态代理只能通过接口代理(因此您的目标类需要实现一个接口,然后由代理类实现)。

CGLIB (and javassist) can create a proxy by subclassing. CGLIB(和javassist)可以通过子类创建代理。 In this scenario the proxy becomes a subclass of the target class. 在这种情况下,代理成为目标类的子类。 No need for interfaces. 不需要接口。

So Java Dynamic proxies can proxy: public class Foo implements iFoo where CGLIB can proxy: public class Foo 所以Java动态代理可以代理: public class Foo implements iFoo CGLIB可以代理的public class Foo implements iFoopublic class Foo

EDIT: 编辑:

I should mention that because javassist and CGLIB use proxy by subclassing, that this is the reason you cannot declare final methods or make the class final when using frameworks that rely on this. 我应该提一下,因为javassist和CGLIB通过子类化使用代理,这就是你在使用依赖于它的框架时不能声明最终方法或使类最终的原因。 That would stop these libraries from allowing to subclass your class and override your methods. 这将阻止这些库允许子类化您的类并覆盖您的方法。

Differences in functionality 功能上的差异

  • The JDK proxies allow to implement any set of interfaces while subclassing Object . JDK代理允许在子类化Object实现任何接口集。 Any interface method, plus Object::hashCode , Object::equals and Object::toString is then forwarded to an InvocationHandler . 然后将任何接口方法加上Object::hashCodeObject::equalsObject::toString转发到InvocationHandler Additionally, the standard library interface java.lang.reflect.Proxy is implemented. 此外,还实现了标准库接口java.lang.reflect.Proxy

  • cglib allows you to implement any set of interfaces while subclassing any non-final class. cglib允许您在子类化任何非final类时实现任何接口集。 Also, methods can be overridden optionally, ie not all non-abstract methods need to be intercepted. 此外,可以任选地覆盖方法,即不需要拦截所有非抽象方法。 Furthermore, there are different ways of implementing a method. 此外,有不同的方法来实现方法。 It also offers an InvocationHandler class (in a different package), but it also allows to call super methods by using more advanced interceptors as for example a MethodInterceptor . 它还提供了一个InvocationHandler类(在不同的包中),但它也允许通过使用更高级的拦截器来调用超级方法,例如MethodInterceptor Furthermore, cglib can improve performance by specialized interceptions like FixedValue . 此外,cglib可以通过像FixedValue这样的专门拦截来提高性能。 I once wrote a summary of different interceptors for cglib . 我曾经为cglib写过不同拦截器的摘要

Performance differences 性能差异

JDK proxies are implemented rather naively with only one interception dispatcher, the InvocationHandler . 只使用一个拦截调度程序InvocationHandler ,JDK代理实现得相当天真。 This requires a virtual method dispatch to an implementation which cannot always be inlined. 这需要对实现进行虚拟方法调度,而实现并不总是内联。 Cglib allows to create specialized byte code what can sometimes improve performance. Cglib允许创建专门的字节代码,有时可以提高性能。 Here are some comparisons for implementing an interface with 18 stub methods: 以下是使用18个存根方法实现接口的一些比较:

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

The time is noted in nanoseconds with standard deviation in braces. 时间以纳秒为单位,括号中标准偏差。 You can find more details on the benchmark in Byte Buddy's tutorial , where Byte Buddy is a more modern alternative to cglib. 您可以在Byte Buddy的教程中找到有关基准测试的更多详细信息,其中Byte Buddy是cglib的更现代的替代品。 Also, note that cglib is no longer under active development. 另请注意,cglib不再处于活动开发阶段。

Dynamic proxy: Dynamic implementations of interfaces at runtime using JDK Reflection API . 动态代理:使用JDK Reflection API在运行时动态实现接口。

Example: Spring uses dynamic proxies for transactions as follows: 示例: Spring使用事务的动态代理,如下所示:

在此输入图像描述

The generated proxy comes on top of bean. 生成的代理位于bean之上。 It adds transnational behavior to the bean. 它为bean添加了跨国行为。 Here the proxy generates dynamically at runtime using JDK Reflection API. 这里代理使用JDK Reflection API在运行时动态生成。

When an application is stopped, the proxy will be destroyed and we will only have interface and bean on the file system. 当应用程序停止时,代理将被销毁,我们将只在文件系统上有接口和bean。


In the above example we have interface. 在上面的例子中我们有接口。 But in most of implementation of interface is not best. 但在大多数接口的实现并不是最好的。 So bean does not implement an interface, in that case we uses inheritance: 所以bean没有实现接口,在这种情况下我们使用继承:

在此输入图像描述

In order to generate such proxies, Spring uses a third party library called CGLib . 为了生成这样的代理,Spring使用名为CGLib的第三方库。

CGLib ( C ode G eneration Lib rary) is built on top of ASM , this is mainly used the generate proxy extending bean and adds bean behavior in the proxy methods. CGLib( C ode G eneration Lib rary)建立在ASM之上,这主要用于生成代理扩展bean并在代理方法中添加bean行为。

Examples for JDK Dynamic proxy and CGLib JDK动态代理和CGLib的示例

Spring ref 春季参考

From Spring documentation : 从Spring文档

Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. Spring AOP使用JDK动态代理或CGLIB为给定目标对象创建代理。 (JDK dynamic proxies are preferred whenever you have a choice). (只要有选择,JDK动态代理就是首选)。

If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used. 如果要代理的目标对象实现至少一个接口,则将使用JDK动态代理。 All of the interfaces implemented by the target type will be proxied. 目标类型实现的所有接口都将被代理。 If the target object does not implement any interfaces then a CGLIB proxy will be created. 如果目标对象未实现任何接口,则将创建CGLIB代理。

If you want to force the use of CGLIB proxying (for example, to proxy every method defined for the target object, not just those implemented by its interfaces) you can do so. 如果要强制使用CGLIB代理(例如,代理为目标对象定义的每个方法,而不仅仅是那些由其接口实现的方法),您可以这样做。 However, there are some issues to consider: 但是,有一些问题需要考虑:

final methods cannot be advised, as they cannot be overriden. 无法建议最终方法,因为它们无法覆盖。

You will need the CGLIB 2 binaries on your classpath, whereas dynamic proxies are available with the JDK. 您将在类路径上需要CGLIB 2二进制文件,而JDK可以使用动态代理。 Spring will automatically warn you when it needs CGLIB and the CGLIB library classes are not found on the classpath. Spring会在需要CGLIB时自动发出警告,并且在类路径中找不到CGLIB库类。

The constructor of your proxied object will be called twice. 代理对象的构造函数将被调用两次。 This is a natural consequence of the CGLIB proxy model whereby a subclass is generated for each proxied object. 这是CGLIB代理模型的自然结果,其中为每个代理对象生成子类。 For each proxied instance, two objects are created: the actual proxied object and an instance of the subclass that implements the advice. 对于每个代理实例,创建两个对象:实际代理对象和实现建议的子类实例。 This behavior is not exhibited when using JDK proxies. 使用JDK代理时不会出现此行为。 Usually, calling the constructor of the proxied type twice, is not an issue, as there are usually only assignments taking place and no real logic is implemented in the constructor. 通常,两次调用代理类型的构造函数不是问题,因为通常只有赋值发生,并且构造函数中没有实现真正的逻辑。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM