cglib中proxy动态代理解析
来源:互联网 发布:淘宝平拍裤子褶皱手法 编辑:程序博客网 时间:2024/06/07 08:31
CGLIB中的动态代理是JDK proxy的一个很好的补充,在JDK中实现代理时,要求代理类必须是继承接口的类,因为JDK最后生成的proxy class其实就是实现了被代理类所继承的接口并且继承了java中的Proxy类,通过反射找到接口的方法,调用InvocationHandler的invoke 方法实现拦截。CGLIb中最后生成的proxy class是一个继承被代理类的class,通过重写被代理类中的非final的方法实现代理。总结为:
JDK proxy:代理类必须实现接口
CGLIB: 代理类不能是final,代理的方法也不能是final(继承限制)
关于JDK proxy原理,可参看之前的整理
http://blog.csdn.net/sunnycoco05/article/details/78845878
下面看看CGLIb中的处理,我们要实现对一个类进行代理,在调用方法前后进行简单处理
创建一个要代理的类:
有一个printName类方法
public class MyTarget { public void printName() { System.err.println("name:Target-"); }
自己的拦截器,CGLIb中实现MethodInterceptor接口,它可支持对不同方法的拦截,这里我们统一处理:
public class MyInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] params, MethodProxy proxy) throws Throwable { System.err.println("=======before======"); Object res = proxy.invokeSuper(obj, params); System.err.println("=======and======"); return res; }}
只是在方法调用前后进行简单打印。CGLIb中通过Enhancer类进行代理操作,开始测试:
@Test public void proxyTest() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MyTarget.class); enhancer.setCallback(new MyInterceptor()); MyTarget target = (MyTarget) enhancer.create(); target.printName(); System.out.println("proxy class name:" + target.getClass().getName()); }
运行结果:
成功实现拦截。
动态代理,其实就是动态的生成一个有关被代理类的class文件,并加装运行这个class文件的过程。如果我们可以把JVM中生成的class 文件保存下来,再通过反编译后的Java文件去分析,应该就很清楚的去看懂它的原理。我们可以用-javaagent去实现这个功能。自定义一个agent 类, 这个Agent在class文件装载的时候,我们判断是否是生成的代理类(根据class name判断),如果符合,将class的字节流写入文件,存在指定位置
public class MyAgent implements ClassFileTransformer { //会在main方法之前执行,添加自定义的的ClassFileTransformer public static void premain(String agentArgs, Instrumentation inst) { inst.addTransformer(new MyAgent()); } //每次装载class文件都会执行 @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { //com.cglib.MyTarget$$EnhancerByCGLIB$$65288428 //com.sun.proxy.$Proxy4 //CGLIB和JDK proxy生成的代理类进行拦截 if (className.contains("$$EnhancerByCGLIB$$") || className.contains("$Proxy")) { //指定存放路径 String path = "yourdirpath"; int lastIndexOf = className.lastIndexOf("/") + 1; String classFileName = className.substring(lastIndexOf) + ".class"; writeClassToDisk(path + classFileName, classfileBuffer); System.out.println(className + "---writeClassToDisk Succeess!"); } return classfileBuffer; } //将class文件存在磁盘 private void writeClassToDisk(String fileName, byte[] data) { try { File file = new File(fileName); if (!file.exists()) { file.createNewFile(); } FileOutputStream fos = new FileOutputStream(file); fos.write(data); fos.close(); } catch (Exception e) { e.printStackTrace(); } }}
将MyAgent打成jar包myagent.jar 。在eclipse中的export功能中,选择工程中存在的MANIFEST.MF文件进行打包,MANIFEST.MF文件内容为:
Manifest-Version: 1.0Premain-Class: com.cglib.MyAgent
表示版本号和Premain-Class所在的class文件
将 jar包放入项目路径下。
测试类:
public class AgentTest { //main方法中运行 public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MyTarget.class); enhancer.setCallback(new MyInterceptor()); MyTarget myTarget = (MyTarget) enhancer.create(); myTarget.printName(); System.err.println("proxy name: " + myTarget.getClass().getName()); }}
字eclipse中,右键->Run As >Run configurations,在Arguments中添加:-javaagent:myagent.jar
运行:
从命名可以看到,MyTarget$$EnhancerByCGLIB$$b0fb28f5就是我们要找的MyTarget类的代理类,而运行中又加载了一个com/cglib/MyTarget$$EnhancerByCGLIB$$b0fb28f5$$FastClassByCGLIB$$1a454ece的class文件。
现在我们已经把cglib生成的class文件保留了,反编译查看代码:
package com.cglib;import com.cglib.MyTarget;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;//继承了MyTarget实现了Factory接口public class MyTarget$$EnhancerByCGLIB$$b0fb28f5 extends MyTarget 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 static final Method CGLIB$printName$0$Method; private static final MethodProxy CGLIB$printName$0$Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$finalize$1$Method; private static final MethodProxy CGLIB$finalize$1$Proxy; private static final Method CGLIB$equals$2$Method; private static final MethodProxy CGLIB$equals$2$Proxy; private static final Method CGLIB$toString$3$Method; private static final MethodProxy CGLIB$toString$3$Proxy; private static final Method CGLIB$hashCode$4$Method; private static final MethodProxy CGLIB$hashCode$4$Proxy; private static final Method CGLIB$clone$5$Method; private static final MethodProxy CGLIB$clone$5$Proxy; //初始化一堆方法参数组等 static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class arg = Class.forName("com.cglib.MyTarget$$EnhancerByCGLIB$$b0fb28f5"); Class arg0; Method[] arg9999 = ReflectUtils.findMethods( new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (arg0 = Class.forName("java.lang.Object")).getDeclaredMethods()); CGLIB$finalize$1$Method = arg9999[0]; CGLIB$finalize$1$Proxy = MethodProxy.create(arg0, arg, "()V", "finalize", "CGLIB$finalize$1"); CGLIB$equals$2$Method = arg9999[1]; CGLIB$equals$2$Proxy = MethodProxy.create(arg0, arg, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2"); CGLIB$toString$3$Method = arg9999[2]; CGLIB$toString$3$Proxy = MethodProxy.create(arg0, arg, "()Ljava/lang/String;", "toString", "CGLIB$toString$3"); CGLIB$hashCode$4$Method = arg9999[3]; CGLIB$hashCode$4$Proxy = MethodProxy.create(arg0, arg, "()I", "hashCode", "CGLIB$hashCode$4"); CGLIB$clone$5$Method = arg9999[4]; CGLIB$clone$5$Proxy = MethodProxy.create(arg0, arg, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5"); CGLIB$printName$0$Method = ReflectUtils.findMethods(new String[]{"printName", "()V"}, (arg0 = Class.forName("com.cglib.MyTarget")).getDeclaredMethods())[0]; CGLIB$printName$0$Proxy = MethodProxy.create(arg0, arg, "()V", "printName", "CGLIB$printName$0"); } final void CGLIB$printName$0() { super.printName(); } //重写MyTarget的printName方法 public final void printName() { MethodInterceptor arg9999 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); arg9999 = this.CGLIB$CALLBACK_0; } if (arg9999 != null) { arg9999.intercept(this, CGLIB$printName$0$Method, CGLIB$emptyArgs, CGLIB$printName$0$Proxy); } else { super.printName(); } } final void CGLIB$finalize$1() throws Throwable { super.finalize(); } protected final void finalize() throws Throwable { MethodInterceptor arg9999 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); arg9999 = this.CGLIB$CALLBACK_0; } if (arg9999 != null) { arg9999.intercept(this, CGLIB$finalize$1$Method, CGLIB$emptyArgs, CGLIB$finalize$1$Proxy); } else { super.finalize(); } } final boolean CGLIB$equals$2(Object arg0) { return super.equals(arg0); } public final boolean equals(Object arg0) { MethodInterceptor arg9999 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); arg9999 = this.CGLIB$CALLBACK_0; } if (arg9999 != null) { Object arg1 = arg9999.intercept(this, CGLIB$equals$2$Method, new Object[]{arg0}, CGLIB$equals$2$Proxy); return arg1 == null ? false : ((Boolean) arg1).booleanValue(); } else { return super.equals(arg0); } } final String CGLIB$toString$3() { return super.toString(); } public final String toString() { MethodInterceptor arg9999 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); arg9999 = this.CGLIB$CALLBACK_0; } return arg9999 != null ? (String) arg9999.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy) : super.toString(); } final int CGLIB$hashCode$4() { return super.hashCode(); } public final int hashCode() { MethodInterceptor arg9999 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); arg9999 = this.CGLIB$CALLBACK_0; } if (arg9999 != null) { Object arg0 = arg9999.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy); return arg0 == null ? 0 : ((Number) arg0).intValue(); } else { return super.hashCode(); } } final Object CGLIB$clone$5() throws CloneNotSupportedException { return super.clone(); } protected final Object clone() throws CloneNotSupportedException { MethodInterceptor arg9999 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); arg9999 = this.CGLIB$CALLBACK_0; } return arg9999 != null ? arg9999.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy) : super.clone(); } public static MethodProxy CGLIB$findMethodProxy(Signature arg) { String arg9999 = arg.toString(); switch (arg9999.hashCode()) { case -1574182249 : if (arg9999.equals("finalize()V")) { return CGLIB$finalize$1$Proxy; } break; case -508378822 : if (arg9999.equals("clone()Ljava/lang/Object;")) { return CGLIB$clone$5$Proxy; } break; case 1826985398 : if (arg9999.equals("equals(Ljava/lang/Object;)Z")) { return CGLIB$equals$2$Proxy; } break; case 1861880221 : if (arg9999.equals("printName()V")) { return CGLIB$printName$0$Proxy; } break; case 1913648695 : if (arg9999.equals("toString()Ljava/lang/String;")) { return CGLIB$toString$3$Proxy; } break; case 1984935277 : if (arg9999.equals("hashCode()I")) { return CGLIB$hashCode$4$Proxy; } } return null; } public MyTarget$$EnhancerByCGLIB$$b0fb28f5() { CGLIB$BIND_CALLBACKS(this); } public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] arg) { CGLIB$THREAD_CALLBACKS.set(arg); } public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] arg) { CGLIB$STATIC_CALLBACKS = arg; } private static final void CGLIB$BIND_CALLBACKS(Object arg) { MyTarget$$EnhancerByCGLIB$$b0fb28f5 arg0 = (MyTarget$$EnhancerByCGLIB$$b0fb28f5) arg; if (!arg0.CGLIB$BOUND) { arg0.CGLIB$BOUND = true; Object arg9999 = CGLIB$THREAD_CALLBACKS.get(); if (arg9999 == null) { arg9999 = CGLIB$STATIC_CALLBACKS; if (CGLIB$STATIC_CALLBACKS == null) { return; } } arg0.CGLIB$CALLBACK_0 = (MethodInterceptor) ((Callback[]) arg9999)[0]; } } public Object newInstance(Callback[] arg0) { CGLIB$SET_THREAD_CALLBACKS(arg0); MyTarget$$EnhancerByCGLIB$$b0fb28f5 arg9999 = new MyTarget$$EnhancerByCGLIB$$b0fb28f5(); CGLIB$SET_THREAD_CALLBACKS((Callback[]) null); return arg9999; } public Object newInstance(Callback arg0) { CGLIB$SET_THREAD_CALLBACKS(new Callback[]{arg0}); MyTarget$$EnhancerByCGLIB$$b0fb28f5 arg9999 = new MyTarget$$EnhancerByCGLIB$$b0fb28f5(); CGLIB$SET_THREAD_CALLBACKS((Callback[]) null); return arg9999; } public Object newInstance(Class[] arg0, Object[] arg1, Callback[] arg2) { // $FF: Couldn't be decompiled } public Callback getCallback(int arg0) { CGLIB$BIND_CALLBACKS(this); MethodInterceptor arg9999; switch (arg0) { case 0 : arg9999 = this.CGLIB$CALLBACK_0; break; default : arg9999 = null; } return arg9999; } public void setCallback(int arg0, Callback arg1) { switch (arg0) { case 0 : this.CGLIB$CALLBACK_0 = (MethodInterceptor) arg1; default : } } public Callback[] getCallbacks() { CGLIB$BIND_CALLBACKS(this); return new Callback[]{this.CGLIB$CALLBACK_0}; } public void setCallbacks(Callback[] arg0) { this.CGLIB$CALLBACK_0 = (MethodInterceptor) arg0[0]; } static { CGLIB$STATICHOOK1(); }}
CGLIB生成的class文件中,部分不能被反编译,但对我们理解它的代理原理起到很大作用了。从中我们看到,继承了MyTaget通过重写定义方法啦实现代理,也就说明CGLIB不能用作代理final标识的类和方法。在调用printName方法时,最终调用传入的MethodInterceptor实例的intercept方法,也就是我们的MyInterceptor。具体的传入和逻辑可根据源码分析
- cglib中proxy动态代理解析
- java动态代理proxy ,cglib
- Cglib 实现动态代理Proxy
- 动态代理proxy与CGLib
- Cglib Dynamic Proxy(Cglib 动态代理)
- 动态代理proxy与CGLib的区别
- java动态代理,proxy和cglib
- CGlib与Java Proxy的动态代理
- 动态代理proxy与CGLib的区别
- 动态代理Proxy和CGLib区别
- 动态代理proxy与CGLib的区别
- 动态代理proxy与CGLib的区别
- 动态代理proxy与CGLib的区别
- 动态代理proxy与CGLib的区别
- 动态代理proxy与CGLib的区别
- 动态代理proxy与CGLib的区别
- Java 静态代理 vs 动态代理 vs CGLib Proxy
- 代理:Proxy和Cglib
- ETL工具kettle与JAVA结合使用程序生成转换
- Unable to instantiate activity ComponentInfo on path: DexPathList
- Apache.NMS.dll引用过程中的问题
- 空间中任一点到超平面的距离公式的推导过程
- golang fmt格式“占位符”
- cglib中proxy动态代理解析
- java变量与数据类型
- 作为一名技术主管,开始记录技术研究内容
- ajax获取后台json信息时跨域
- KMP算法
- 工作流引擎
- 现代OpenGL自学----纹理测试
- 纪念一位歌手
- PX4飞控之导航及任务架构