Answers:
JDK动态代理只能按接口进行代理(因此您的目标类需要实现一个接口,然后该接口也可以由代理类实现)。
CGLIB(和javassist)可以通过子类化创建代理。在这种情况下,代理将成为目标类的子类。无需接口。
因此,Java Dynamic代理可以代理:public class Foo implements iFoo
CGLIB可以代理:public class Foo
编辑:
我应该提到的是,由于javassist和CGLIB通过子类化使用代理,因此这就是您在使用依赖于此的框架时无法声明final方法或将类定为final的原因。这将阻止这些库允许子类化您的类并覆盖您的方法。
功能上的差异
JDK代理允许在子类化时实现任何接口集Object
。任何界面的方法,加Object::hashCode
,Object::equals
并且Object::toString
然后被转发到一个InvocationHandler
。此外,实现了标准库接口java.lang.reflect.Proxy
。
cglib允许您在继承任何非最终类的同时实现任何接口集。同样,方法可以可选地被覆盖,即并非所有非抽象方法都需要被拦截。此外,有多种实现方法的方式。它还提供了一个InvocationHandler
类(在不同的包中),但是它也允许通过使用更高级的拦截器(例如a)来调用超级方法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的教程中找到有关基准测试的更多详细信息,其中Byte Buddy是cglib的更现代的替代品。另外,请注意,cglib不再处于主动开发中。
动态代理:使用JDK Reflection API在运行时动态实现接口。
示例: Spring使用动态代理进行事务处理,如下所示:
生成的代理位于bean之上。它将跨国行为添加到bean中。在这里,代理使用JDK Reflection API在运行时动态生成。
当应用程序停止时,代理将被销毁,并且文件系统上将只有接口和Bean。
在上面的示例中,我们有接口。但是在大多数实现接口中并不是最好的。因此bean不实现接口,在这种情况下,我们使用继承:
为了生成此类代理,Spring使用了一个名为CGLib的第三方库。
CGLIB(ç ODE ģ eneration 库郭宝宏)是建立在顶部ASM,这是主要用于在生成代理延伸豆和在代理方法增加豆的行为。
Spring AOP使用JDK动态代理或CGLIB创建给定目标对象的代理。(只要有选择,首选JDK动态代理)。
如果要代理的目标对象实现了至少一个接口,则将使用JDK动态代理。目标类型实现的所有接口都将被代理。如果目标对象未实现任何接口,则将创建CGLIB代理。
如果要强制使用CGLIB代理(例如,代理为目标对象定义的每个方法,而不仅仅是代理由其接口实现的方法),则可以这样做。但是,有一些问题要考虑:
不能建议使用final方法,因为它们不能被覆盖。
您将在类路径上需要CGLIB 2二进制文件,而JDK提供了动态代理。当需要CGLIB并且在类路径中找不到CGLIB库类时,Spring会自动向您发出警告。
代理对象的构造函数将被调用两次。这是CGLIB代理模型的自然结果,其中为每个代理对象生成一个子类。对于每个代理实例,都会创建两个对象:实际的代理对象和实现建议的子类的实例。使用JDK代理时,不会出现此现象。通常,两次调用代理类型的构造函数不是问题,因为通常仅发生分配,并且构造函数中未实现任何实际逻辑。