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学得好,博客写到老,各种奇怪的字符都得手动删,囧