cglib是一个字节码操纵库,底层基于ASM框架。虽然JDK同样提供了动态代理功能,但是必须将需要代理的方法写在接口中,由主体类继承,很不灵活,而且性能不如cglib。下面是一个使用cglib动态代理的例子。
主体类:
public class MyClass {
public void print() {
System.out.println("I'm in MyClass.print!");
}
}
代理类:
public class Main { public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, System.getProperty("user.dir") + "/bin");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyClass.class);
enhancer.setCallback(new MethodInterceptorImpl());
MyClass my = (MyClass) enhancer.create();
my.print();
}
private static class MethodInterceptorImpl implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// log something
System.out.println(method + " intercepted!");
proxy.invokeSuper(obj, args);
return null;
}
}
}
实现原理:
1.操纵字节码,生成Myclass的子类。将MyClass的子类的字节码反编译之后,看起来会接近这个等价类:
class MyClass$$EnhancerByCGLIB extends MyClass{
private MethodInterceptor methodInterceptor;
public void print() throws Throwable {
Class localClass = Class.forName("cgproxy.MyClass$$EnhancerByCGLIB");
ClassLoader classLoader = localClass.getClassLoader();
Method method = Class.forName("cgproxy.MyClass").getDeclaredMethod("print", new Class[0]);
MethodProxy methodProxy = MethodProxy.create(classLoader, method.getDeclaringClass(), localClass, "()V", "print", "CGLIB$print");
methodInterceptor.intercept(this, method, null, methodProxy);
}
public void CGLIB$print() {
super.print();
}
}
需要注意的是,继承自Object的方法都会按相同的方式生成字节码,wait,notify方法除外,因此MyClass.toString()也会被代理。
这是直接由字节码反编译的结果,可以忽略
public class MyClass$$EnhancerByCGLIB$$1e2eda10 extends MyClass
implements Factory {
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
Class localClass;
CGLIB$emptyArgs = new Object[0];
ClassLoader tmp27_17 = (localClass = Class.forName("cgproxy.MyClass$$EnhancerByCGLIB$$1e2eda10")).getClassLoader();
CGLIB$print$0$0$Proxy = MethodProxy.create(tmp27_17, (1e2eda10.CGLIB$print$0$0$Method = Class.forName("cgproxy.MyClass").getDeclaredMethod("print", new Class[0])).getDeclaringClass(), localClass, "()V", "print", "CGLIB$print$0$0");
return;
}
final void CGLIB$print$0$0() {
super.print();
}
public final void print() {
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
if (this.CGLIB$CALLBACK_0 != null)
return;
super.print();
}}
2.执行代码,enhancer.create返回的是MyClass$$EnhancerByCGLIB类的实例,my.print()实际调用的是MyClass$$EnhancerByCGLIB.print(),然后会被MethodInterceptor代理类拦截
3.完成前置代理
4.调用主体类的print()方法:MethodProxy.invokeSuper(MyClass$$EnhancerByCGLIB, Object[]), 这是MethodProxy的相关代码
4.调用主体类的print()方法:MethodProxy.invokeSuper(MyClass$$EnhancerByCGLIB, Object[]), 这是MethodProxy的相关代码
public class MethodProxy {
private FastClass f2;
private int i2;
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
return f2.invoke(i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
}
FastClass也是操纵字节码动态生成的类,invoke()会根据事先建立的int值与方法之间的映射,通过int索引访问方法,字节码如下(可能是JD-GUI支持的jdk版本太低了,这一段不能反编译成Java代码)
public Object invoke(int paramInt, Object paramObject, Object[] paramArrayOfObject)
throws InvocationTargetException
{
// Byte code:
// 0: aload_2 //load paramObject到oprand stack
// 1: checkcast 152 cgproxy/MyClass$$EnhancerByCGLIB$$1e2eda10 //类型检查
// 4: iload_1 //load paramInt索引到opran stack
// 5: tableswitch default:+387 -> 392, 0:+123->128, 1:+138->143, 2:+142->147, 3:+154->159, 4:+176->181, 5:+186->191, 6:+196->201 //跳转
// 129: iconst_0
// 130: aaload
// 131: invokevirtual 153 cgproxy/MyClass$$EnhancerByCGLIB$$1e2eda10:equals (Ljava/lang/Object;)Z
// 134: new 155 java/lang/Boolean
// 137: dup_x1
// 138: swap
// 139: invokespecial 158 java/lang/Boolean:<init> (Z)V
// 142: areturn
// 143: invokevirtual 159 cgproxy/MyClass$$EnhancerByCGLIB$$1e2eda10:toString ()Ljava/lang/String;
// 146: areturn
// 147: invokevirtual 160 cgproxy/MyClass$$EnhancerByCGLIB$$1e2eda10:hashCode ()I
// 150: new 162 java/lang/Integer
// 153: dup_x1
// 154: swap
// 155: invokespecial 165 java/lang/Integer:<init> (I)V
// 158: areturn
// 159: aload_3
// 160: iconst_0
// 161: aaload
// 162: checkcast 167 [Ljava/lang/Class;
// 165: aload_3
// 166: iconst_1
// 167: aaload
// 168: checkcast 169 [Ljava/lang/Object;
// 171: aload_3
// 172: iconst_2
// 173: aaload
// 174: checkcast 171 [Lnet/sf/cglib/proxy/Callback;
// 177: invokevirtual 174 cgproxy/MyClass$$EnhancerByCGLIB$$1e2eda10:newInstance ([Ljava/lang/Class;[Ljava/lang/Object;[Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;
// 180: areturn
// 181: aload_3
// 182: iconst_0
// 183: aaload
// 184: checkcast 176 net/sf/cglib/proxy/Callback
// 187: invokevirtual 179 cgproxy/MyClass$$EnhancerByCGLIB$$1e2eda10:newInstance (Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;
// 190: areturn
// 191: aload_3
// 192: iconst_0
// 193: aaload
// 194: checkcast 171 [Lnet/sf/cglib/proxy/Callback;
// 197: invokevirtual 182 cgproxy/MyClass$$EnhancerByCGLIB$$1e2eda10:newInstance ([Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;
// 200: areturn
// 201: invokevirtual 185 cgproxy/MyClass$$EnhancerByCGLIB$$1e2eda10:print ()V //paramObject.print()
// 204: aconst_null //将null压入oprand stack
// 205: areturn //return
}
5.方法返回,如果有后置增强的话,进行后置增强
html学得好,博客写到老,各种奇怪的字符都得手动删,囧
本文介绍了Cglib动态代理的实现原理,并通过一个具体的例子展示了如何使用Cglib进行动态代理,包括主体类和代理类的设计,以及字节码层面的详细解释。
2930

被折叠的 条评论
为什么被折叠?



