什么是代理
代理是一种设计模式。当我们想要添加或修改现有类的某些功能时,我们创建并使用代理对象。通常,代理对象具有与原始代理对象相同的方法,并且在 Java 代理类中通常扩展原始类。代理的主要目的是控制对目标对象的访问,而不是增强目标对象的功能。
这样,代理类可以通过方便的方式实现许多功能:
- 方法开始和结束时日志
- 访问控制,过滤恶意请求
- 本地执行远程服务
- 缓存请求结果
- 对参数执行额外检查
- 模拟原始类的行为
- 实现对昂贵资源的懒加载
- 智能引用,可在没有客户端使用某个重量级对象时立即销毁该对象
- etc…
在实际应用中,代理类不直接实现功能。遵循单一责任原则,代理类仅执行代理,并且实际行为在处理程序中实现。
与静态代理相比,动态代理需要在运行时进行 Java 反射的字节码生成。使用动态方法,无需创建代理类,这可以带来更多便利。
静态代理类
由程序创建或特定工具自动生成源代码,在程序运行前,代理类的.class文件就已经存在。
通过将目标类与代理类实现同一个接口,让代理类持有真实类对象,然后在代理类方法中调用真实类方法,在调用真实类方法的前后添加我们所需要的功能扩展代码来达到增强的目的。
缺点
- 冗余。定义的静态代理类非常特定于一个实现,这意味着对于每个实现,代理都需要明确定义,这是重复的工作。
- 受限。单个代理类无法实现对多个不同类的方法调用的代理。
- 不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。
动态代理类
动态代理类(下面简称为代理类)是一个在运行时实现指定接口列表的类,这样就可以通过其中一个接口实例类上的方法调用编码并通过统一接口分配给另一个对象。因此,动态代理类可用于为接口列表创建类型安全的代理对象,而无需预生成代理类,例如使用编译时工具。动态代理类的实例上的方法调用被分派到实例的调用处理程序中的单个方法 - invoke
。
动态代理类对于需要在呈现接口 API 的对象上提供类型安全反射调度调用的应用程序或库非常有用。例如应用程序可以使用动态代理类来创建实现多个任意事件侦听器接口的对象,通过扩展 java.util.EventListener
的接口,以统一的方式处理不同类型的各种事件,例如将所有此类事件记录到文件中。
动态代理类 API
动态代理类(下面简称为代理类)是一个实现在运行时创建指定的接口列表的类的类(Class)。
代理接口就是由代理类实现的接口。
代理实例是代理类的实例。
创建代理类
使用类 java.lang.reflect.Proxy
的静态方法来创建代理类及其实例。
在给定类加载器和接口数组的情况下,Proxy.getProxyClass
方法返回代理类的 java.lang.Class 对象。代理类将在指定的类加载器中定义,并将实现所有提供的接口。如果已经在类加载器中定义了相同的接口排列的代理类,将返回现有的代理类;否则,将动态生成这些接口的代理类,并在类加载器中定义。
可以传递给 Proxy.getProxyClass
的参数有几个限制:
interfaces
数组中的所有Class
对象都必须表示接口,而不是类或基本类型。interfaces
数组中的任何两个元素都不能引用相同的Class
对象。- 所有接口类型必须通过指定的类加载器按名称可见。换句话说,对于类加载器 cl 和每个接口 i,以下表达式必须为 true:
Class.forName(i.getName(), false, cl) == i
- 所有非公共接口必须位于同一个包中;否则,代理类无法实现所有接口,无论它在哪个包中定义。
- 对于具有相同签名的指定接口的任何成员方法集:
- 如果任何方法的返回类型是基本类型或 void,则所有方法必须具有相同的返回类型。
- 否则,其中一个方法必须具有可分配给其余方法的所有返回类型的返回类型。
- 生成的代理类不得超过虚拟机对类强加的任何限制。例如,VM 可以将类可以实现的接口数量限制为 65535;在这种情况下,
interfaces
数组的大小不得超过 65535。
如果违反任何这些限制,Proxy.getProxyClass
将抛出 IllegalArgumentException
。如果接口数组参数或其任何元素为空,则将抛出 NullPointerException
。
请注意,指定代理接口的顺序很重要:对具有相同接口组合但顺序不同的代理类的两个请求将导致两个不同的代理类。代理类通过其代理接口的顺序来区分,以便在两个或更多代理接口共享具有相同名称和参数签名的方法的情况下提供确定性方法调用编码;
因此,每次使用相同的类加载器和接口列表调用 Proxy.getProxyClass
时,不需要生成新的代理类,动态代理类 API 的实现应保留生成的代理类的缓存,缓存的键由其相应的加载器和接口列表定义。实现缓存时应注意不要引用类加载器,接口和代理类,以防止类加载器及其所有类在适当时被垃圾收集。
代理类属性
代理类具有以下属性:
- 代理类是公共的,最终的,而不是抽象的。
- 代理类的非限定名称是未指定的。但是,以字符串
“$Proxy”
开头的类名空间是为代理类保留的。 - 代理类 extend
java.lang.reflect.Proxy
。 - 代理类以相同的顺序实现其创建时指定的接口。
- 由于代理类实现了在创建时指定的所有接口,因此在其 Class 对象上调用
getInterfaces
将返回一个包含相同接口列表的数组(按照创建时指定的顺序)。在其 Class 对象上调用getMethods
将返回包含这些接口中所有方法的Method
对象数组,并且调用getMethod
将按预期在代理接口中查找方法。 - 如果
Proxy.isProxyClass
方法传递了一个代理类 -(由Proxy.getProxyClass
返回的类或Proxy.newProxyInstance
返回的对象的类),则返回 true,否则返回 false。 - 代理类的
java.security.ProtectionDomain
与引导类加载器加载的系统类相同,例如 java.lang.Object,因为代理类的代码是由受信任的系统代码生成的。通常会授予此保护域java.security.AllPermission
。
创建代理实例
每个代理类都有一个公共构造函数,它接受一个参数,即 InvocationHandler
接口的实现。
每个代理实例都有一个关联的 InvocationHandler
对象,该对象就是通过构造方法传递进来的。不必非要使用反射 API 来访问公共构造函数,也可以通过调用 Proxy.newProxyInstance
方法创建代理实例。 Proxy.newProxyInstance
因与 Proxy.getProxyClass
相同的原因抛出 IllegalArgumentException
(比如接口数量不能超过 65535)。
InvocationHandler handler = new MyInvocationHandler(...);
// 创建代理类
Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
// 使用代理类创建代理实例
Foo proxy = (Foo) proxyClass.getConstructor(InvocationHandler.class).newInstance(handler);
// 或者使用静态方法直接创建代理实例
Foo proxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class<?>[]{Foo.class}, handler);
代理实例属性
代理实例有以下属性:
- 给定代理实例 proxy 和其代理类 Foo 实现的接口之一,以下表达式将返回 true:
proxy instanceof Foo
并且以下转换操作将成功(而不是抛出 ClassCastException):
(Foo) proxy
静态
Proxy.getInvocationHandler
方法传递代理实例并返回与之关联的InvocationHandler
。如果传递给Proxy.getInvocationHandler
的对象不是代理实例,则将抛出IllegalArgumentException
。代理实例上的接口方法调用将被编码并调度到
InvocationHandler
的调用方法,如下所述。代理实例本身将作为
invoke
的第一个参数传递,它是 Object 类型。传递给
invoke
的第二个参数是java.lang.reflect.Method
实例,该实例对应于在代理实例上调用的接口方法。Method
对象的声明类将是声明方法的接口,它可以是代理接口继承方法的代理接口的超接口。传递给
invoke
的第三个参数是一个对象数组,其中包含在代理实例上的方法调用中传递的参数的值。原始类型的参数包装在适当的原始包装类的实例中,例如java.lang.Integer
或java.lang.Boolean
。invoke
方法的实现可以自由修改此数组的内容。invoke
方法返回的值将成为代理实例上方法调用的返回值。如果接口方法的声明返回值是基本类型,则 invoke 返回的值必须是相应原始包装类的实例;否则,它必须是可分配给声明的返回类型的类型。如果invoke
返回的值为null
,并且接口方法的返回类型为原始类型,则代理实例上的方法调用将抛出NullPointerException
。如果invoke
返回的值与上面描述的方法声明的返回类型不兼容,则代理实例将抛出ClassCastException
。如果
invoke
方法抛出异常,它也将在代理实例上的方法调用中抛出。异常的类型必须可分配给在接口方法的签名中声明的任何异常类型,或者分配给未经检查的异常类型java.lang.RuntimeException
或java.lang.Error
。如果通过调用抛出已检查的异常,该异常不能分配给接口方法的throws
子句中声明的任何异常类型,在代理实例上的方法调用时将抛出UndeclaredThrowableException
,该异常由invoke
方法抛出的异常来构造。在代理实例上的
java.lang.Object
中声明的hashCode
,equals
或toString
方法的调用将被编码并调度到InvocationHandler
的invoke
方法,其方式与编码和分派接口方法调用的方式相同,如上所述。 传递给invoke
的Method
对象的声明类将是java.lang.Object
。 从java.lang.Object
继承的代理实例的其他公共方法不会被代理类覆盖,因此这些方法的调用行为就像它们对 java.lang.Object 的实例所做的一样。
序列化
由于 java.lang.reflect.Proxy
实现了 java.io.Serializable
,因此可以序列化代理实例。
在多个代理接口中重复的方法
当两个或多个代理接口包含具有相同名称和参数签名的方法时,代理类接口的顺序就变得很重要。
当在代理实例上调用重复方法时,包含代理类接口列表中的方法(直接或通过超接口继承)的最前面接口中方法的 Method 对象将传递给调用处理程序的 invoke
方法。
Examples
Example 1
下面是一个简单的示例,它在实现任意接口列表的对象上的方法调用之前和之后打印出一条消息:
public interface Foo {
Object bar(Object obj) throws BazException;
}
public class FooImpl implements Foo {
Object bar(Object obj) throws BazException {
// ...
}
}
public class DebugProxy implements java.lang.reflect.InvocationHandler {
private Object obj;
public static Object newInstance(Object obj) {
return java.lang.reflect.Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
new DebugProxy(obj));
}
private DebugProxy(Object obj) {
this.obj = obj;
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable
{
Object result;
try {
System.out.println("before method " + m.getName());
result = m.invoke(obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException("unexpected invocation exception: " +
e.getMessage());
} finally {
System.out.println("after method " + m.getName());
}
return result;
}
}
要为 Foo
接口的实现构造 DebugProxy
并调用其方法之一:
Foo foo = (Foo) DebugProxy.newInstance(new FooImpl());
foo.bar(null);
Example 2
下面是一个实用程序调用处理程序类的示例,它为从 java.lang.Object
继承的方法提供默认代理行为,并根据被调用方法的接口实现对不同对象的某些代理方法调用的委派:
import java.lang.reflect.*;
public class Delegator implements InvocationHandler {
// preloaded Method objects for the methods in java.lang.Object
private static Method hashCodeMethod;
private static Method equalsMethod;
private static Method toStringMethod;
static {
try {
hashCodeMethod = Object.class.getMethod("hashCode", null);
equalsMethod =
Object.class.getMethod("equals", new Class[] { Object.class });
toStringMethod = Object.class.getMethod("toString", null);
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
private Class[] interfaces;
private Object[] delegates;
public Delegator(Class[] interfaces, Object[] delegates) {
this.interfaces = (Class[]) interfaces.clone();
this.delegates = (Object[]) delegates.clone();
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable
{
Class declaringClass = m.getDeclaringClass();
if (declaringClass == Object.class) {
if (m.equals(hashCodeMethod)) {
return proxyHashCode(proxy);
} else if (m.equals(equalsMethod)) {
return proxyEquals(proxy, args[0]);
} else if (m.equals(toStringMethod)) {
return proxyToString(proxy);
} else {
throw new InternalError(
"unexpected Object method dispatched: " + m);
}
} else {
for (int i = 0; i < interfaces.length; i++) {
if (declaringClass.isAssignableFrom(interfaces[i])) {
try {
return m.invoke(delegates[i], args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
}
return invokeNotDelegated(proxy, m, args);
}
}
protected Object invokeNotDelegated(Object proxy, Method m,
Object[] args) throws Throwable {
throw new InternalError("unexpected method dispatched: " + m);
}
protected Integer proxyHashCode(Object proxy) {
return new Integer(System.identityHashCode(proxy));
}
protected Boolean proxyEquals(Object proxy, Object other) {
return (proxy == other ? Boolean.TRUE : Boolean.FALSE);
}
protected String proxyToString(Object proxy) {
return proxy.getClass().getName() + '@' +
Integer.toHexString(proxy.hashCode());
}
}
Delegator
的子类可以覆盖 invokeNotDelegated
以实现代理方法调用的行为,而不是直接委托给其他对象,并且可以覆盖 proxyHashCode
、proxyEquals
和 proxyToString
来覆盖代理从 java.lang.Object 继承的方法的默认行为。
构造实现 Foo
接口的 Delegator
:
Class[] proxyInterfaces = new Class[] { Foo.class };
Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), proxyInterfaces,
new Delegator(proxyInterfaces, new Object[] { new FooImpl() }));
请注意,上面给出的 Delegator
类的实现旨在说明用途而不是优化。
由于
InvocationHandler
是一个函数接口,因此可以使用lambda
表达式内联定义处理程序。
Example 3
将代理模式和工厂模式结合起来,可以无感的创建基于代理的实例。很多框架都是这样提供的实例,比如 Spring 的 Bean、MyBatis 的 Mapper、Hibernate 的关联集合、Spring Data JPA 的 Repository 等。
- 工厂方法用于抽象或隐藏对象创建的逻辑。
- 代理类用于正确代理并委托所有方法给实现,为需要代理的类提供统一抽象处理,或者为代理类不同生命周期阶段提供钩子(Hook)。
比如 MyBatis 的 Mapper 的实例化,通过 Mapper 接口类来实例化 Mapper。
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
...
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
...
}
CGLIB 代理
JDK 动态代理
通常用于动态创建代理。JDK 动态代理很容易使用,但 JDK 动态代理方法要求目标对象实现一个或多个接口。对象不一定实现接口,对象集合不一定共享相同的接口。面对这样的需求,JDK 动态代理无法提供答案。
CGLIB
是一个功能强大的高性能代码生成库。它在基于代理的面向方面编程(AOP
)框架(如 Spring AOP
和 dynaop
)中广泛使用,以提供方法拦截。Hibernate
使用 CGLIB
为持久化类生成代理。EasyMock
和 jMock
是使用模拟对象测试 Java
代码的库。它们都使用 CGLIB
库为没有接口的类创建模拟对象。
在幕后,CGLIB 库使用 ASM
,一个小但快速的字节码操作框架,来转换现有的字节码并生成新的类。除了 CGLIB
库之外,脚本语言(如 Groovy
和 BeanShell
)也使用 ASM
生成 Java
字节码。 ASM
使用类似 SAX
解析器的机制来实现高性能。不鼓励直接使用 ASM
,因为它需要熟悉 JVM
,包括类文件格式和指令集。
请注意,某些框架(如
Spring AOP
和Hibernate
)通常同时使用CGLIB
库和JDK 动态代理
来满足其需求。Hibernate
使用 JDK 动态代理为WebShere
应用程序服务器实现事务管理器适配器;默认情况下,Spring AOP
使用 JDK 动态代理来代理接口,除非您强制使用 CGLIB 代理。
CGLIB 代理 API
CGLIB
库代码库很小,但由于缺少文档,很难学习。CGLIB
库的版本组织如下:
net.sf.cglib.core
低级字节码操作类;他们中的大多数都与
ASM
有关。net.sf.cglib.transform
运行时或构建时的类文件转换的类
net.sf.cglib.proxy
代理创建和方法拦截的类
net.sf.cglib.reflect
用于更快反射的类和
C#
样式的委托net.sf.cglib.util
集合排序工具类
net.sf.cglib.beans
JavaBean
相关工具类
要动态创建代理,大多数情况下,您只需要使用代理包中的一些 API。
如前一节所述,CGLIB
库是 ASM
之上的高级层。它对代理不实现接口的类非常有用。本质上,它动态生成一个子类来覆盖代理类的非 final 方法,并连接回调用户定义的拦截器的钩子。它比 JDK 动态代理方法更快。
通常用于代理具体类的 CGLIB 库 API 如上图所示。net.sf.cglib.proxy.Callback
接口是一个标记接口。 net.sf.cglib.proxy.Enhancer
类使用的所有回调接口都扩展了此接口。
net.sf.cglib.proxy.MethodInterceptor
是最常用的回调类型。它在 AOP 术语中启用 ”around advice“(围绕建议) - 也就是说,您可以在调用 “super” 方法之前和之后调用自定义代码。此外,您可以在调用 super 方法之前修改参数,或者根本不调用它:
public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;
当 net.sf.cglib.proxy.MethodInterceptor
是代理的所有方法的回调时,代理上的方法调用将在调用原始对象上的方法之前路由到此方法。如下图所示。第一个参数是代理对象。第二个和第三个参数分别是截获的方法和方法参数。可以使用 java.lang.reflect.Method
对象或使用 net.sf.cglib.proxy.MethodProxy
对象通过常规反射调用原始方法。net.sf.cglib.proxy.MethodProxy
通常是首选,因为它更快。在此方法中,可以在调用原始方法之前或之后注入自定义代码。
net.sf.cglib.proxy.MethodInterceptor
满足任何拦截需求,但在某些情况下可能有点过分。为了简化和性能,提供了开箱即用的其他专用回调类型。举些例子,
net.sf.cglib.proxy.FixedValue
出于性能原因,强制特定方法返回固定值很有用。
net.sf.cglib.proxy.NoOp
它将方法调用直接委托给超类中的默认实现。
net.sf.cglib.proxy.LazyLoader
当真实对象需要延迟加载时,它很有用。加载实际对象后,它将用于代理实例的每个未来方法调用。
net.sf.cglib.proxy.Dispatcher
它与
net.sf.cglib.proxy.LazyLoader
具有相同的签名,但在调用代理方法时始终会调用loadObject
方法。net.sf.cglib.proxy.ProxyRefDispatcher
它与
Dispatcher
相同,但它允许将代理对象作为loadObject
方法的参数传入。
回调通常用于代理类中的所有方法,如上图所示,但您可以使用 net.sf.cglib.proxy.CallbackFilter
有选择地对方法应用回调。JDK 动态代理中没有这种细粒度控制功能,其中 java.lang.reflect.InvocationHandler
的 invoke
方法适用于代理对象的所有方法。
除了代理类之外,CGLIB 还可以通过提供 java.lang.reflect.Proxy
的替代代码 net.sf.cglib.proxy.Proxy
来代理接口,以支持 JDK 1.3 之前的 Java 代理。由于很少使用此替换代理功能,因此此处不涉及相关代理 API。
代理包还提供对 net.sf.cglib.proxy.Mixin
的支持。基本上,它允许将多个对象组合成一个更大的对象。代理上的方法调用委托给底层对象。
让我们看看如何使用 CGLIB 代理 API 创建代理。
创建代理类
CGLIB 代理的核心是 net.sf.cglib.proxy.Enhancer
类。要创建 CGLIB 代理,至少需要一个类。让我们先使用内置的 NoOp 回调:
/**
* 使用NoOp回调创建代理。目标类必须有一个默认的零参数构造函数。
*
* @param targetClass 代理的超类
* @return 目标类实例的新代理
*/
public Object createProxy(Class targetClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setCallback(NoOp.INSTANCE);
return enhancer.create();
}
返回值是目标类实例的代理。在此示例中,为 net.sf.cglib.proxy.Enhancer
类配置了单个 net.sf.cglib.proxy.Callback
。可以看出,创建一个简单的代理是相当简单的。而不是创建 net.sf.cglib.proxy.Enhancer
的新实例,您可以简单地使用 net.sf.cglib.proxy.Enhancer
类中的静态帮助器方法来创建代理。但是最好使用上例中显示的方法,因为它允许您配置 net.sf.cglib.proxy.Enhancer
实例以精确控制生成的代理。
请注意,目标类作为生成的代理的超类传入。与 JDK 动态代理不同,您无法在代理创建期间传递目标对象。目标对象必须由 CGLIB 库创建。在此示例中,默认的零参数构造函数用于创建目标实例。如果您希望 CGLIB 使用一些参数创建实例,net.sf.cglib.proxy.Enhancer.create(Class[],Object[])
应当使用该方法替代 net.sf.cglib.proxy.Enhancer.create()
。第一个参数指定参数类型和第二个指定参数值。原始类型在参数中应该使用包装类型。
使用 MethodInterceptor
要使代理更有用,可以使用自定义 net.sf.cglib.proxy.MethodInterceptor
替换 net.sf.cglib.proxy.NoOp 回调。代理上的所有方法调用都被调度到 net.sf.cglib.proxy.MethodInterceptor
的单个 intercept
方法。然后,intercept
方法将调用委托给底层对象。
假设您要对目标对象的所有方法调用应用授权检查。如果授权失败,将抛出运行时异常 AuthorizationException
。 Authorization.java
接口如下所示:
import java.lang.reflect.Method;
/**
* 用于说明目的的简单授权服务。
*/
public interface AuthorizationService {
/**
* 方法调用的授权检查。如果检查失败将抛出 AuthorizationException。
*/
void authorize(Method method);
}
net.sf.cglib.proxy.MethodInterceptor
的实现如下:
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import com.lizjason.cglibproxy.AuthorizationService;
/**
* 一个简单的 MethodInterceptor 实现,对代理方法调用应用授权检查。
*/
public class AuthorizationInterceptor implements MethodInterceptor {
private AuthorizationService authorizationService;
/**
* 使用给定的 AuthorizationServic 创建 AuthorizationInterceptor
*/
public AuthorizationInterceptor (AuthorizationService authorizationService) {
this.authorizationService = authorizationService;
}
/**
* 拦截代理方法调用以注入授权检查。通过 MethodProxy 调用原始方法。
*
* @param object 代理对象
* @param method 拦截方法
* @param args 方法传参
* @param proxy 用于调用原始方法的代理
* @throws 可抛出任何异常;如果抛出异常,将不会调用 super 方法
* @return 与代理方法的签名兼容的任何值。
*/
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy ) throws Throwable {
if (authorizationService != null) {
//may throw an AuthorizationException if authorization failed
authorizationService.authorize(method);
}
return methodProxy.invokeSuper(object, args);
}
}
在 intercept
方法中,首先检查授权。如果授权通过,则 intercept
方法将调用目标对象上的原始方法。出于性能原因,使用 CGLIB
net.sf.cglib.proxy.MethodProxy
对象而不是使用 java.lang.reflect.Method
对象进行常规反射来调用原始方法。
使用 CallbackFilter
net.sf.cglib.proxy.CallbackFilter
允许您在方法级别设置回调。假设您有一个 PersistenceServiceImpl
类,它有两个方法:save
和 load
。 save
方法需要授权检查,但 load
方法不需要。
import com.lizjason.cglibproxy.PersistenceService;
/**
* PersistenceService 接口的简单实现
*/
public class PersistenceServiceImpl implements PersistenceService {
public void save(long id, String data) {
System.out.println(data + " has been saved successfully.");
}
public String load(long id) {
return "Jason Zhicheng Li";
}
}
请注意,PersistenceServiceImpl
类实现了 PersistenceService
接口,但这不是使用 CGLIB
生成代理所必需的。 PersistenceServiceImpl
的 net.sf.cglib.proxy.CallbackFilter
实现如下:
import java.lang.reflect.Method;
import net.sf.cglib.proxy.CallbackFilter;
/**
* PersistenceServiceImpl 的 CallbackFilter 实现
*/
public class PersistenceServiceCallbackFilter implements CallbackFilter {
// save 方法的回调索引
private static final int SAVE = 0;
// load 方法的回调索引
private static final int LOAD = 1;
/**
* 指定要用于所调用方法的回调。
*
* @method 被调用的方法。
* @return 此方法的回调数组中的回调索引
*/
public int accept(Method method) {
String name = method.getName();
if ("save".equals(name)) {
return SAVE;
}
// 对于其他方法,包括 load方法,使用第二个回调
return LOAD;
}
}
accept
方法将代理方法映射到回调。返回值是特定方法的回调数组中的索引。以下是 PersistenceServiceImpl
类的代理创建实现:
...
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersistenceServiceImpl.class);
CallbackFilter callbackFilter = new PersistenceServiceCallbackFilter();
enhancer.setCallbackFilter(callbackFilter);
AuthorizationService authorizationService = ...
Callback saveCallback = new AuthorizationInterceptor(authorizationService);
Callback loadCallback = NoOp.INSTANCE;
Callback[] callbacks = new Callback[]{saveCallback, loadCallback };
enhancer.setCallbacks(callbacks);
...
return (PersistenceServiceImpl)enhancer.create();
在此示例中,AuthorizationInterceptor
实例应用于 save
方法,NoOp.INSTANCE
应用于 load
方法。可选的,您可以通过 net.sf.cglib.proxy.Enhancer.setInterfaces(Class [])
方法指定代理对象要实现的接口。
除了设置 net.sf.cglib.proxy.Enhancer
的回调数组外,还可以通过 net.sf.cglib.proxy.Enhancer.setCallbackTypes(Class [])
方法指定回调类型数组。如果在代理创建期间没有实际回调实例数组,则回调类型很有用。与回调类似,您需要使用 net.sf.cglib.proxy.CallbackFilter
为每个方法指定回调类型索引。
总结
**JDK 动态代理
**允许我们在运行时动态的创建代理类。但必须要求代理对象实现一个或多个接口。它大量使用反射进行操作,使用原生 Java 代码。
CGLIB
是一个功能强大的高性能代码生成库。它是 JDK 动态代理的补充,除了实现接口之外,还允许代理扩展具体的基类。底层,它使用 ASM 字节码操作框架。实质上,CGLIB 动态生成一个子类来覆盖代理类的非 final 方法。它比使用 Java 反射的 JDK 动态代理方法更快。
CGLIB 不能代理 final 类或任何 final 方法。
对于一般情况,您使用 JDK 动态代理方法来创建代理。 当接口不可用或性能问题时,CGLIB 是一个很好的选择。
原文链接:使用 CGLIB 库动态创建代理