CGLIB介绍 CGLIB是一个功能强大的高性能代码生成库。它被广泛应用于基于代理的AOP框架(如springaop和dynaop)中,以提供方法拦截。Hibernate作为最流行的ORM工具,也使用CGLIB库代理单端关联(除了collection lazy loading,它使用另一种机制)。EasyMock和jMock是流行的Java测试库。它们提供模拟对象来支持测试。两者都使用CGLIB代理没有接口的类。 CGLIB库使用ASM(一种轻量级但高性能的字节码操作框架)来转换字节码并生成新的类(如图1.1)。
除了CGLIB之外,Groovy和Bean Shell等脚本语言也使用ASM来生成Java字节码。ASM使用类似于SAX analyzer的机制来实现高性能。但是,并不推荐直接使用ASM,因为这需要对JVM有很深的了解,包括类文件格式和指令集。 另外,像Spring AOP和Hibernate这样的框架经常使用CGLIB和JDK动态代理来满足各自的需求。Hibernate使用JDK动态代理为webshere应用服务实现事务管理适配器;Spring AOP默认情况下使用JDK动态代理来代理接口,除非强制使用CGLIB,或者代理没有实现接口的类。
CGLIB API CGLIB库虽然只有少量代码,但是由于缺乏文档,学习难度较大。CGLIB库的包结构如图2.1(本文采用Cglib 3.1):
net.sf.cglib.beans:Java bean 相关的工具类
net.sf.cglib.core:底层字节码操作相关的类;大多数与ASP相关
net.sf.cglib.proxy:代理创建和方法拦截相关的类
net.sf.cglib.reflect:反射相关的类
net.sf.cglib.transform:编译和运行时类文件转换相关的类
net.sf.cglib.util:集合排序工具相关类
对于创建动态代理,在大多数情况下,只需要使用代理包的部分API。 如上所述,CGLIB库基于ASM的上层应用程序。CGLIB对于代理不实现接口的类非常有用。本质上,对于需要代理的类,它只是动态地生成一个子类来覆盖非final方法,同时绑定钩子函数来回调自定义拦截器,这样就可以对目标类进行增强。值得一提的是,它比JDK动态代理更快。
net.sf.cglib.proxy.MethodInterceptor是最常用的回调类型,通常用于在基于代理的AOP实现中拦截方法回调。此接口只有一个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public interface MethodInterceptor extends Callback { public Object intercept (Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable ;}
如果net.sf.cglib.proxy.MethodInterceptor被设置为方法回调,当调用代理方法时,它调会用MethodInterceptor.intercept方法,然后再调用代理对象的方法,也就是目标方法。MethodInterceptor的intercept方法的第一个参数是代理对象,第二个参数和第三个参数分别是被拦截的方法和方法的参数,最后一个参数是代理方法。如果要调用代理对象的原始方法,可以使用java.lang.reflect方法对象来反射调用,或net.sf.cglib.proxy.MethodProxy对象。我们通常用net.sf.cglib.proxy.MethodProxy.MethodProxy,因为它更快。在intercept方法中,可以在目标方法调用之前或之后注入自定义代码(环绕通知)。
net.sf.cglib.proxy.MethodInterceptor满足大部分场景代理要求,但对于某些特定场景可能不太方便。为了易于使用和高性能,CGLIB提供了其他特殊的回调类型。例如,
net.sf.cglib.proxy.FixedValue:在特定情况下,强制特定方法返回固定值,是非常有用和高性能的。
net.sf.cglib.proxy.NoOp:它直接传递父类的方法实现。
net.sf.cglib.proxy.LazyLoader:这在需要延迟加载代理对象的情况下非常有用。如果加载了代理对象,它将在以后的代理调用中重用。
net.sf.cglib.Dispatcher:类似于net.sf.cglib.proxy.LazyLoader,但每次调用代理方法时,都会调用loadObject方法来加载代理对象。
net.sf.cglib.proxy.ProxyRefDispatcher:与Dispatcher相同,但其loadObject方法支持传入的代理对象。
我们通常对代理类的所有方法使用相同的回调,但是我们也可以使用net.sf.cglib.proxy.callsFilter对不同的方法使用不同的回调。然而JDK动态代理并没有提供这样的细粒度的控制。也就是说JDK中, java.lang.reflect.InvocationHandler的invoke方法不能对不同方法使用不同回调,只能使用相同的回调。
现在让我们看看如何使用CGLIB创建代理。
用CGLIB 创建代理 使用Cglib可以很方便的创建代理。
创建一个简单的代理 只需要传入一个目标类即可,Cglib就可以为该类创建代理对象。
1 2 3 4 5 6 7 8 9 10 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。如你所见,使用CGLIB创建一个简单的代理很容易。除了new一个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[])。方法的第一个参数指定参数类型,第二个参数指定参数值。参数中的基本类型需要包装类型。
创建一个复杂的代理 说复杂其实也不复杂,只是相对与上面简单类型而言,多了一些回调接口来实现不同类型的通知对目标类增强,例如前置通知等。
使用MethodInterceptor 我们可以替换net.sf.cglib.proxy.NoOp,实现net.sf.cglib.proxy.MethodInterceptor接口以获得更强的代理。代理对象的所有方法调用都分配给的intercept方法net.sf.cglib.proxy.MethodInterceptor. intercept方法然后调用底层对象。
假设您要检查是否有权限调用目标对象的某个方法,如果授权失败,可以抛出一个运行时异常AuthorizationException(本例中只是简单打印信息)。接口授权.java具体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.wonglay;import java.lang.reflect.Method;public interface AuthorizationService { void authorize (Method method) ; }
net.sf.cglib.proxy.MethodInterceptor接口的实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.wonglay;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class AuthorizationInterceptor implements MethodInterceptor { private final AuthorizationService authorizationService; public AuthorizationInterceptor (AuthorizationService authorizationService) { this .authorizationService = authorizationService; } @Override public Object intercept (Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { if (authorizationService != null ) { authorizationService.authorize(method); } return methodProxy.invokeSuper(obj, args); } }
在intercept方法中,首先检查授权,如果授权通过,intercept方法调用目标对象的方法。出于性能原因,我们使用CGLIB net.sf.cglib.proxy.MethodProxy对象而不是常规java.lang.reflect方法反射对象来调用原始方法。
使用CallbackFilter net.sf.cglib.proxy.callsFilter允许在方法级别设置回调。假设有一个PersistenceServiceImpl类,它有两个方法:save和load。save方法需要授权检查,而load方法不需要。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.wonglay;public class PersistenceServiceImpl implements PersistenceService { @Override public void save (Long id, String data) { System.out.println(data + " has been saved successfully" ); } @Override public String load (Long id) { return "wonglay.com" ; } }
PersistenceService接口的net.sf.cglib.proxy.CallbackFilter实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.wonglay;import net.sf.cglib.proxy.CallbackFilter;import java.lang.reflect.Method;public class PersistenceServiceCallbackFilter implements CallbackFilter { public static final int SAVE = 0 ; public static final int LOAD = 1 ; @Override public int accept (Method method) { String methodName = method.getName(); if ("save" .equals(methodName)) { return SAVE; } return LOAD; } }
accept方法将代理方法映射到MethodInterceptor,也即callback。这个方法返回callback对象数组的下标。下面是为PersistenceServiceImpl类创建代理对象的过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.wonglay;import net.sf.cglib.proxy.Callback;import net.sf.cglib.proxy.CallbackFilter;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.NoOp;public class Main { public static void main (String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(PersistenceServiceImpl.class); CallbackFilter callbackFilter = new PersistenceServiceCallbackFilter(); enhancer.setCallbackFilter(callbackFilter); AuthorizationService authorizationService = (method) -> { String methodName = method.getName(); System.out.println("[" + methodName + "] method authenticating..." ); }; Callback saveCallback = new AuthorizationInterceptor(authorizationService); Callback loadCallback = NoOp.INSTANCE; Callback[] callbacks = new Callback[]{saveCallback, loadCallback}; enhancer.setCallbacks(callbacks); PersistenceServiceImpl persistenceService = (PersistenceServiceImpl) enhancer.create(); System.out.println(persistenceService.load(1L )); persistenceService.save(2L , "wonglay.com" ); } }
打印结果如图3.2.2,load方法调用没有打印权限检查信息,而save方法打印了权限检查信息,原因是load方法使用了NoOp.INSTANCE callback,它只是一个线程安全的空实现,而save方法使用了AuthorizationInterceptor callback对目标类进行增加,这里的通知(Advice)类型为前置通知。
在本例中,授权拦截器应用于save方法,NoOp.INSTANCE 应用于load方法。你可以通net.sf.cglib.proxy.Enhancer.setInterfaces(Class [])指定代理需要实现的接口,但这不是必需的。 对于net.sf.cglib.proxy.Enhancer,除了设置callback对象的数组外,还可以使用net.sf.cglib.proxy.Enhancer.setCallbackTypes(Class [])设置callback对象的数组。如果在代理创建期间没有实际的callback对象,则此方法非常有用。与callback对象一样,还需要使用net.sf.cglib.proxy.callsFilter为每个拦截方法指定callback对象数组的下标。
底层实现原理探究 当调用Enhancer的create方法创建代理对象时底层回调用下面这个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 protected Object create (Object key) { Class gen = null ; synchronized (source) { ClassLoader loader = getClassLoader(); Map cache2 = null ; cache2 = (Map)source.cache.get(loader); if (cache2 == null ) { cache2 = new HashMap(); cache2.put(NAME_KEY, new HashSet()); source.cache.put(loader, cache2); } else if (useCache) { Reference ref = (Reference)cache2.get(key); gen = (Class) (( ref == null ) ? null : ref.get()); } if (gen == null ) { Object save = CURRENT.get(); CURRENT.set(this ); try { this .key = key; if (attemptLoad) { try { gen = loader.loadClass(getClassName()); } catch (ClassNotFoundException e) { } } if (gen == null ) { byte [] b = strategy.generate(this ); String className = ClassNameReader.getClassName(new ClassReader(b)); getClassNameCache(loader).add(className); gen = ReflectUtils.defineClass(className, b, loader); } if (useCache) { cache2.put(key, new WeakReference(gen)); } return firstInstance(gen); } finally { CURRENT.set(save); } } } return firstInstance(gen); }
我们可以通过给JVM加一个Debug属性,使生成的代理对象输出到磁盘:
1 2 3 4 5 6 7 8 public static void main (String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\java" ); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(PersistenceServiceImpl.class); .... }
生成的Class文件用IDE反编译得到如下代理类,此对象继承了目标对象,并且实现了Factory接口,该接口可用于创建此代理类型相同的实例,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 package com.wonglay;import java.lang.reflect.Method;import net.sf.cglib.core.ReflectUtils;import net.sf.cglib.core.Signature;import net.sf.cglib.proxy.Callback;import net.sf.cglib.proxy.Factory;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import net.sf.cglib.proxy.NoOp;public class PersistenceServiceImpl $$EnhancerByCGLIB $$f5def17a extends PersistenceServiceImpl implements Factory { private boolean CGLIB$BOUND; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private NoOp CGLIB$CALLBACK_1; private static final Method CGLIB$save$1 $Method; private static final MethodProxy CGLIB$save$1 $Proxy; private static final Object[] CGLIB$emptyArgs; static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0 ]; Class var0 = Class.forName("com.wonglay.PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a" ); Class var1; CGLIB$save$1 $Method = ReflectUtils.findMethods(new String[]{"save" , "(Ljava/lang/Long;Ljava/lang/String;)V" }, (var1 = Class.forName("com.wonglay.PersistenceServiceImpl" )).getDeclaredMethods())[0 ]; CGLIB$save$1 $Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Long;Ljava/lang/String;)V" , "save" , "CGLIB$save$1" ); } final void CGLIB$save$1 (Long var1, String var2) { super .save(var1, var2); } public final void save (Long var1, String var2) { MethodInterceptor var10000 = this .CGLIB$CALLBACK_0; if (var10000 == null ) { CGLIB$BIND_CALLBACKS(this ); var10000 = this .CGLIB$CALLBACK_0; } if (var10000 != null ) { var10000.intercept(this , CGLIB$save$1 $Method, new Object[]{var1, var2}, CGLIB$save$1 $Proxy); } else { super .save(var1, var2); } } public static MethodProxy CGLIB$findMethodProxy(Signature var0) { String var10000 = var0.toString(); switch (var10000.hashCode()) { case -279689439 : if (var10000.equals("save(Ljava/lang/Long;Ljava/lang/String;)V" )) { return CGLIB$save$1 $Proxy; } } return null ; } public PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a() { CGLIB$BIND_CALLBACKS(this ); } public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) { CGLIB$THREAD_CALLBACKS.set(var0); } public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) { CGLIB$STATIC_CALLBACKS = var0; } private static final void CGLIB$BIND_CALLBACKS(Object var0) { PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a var1 = (PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a)var0; if (!var1.CGLIB$BOUND) { var1.CGLIB$BOUND = true ; Object var10000 = CGLIB$THREAD_CALLBACKS.get(); if (var10000 == null ) { var10000 = CGLIB$STATIC_CALLBACKS; if (var10000 == null ) { return ; } } Callback[] var10001 = (Callback[])var10000; var1.CGLIB$CALLBACK_1 = (NoOp)((Callback[])var10000)[1 ]; var1.CGLIB$CALLBACK_0 = (MethodInterceptor)var10001[0 ]; } } public Object newInstance (Callback[] var1) { CGLIB$SET_THREAD_CALLBACKS(var1); PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a var10000 = new PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null ); return var10000; } public Object newInstance (Callback var1) { throw new IllegalStateException("More than one callback object required" ); } public Object newInstance (Class[] var1, Object[] var2, Callback[] var3) { CGLIB$SET_THREAD_CALLBACKS(var3); PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a var10000 = new PersistenceServiceImpl$$EnhancerByCGLIB$$f5def17a; switch (var1.length) { case 0 : var10000.<init>(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null ); return var10000; default : throw new IllegalArgumentException("Constructor not found" ); } } public Callback getCallback (int var1) { CGLIB$BIND_CALLBACKS(this ); Object var10000; switch (var1) { case 0 : var10000 = this .CGLIB$CALLBACK_0; break ; case 1 : var10000 = this .CGLIB$CALLBACK_1; break ; default : var10000 = null ; } return (Callback)var10000; } public void setCallback (int var1, Callback var2) { switch (var1) { case 0 : this .CGLIB$CALLBACK_0 = (MethodInterceptor)var2; break ; case 1 : this .CGLIB$CALLBACK_1 = (NoOp)var2; } } public Callback[] getCallbacks() { CGLIB$BIND_CALLBACKS(this ); return new Callback[]{this .CGLIB$CALLBACK_0, this .CGLIB$CALLBACK_1}; } public void setCallbacks (Callback[] var1) { this .CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0 ]; this .CGLIB$CALLBACK_1 = (NoOp)var1[1 ]; } static { CGLIB$STATICHOOK1(); } }
生成的代理对象继承了目标对象,并重写了目标对象的方法以此来达到增强目标对象的目的。实现net.sf.cglib.proxy.Factory的目的是提供一些创建代理对象实例的工厂方法,这些方法会比反射创建对象快。另外,要拦截在对象构造期间调用的方法,必须使用这些方法而不是反射。
总结 CGLIB是一个功能强大的高性能代码生成库。作为对JDK动态代理的补充,它为不实现接口的类提供代理解决方案。在底部,它使用ASM字节码操作框架。实际上,CGLIB通过生成子类来重写非final方法来代理。它比使用Java反射的JDK动态代理方法更快。CGLIB不能代理final类或final方法。一般来说,可以使用JDK动态代理方法来创建代理。对于没有接口或性能因素的情况,CGLIB是一个很好的选择。