KDj*_*ava 127 java reflection dynamic-proxy cglib
在的情况下,代理设计模式,是有什么区别JDK的动态代理和第三方动态代码生成API会如CGLIB?
使用这两种方法之间有什么区别?何时应该优先选择另一种方法?
rap*_*aëλ 167
JDK动态代理只能通过接口代理(因此您的目标类需要实现一个接口,然后由代理类实现).
CGLIB(和javassist)可以通过子类创建代理.在这种情况下,代理成为目标类的子类.不需要接口.
所以Java动态代理可以代理:public class Foo implements iFooCGLIB可以代理的地方:public class Foo
编辑:
我应该提一下,因为javassist和CGLIB通过子类化使用代理,这就是你在使用依赖于它的框架时不能声明最终方法或使类最终的原因.这将阻止这些库允许子类化您的类并覆盖您的方法.
Raf*_*ter 52
功能上的差异
JDK代理允许在子类化时实现任何接口集Object.任何界面的方法,加Object::hashCode,Object::equals并且Object::toString然后被转发到一个InvocationHandler.
cglib允许您在子类化任何非final类时实现任何接口集.此外,可以任选地覆盖方法,即不需要拦截所有非抽象方法.此外,有不同的方法来实现方法.它还提供了一个java.lang.reflect.Proxy类(在不同的包中),但它也允许通过使用更高级的拦截器来调用超级方法,例如a InvocationHandler.此外,cglib可以通过专门的拦截来提高性能MethodInterceptor.我曾经为cglib写过不同拦截器的摘要.
性能差异
JDK代理实现得相当天真,只有一个拦截调度程序,即FixedValue.这需要对实现进行虚拟方法调度,而实现并不总是内联.Cglib允许创建专门的字节代码,有时可以提高性能.以下是使用18个存根方法实现接口的一些比较:
cglib JDK proxy
creation 804.000 (1.899) 973.650 (1.624)
invocation 0.002 (0.000) 0.005 (0.000)
Run Code Online (Sandbox Code Playgroud)
时间以纳秒为单位,括号中标准偏差.您可以在Byte Buddy的教程中找到有关基准测试的更多详细信息,其中Byte Buddy是cglib的更现代的替代品.另请注意,cglib不再处于活动开发阶段.
Pre*_*raj 23
动态代理:使用JDK Reflection API在运行时动态实现接口.
示例: Spring使用事务的动态代理,如下所示:
生成的代理位于bean之上.它为bean添加了跨国行为.这里代理使用JDK Reflection API在运行时动态生成.
当应用程序停止时,代理将被销毁,我们将只在文件系统上有接口和bean.
在上面的例子中我们有接口.但在大多数接口的实现并不是最好的.所以bean没有实现接口,在这种情况下我们使用继承:
为了生成这样的代理,Spring使用名为CGLib的第三方库.
CGLib(C ode G eneration Lib rary )建立在ASM之上,这主要用于生成代理扩展bean并在代理方法中添加bean行为.
Spring AOP 使用 JDK 动态代理或 CGLIB 为给定的目标对象创建代理。(如果您有选择,则首选 JDK 动态代理)。
如果要代理的目标对象至少实现了一个接口,则将使用 JDK 动态代理。目标类型实现的所有接口都将被代理。如果目标对象没有实现任何接口,那么将创建一个 CGLIB 代理。
如果您想强制使用 CGLIB 代理(例如,代理为目标对象定义的每个方法,而不仅仅是由其接口实现的方法),您可以这样做。但是,有一些问题需要考虑:
不能建议 final 方法,因为它们不能被覆盖。
您将需要类路径上的 CGLIB 2 二进制文件,而动态代理可用于 JDK。当 Spring 需要 CGLIB 并且在类路径中找不到 CGLIB 库类时,它会自动警告您。
代理对象的构造函数将被调用两次。这是 CGLIB 代理模型的自然结果,为每个代理对象生成一个子类。对于每个代理实例,会创建两个对象:实际代理对象和实现建议的子类实例。使用 JDK 代理时不会出现此行为。通常,两次调用代理类型的构造函数不是问题,因为通常只发生赋值,并且构造函数中没有实现真正的逻辑。