# 彻底搞懂Spring AOP **Repository Path**: 1028125449/Spring-AOP ## Basic Information - **Project Name**: 彻底搞懂Spring AOP - **Description**: 本项目旨在从AOP生成的clas文件入手,完全搞懂AOP。 1、如何搞到生成的class文件 2、如何看懂生成的class字节码 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 4 - **Created**: 2017-09-25 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 彻底搞懂Spring AOP 本项目旨在从AOP生成的class文件入手,完全搞懂AOP(以常规的cglib为基础)。 ## 目的 对于AOP一直的观点都是AOP是动态修改class字节码, 但到底生成了什么样的代码, 仍然是雾里看花, 让我们保存动态生成的class文件到磁盘一探究竟. ## 运行 运行ProxyFactoryTest的Junit,即可看到生成的class文件(通过两种方式生成,生成的内容一致) ``` 文件一:org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$b6871ed4.class 文件二:gen.class/..... ``` ## 实现步骤 ### STEP.0 以如下类作为需要代理的类(TableDao.java) ``` /// Before表示拦截器栈(不需要关注如何实现的) @Before(value={LogInterceptor.class, AuthInterceptor.class}) public class TableDao { public void create(){ System.out.println("create() is running..."); } public void delete(){ System.out.println("delete() is running..."); } public void update(){ System.out.println("update() is running..."); } @Clear(value=AuthInterceptor.class) public void query(){ System.out.println("query() is running..."); } } ``` ### STEP.1 如何搞到生成的class文件 方法1.(ProxyFactory.java)利用cglib的增强API 即可获得class的字节码, 通过文件流保存即可(保存在工程根目录:org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$b6871ed4.class). ``` byte[] bytes = Enhancer.getStrategy().generate(en) ``` 方法2. (ProxyFactoryTest.java)利用cglib提供的保存生成的字节码参数(保存在gen.class目录下): ``` System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "gen.class"); ``` ### STEP.2 如何看懂生成的class字节码 生成的class文件不一定能通过一般的反编译工具打开(反编译成功的可能不准确),原因未知, 但是我们可以通过eclipse的class字节码解析工具,获得如下结果(重点关注其中一个void create方法,删除一些干扰项): ``` // Compiled from (version 1.2 : 46.0, no super bit) /// **类继承自TableDao,即代理类,这就是为什么能强转为代理类直接使用的原因** public class org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f extends org.smvc.test.aop.TableDao implements net.sf.cglib.proxy.Factory { // Field descriptor #9 Z private boolean CGLIB$BOUND; // Field descriptor #11 Ljava/lang/ThreadLocal; private static final java.lang.ThreadLocal CGLIB$THREAD_CALLBACKS; // Field descriptor #13 [Lnet/sf/cglib/proxy/Callback; private static final net.sf.cglib.proxy.Callback[] CGLIB$STATIC_CALLBACKS; // **这里就是我们通过Enhance设定进去的拦截器栈** // Field descriptor #15 Lnet/sf/cglib/proxy/MethodInterceptor; private net.sf.cglib.proxy.MethodInterceptor CGLIB$CALLBACK_0; // Field descriptor #26 Ljava/lang/reflect/Method; private static final java.lang.reflect.Method CGLIB$create$0$Method; // Field descriptor #28 Lnet/sf/cglib/proxy/MethodProxy; private static final net.sf.cglib.proxy.MethodProxy CGLIB$create$0$Proxy; // Method descriptor #17 ()V // Stack: 1, Locals: 1 final void CGLIB$create$0(); 0 aload_0 [this] 1 invokespecial org.smvc.test.aop.TableDao.create() : void [34] 4 return // Method descriptor #17 ()V // Stack: 5, Locals: 1 /// 代理类的create方法 public final void create(); 0 aload_0 [this] /// **先获取拦截器栈** 1 getfield org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$CALLBACK_0 : net.sf.cglib.proxy.MethodInterceptor [36] 4 dup /// **如果拦截器栈不为空** 5 ifnonnull 17 8 pop 9 aload_0 [this] 10 invokestatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$BIND_CALLBACKS(java.lang.Object) : void [40] 13 aload_0 [this] /// **绑定参数,准备调用拦截器栈** 14 getfield org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$CALLBACK_0 : net.sf.cglib.proxy.MethodInterceptor [36] 17 dup 18 ifnull 37 21 aload_0 [this] 22 getstatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$create$0$Method : java.lang.reflect.Method [42] 25 getstatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$emptyArgs : java.lang.Object[] [44] 28 getstatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$create$0$Proxy : net.sf.cglib.proxy.MethodProxy [46] /// **准备好参数,正好能和Callback类的intercept方法四个参数对应上,调用拦截器栈** 31 invokeinterface net.sf.cglib.proxy.MethodInterceptor.intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy) : java.lang.Object [52] [nargs: 5] 36 return /// **否则拦截器栈为空,则直接调用被代理类的真实方法** 37 aload_0 [this] 38 invokespecial org.smvc.test.aop.TableDao.create() : void [34] 41 return // Method descriptor #231 ([Lnet/sf/cglib/proxy/Callback;)V // Stack: 1, Locals: 1 public static void CGLIB$SET_STATIC_CALLBACKS(net.sf.cglib.proxy.Callback[] arg0); 0 aload_0 [arg0] 1 putstatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$STATIC_CALLBACKS : net.sf.cglib.proxy.Callback[] [237] 4 return // Method descriptor #38 (Ljava/lang/Object;)V // Stack: 3, Locals: 2 private static final void CGLIB$BIND_CALLBACKS(java.lang.Object arg0); 0 aload_0 [arg0] 1 checkcast org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f [2] 4 astore_1 5 aload_1 6 getfield org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$BOUND : boolean [239] 9 ifne 52 12 aload_1 13 iconst_1 14 putfield org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$BOUND : boolean [239] 17 getstatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$THREAD_CALLBACKS : java.lang.ThreadLocal [24] 20 invokevirtual java.lang.ThreadLocal.get() : java.lang.Object [242] 23 dup 24 ifnonnull 39 27 pop 28 getstatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$STATIC_CALLBACKS : net.sf.cglib.proxy.Callback[] [237] 31 dup 32 ifnonnull 39 35 pop 36 goto 52 39 checkcast net.sf.cglib.proxy.Callback[] [243] 42 aload_1 43 swap 44 iconst_0 45 aaload 46 checkcast net.sf.cglib.proxy.MethodInterceptor [48] 49 putfield org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$CALLBACK_0 : net.sf.cglib.proxy.MethodInterceptor [36] 52 return // Method descriptor #245 ([Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object; // Stack: 2, Locals: 2 public java.lang.Object newInstance(net.sf.cglib.proxy.Callback[] arg0); 0 aload_1 [arg0] 1 invokestatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$SET_THREAD_CALLBACKS(net.sf.cglib.proxy.Callback[]) : void [247] 4 new org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f [2] 7 dup 8 invokespecial org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f() [248] 11 aconst_null 12 invokestatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$SET_THREAD_CALLBACKS(net.sf.cglib.proxy.Callback[]) : void [247] 15 areturn // Method descriptor #249 (Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object; // Stack: 4, Locals: 2 public java.lang.Object newInstance(net.sf.cglib.proxy.Callback arg0); 0 iconst_1 1 anewarray net.sf.cglib.proxy.Callback [251] 4 dup 5 iconst_0 6 aload_1 [arg0] 7 aastore 8 invokestatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$SET_THREAD_CALLBACKS(net.sf.cglib.proxy.Callback[]) : void [247] 11 new org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f [2] 14 dup 15 invokespecial org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f() [248] 18 aconst_null 19 invokestatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$SET_THREAD_CALLBACKS(net.sf.cglib.proxy.Callback[]) : void [247] 22 areturn // Method descriptor #252 ([Ljava/lang/Class;[Ljava/lang/Object;[Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object; // Stack: 5, Locals: 4 public java.lang.Object newInstance(java.lang.Class[] arg0, java.lang.Object[] arg1, net.sf.cglib.proxy.Callback[] arg2); 0 aload_3 [arg2] 1 invokestatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$SET_THREAD_CALLBACKS(net.sf.cglib.proxy.Callback[]) : void [247] 4 new org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f [2] 7 dup 8 aload_1 [arg0] 9 dup 10 arraylength 11 tableswitch default: 35 case 0: 28 28 pop 29 invokespecial org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f() [248] 32 goto 50 35 goto 38 38 pop 39 new java.lang.IllegalArgumentException [254] 42 dup 43 ldc_w [256] 46 invokespecial java.lang.IllegalArgumentException(java.lang.String) [259] 49 athrow 50 aconst_null 51 invokestatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$SET_THREAD_CALLBACKS(net.sf.cglib.proxy.Callback[]) : void [247] 54 areturn // Method descriptor #261 (I)Lnet/sf/cglib/proxy/Callback; // Stack: 2, Locals: 2 public net.sf.cglib.proxy.Callback getCallback(int arg0); 0 aload_0 [this] 1 invokestatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$BIND_CALLBACKS(java.lang.Object) : void [40] 4 aload_0 [this] 5 iload_1 [arg0] 6 tableswitch default: 30 case 0: 24 24 getfield org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$CALLBACK_0 : net.sf.cglib.proxy.MethodInterceptor [36] 27 goto 32 30 pop 31 aconst_null 32 areturn // Method descriptor #263 (ILnet/sf/cglib/proxy/Callback;)V // Stack: 2, Locals: 3 public void setCallback(int arg0, net.sf.cglib.proxy.Callback arg1); 0 iload_1 [arg0] 1 tableswitch default: 31 case 0: 20 20 aload_0 [this] 21 aload_2 [arg1] 22 checkcast net.sf.cglib.proxy.MethodInterceptor [48] 25 putfield org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$CALLBACK_0 : net.sf.cglib.proxy.MethodInterceptor [36] 28 goto 31 31 return // Method descriptor #265 ()[Lnet/sf/cglib/proxy/Callback; // Stack: 5, Locals: 1 public net.sf.cglib.proxy.Callback[] getCallbacks(); 0 aload_0 [this] 1 invokestatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$BIND_CALLBACKS(java.lang.Object) : void [40] 4 aload_0 [this] 5 iconst_1 6 anewarray net.sf.cglib.proxy.Callback [251] 9 dup 10 iconst_0 11 aload_0 [this] 12 getfield org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$CALLBACK_0 : net.sf.cglib.proxy.MethodInterceptor [36] 15 aastore 16 areturn // Method descriptor #231 ([Lnet/sf/cglib/proxy/Callback;)V // Stack: 5, Locals: 2 public void setCallbacks(net.sf.cglib.proxy.Callback[] arg0); 0 aload_0 [this] 1 aload_1 [arg0] 2 dup2 3 iconst_0 4 aaload 5 checkcast net.sf.cglib.proxy.MethodInterceptor [48] 8 putfield org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$CALLBACK_0 : net.sf.cglib.proxy.MethodInterceptor [36] 11 return // Method descriptor #17 ()V // Stack: 0, Locals: 0 static {}; 0 invokestatic org.smvc.test.aop.TableDao$$EnhancerByCGLIB$$d519990f.CGLIB$STATICHOOK2() : void [269] 3 return } ```