在上一篇文章中,我讨论了基于标准Java的代理对象。 当您要在实现接口的对象上具有方法调用处理程序时,可以使用这些方法。 Java反射代理的创建要求您具有一个实现接口的对象。 我们要代理的对象已经失控,它没有实现我们要从处理程序调用的接口,而我们仍然希望拥有代理。
什么时候需要不带接口的对象的代理?
这是很常见的情况。 如果有的话,我们有一个JPA实现,例如Hibernate,它实现了记录的延迟加载。 例如,审核日志记录存储在一个表中,并且每个记录(第一个记录除外)都引用了上一项。 就像是
class LinkedAuditLogRecord {LinkedAuditLogRecord previous;AuditLogRecord actualRecord;
}
通过JPA加载记录将返回一个对象LinkedAuditLogRecord
,该对象包含以前的记录作为对象,依此类推,直到第一个在名为previos
的字段中可能为null
记录为止。 (这不是实际的代码。)任何JPA实现从一开始就抓取并加载整个表到我们感兴趣的记录都是一个非常糟糕的实现。 相反,持久层仅加载实际记录,并创建一个扩展LinkedAuditLogRecord
的代理对象,而这就是previous
字段的内容。 实际字段通常是私有字段,如果我们的代码尝试访问上一条记录,则代理对象将在该时间加载它。 简而言之,这是延迟加载。
但是,JPA实现如何为未实现接口的类的对象创建代理? Java反射代理实现无法做到这一点,因此JPA实现使用了不同的东西。 他们通常使用的是cglib。
什么是cglib
Cglib是一个开放源代码库,能够在Java运行时创建和加载内存中的类文件。 为此,它使用Java字节码生成库'asm',这是一个非常低级的字节码创建工具。 我不会在本文中深入探讨。
如何使用cglib
使用cglib创建代理对象几乎与使用JDK反射代理API一样简单。 我使用cglib创建了与上周文章相同的代码:
package proxy;import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CglibProxyDemo {static class Original {public void originalMethod(String s) {System.out.println(s);}}static class Handler implements MethodInterceptor {private final Original original;public Handler(Original original) {this.original = original;}public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("BEFORE");method.invoke(original, args);System.out.println("AFTER");return null;}}public static void main(String[] args){Original original = new Original();MethodInterceptor handler = new Handler(original);Original f = (Original) Enhancer.create(Original.class,handler);f.originalMethod("Hallo");}
}
区别在于类的名称略有不同,并且我们没有接口。
代理类扩展原始类也很重要,因此在创建代理对象时,它将调用原始类的构造函数。 如果这是资源匮乏的话,我们可能会有一些问题。 但是,这是我们不能回避的事情。 如果我们想为一个已经存在的类提供一个代理对象,那么我们应该有一个接口,或者我们必须扩展原始类,否则我们就不能使用代理对象代替原始类。
翻译自: https://www.javacodegeeks.com/2016/01/creating-proxy-object-using-cglib.html