javascript
说说 Spring AOP 原理
jdk中的動(dòng)態(tài)代理通過反射類Proxy和InvocationHandler回調(diào)接口實(shí)現(xiàn),要求委托類必須實(shí)現(xiàn)一個(gè)接口,只能對(duì)該類接口中定義的方法實(shí)現(xiàn)代理,這在實(shí)際編程中有一定的局限性。
其原理完全基于反射。
cglib實(shí)現(xiàn)(不僅僅是反射,其主要是Java 字節(jié)碼生成技術(shù))
使用cglib[Code Generation Library]實(shí)現(xiàn)動(dòng)態(tài)代理,并不要求委托類必須實(shí)現(xiàn)接口,底層采用asm字節(jié)碼生成框架生成代理類的字節(jié)碼,下面通過一個(gè)例子看看使用CGLib如何實(shí)現(xiàn)動(dòng)態(tài)代理。
1、定義業(yè)務(wù)邏輯
2、實(shí)現(xiàn)MethodInterceptor接口,定義方法的攔截器
public class MyMethodInterceptor implements MethodInterceptor { public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable { System.out.println("Before:" + method); Object object = proxy.invokeSuper(obj, arg); System.out.println("After:" + method); return object; } }3、利用Enhancer類生成代理類;
Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserServiceImpl.class); enhancer.setCallback(new MyMethodInterceptor()); UserServiceImpl userService = (UserServiceImpl)enhancer.create(); userService.add();4、userService.add()的執(zhí)行結(jié)果:
Before: add This is add service After: add 代理對(duì)象的生成過程由Enhancer類實(shí)現(xiàn),大概步驟如下:
1、生成代理類Class的二進(jìn)制字節(jié)碼;
2、通過Class.forName加載二進(jìn)制字節(jié)碼,生成Class對(duì)象;
3、通過反射機(jī)制獲取實(shí)例構(gòu)造,并初始化代理類對(duì)象。
cglib字節(jié)碼生成
Enhancer是CGLib的字節(jié)碼增強(qiáng)器,可以方便的對(duì)類進(jìn)行擴(kuò)展,內(nèi)部調(diào)用GeneratorStrategy.generate方法生成代理類的字節(jié)碼,通過以下方式可以生成class文件。
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\\\Code\\\\whywhy\\\\target\\\\classes\\\\zzzzzz")使用?反編譯工具 procyon?查看代理類實(shí)現(xiàn)
java -jar procyon-decompiler-0.5.30.jar UserService$$EnhancerByCGLIB$$394dddeb;反編譯之后的代理類add方法實(shí)現(xiàn)如下:
import net.sf.cglib.core.Signature; import net.sf.cglib.core.ReflectUtils; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Factory;// // Decompiled by Procyon v0.5.30 // public class UserService$$EnhancerByCGLIB$$394dddeb extends UserService 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$add$0$Method; private static final MethodProxy CGLIB$add$0$Proxy; private static final Object[] CGLIB$emptyArgs; static void CGLIB$STATICHOOK2() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; final Class<?> forName = Class.forName("UserService$$EnhancerByCGLIB$$394dddeb"); final Class<?> forName3; CGLIB$add$0$Method = ReflectUtils.findMethods(new String[] { "add", "()V" }, (forName3 = Class.forName("UserService")).getDeclaredMethods())[0]; CGLIB$add$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "add", "CGLIB$add$0"); } final void CGLIB$add$0() { super.add(); } public final void add() { MethodInterceptor cglib$CALLBACK_2; MethodInterceptor cglib$CALLBACK_0; if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) { CGLIB$BIND_CALLBACKS(this); cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0); } if (cglib$CALLBACK_0 != null) { cglib$CALLBACK_2.intercept((Object)this, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Method, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$emptyArgs, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Proxy); return; } super.add(); } static { CGLIB$STATICHOOK2(); } } 通過cglib生成的字節(jié)碼相比jdk實(shí)現(xiàn)來說顯得更加復(fù)雜。
1、代理類UserService$$EnhancerByCGLIB$$394dddeb繼承了委托類UserSevice,且委托類的final方法不能被代理;
2、代理類為每個(gè)委托方法都生成兩個(gè)方法,以add方法為例,一個(gè)是重寫的add方法,一個(gè)是CGLIB$add$0方法,該方法直接調(diào)用委托類的add方法;
3、當(dāng)執(zhí)行代理對(duì)象的add方法時(shí),會(huì)先判斷是否存在實(shí)現(xiàn)了MethodInterceptor接口的對(duì)象cglib$CALLBACK_0,如果存在,則調(diào)用MethodInterceptor對(duì)象的intercept方法:
參數(shù)分別為:1、代理對(duì)象;2、委托類方法;3、方法參數(shù);4、代理方法的MethodProxy對(duì)象(方法代理對(duì)象)。
4、每個(gè)被代理的方法都對(duì)應(yīng)一個(gè)MethodProxy對(duì)象,methodProxy.invokeSuper方法最終調(diào)用委托類的add方法,實(shí)現(xiàn)如下:
public Object invokeSuper(Object obj, Object[] args) throws Throwable { try { init(); FastClassInfo fci = fastClassInfo; return fci.f2.invoke(fci.i2, obj, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } }單看invokeSuper方法的實(shí)現(xiàn),似乎看不出委托類add方法調(diào)用,在MethodProxy實(shí)現(xiàn)中,通過FastClassInfo維護(hù)了委托類和代理類的FastClass。
private static class FastClassInfo {FastClass f1;FastClass f2;int i1; int i2; }以add方法的methodProxy為例,f1指向委托類對(duì)象,f2指向代理類對(duì)象,i1和i2分別是方法add和CGLIB$add$0在對(duì)象中索引位置。
FastClass實(shí)現(xiàn)機(jī)制
FastClass其實(shí)就是對(duì)Class對(duì)象進(jìn)行特殊處理,提出下標(biāo)概念index,通過索引保存方法的引用信息,將原先的反射調(diào)用,轉(zhuǎn)化為方法的直接調(diào)用,從而體現(xiàn)所謂的fast,下面通過一個(gè)例子了解一下FastClass的實(shí)現(xiàn)機(jī)制。
1、定義原類
2、定義Fast類
class FastTest {public int getIndex(String signature){ switch(signature.hashCode()){ case 3078479: return 1; case 3108270: return 2; } return -1; } public Object invoke(int index, Object o, Object[] ol){ Test t = (Test) o; switch(index){ case 1: t.f(); return null; case 2: t.g(); return null; } return null; } }在FastTest中有兩個(gè)方法,getIndex中對(duì)Test類的每個(gè)方法根據(jù)hash建立索引,invoke根據(jù)指定的索引,直接調(diào)用目標(biāo)方法,避免了反射調(diào)用。所以當(dāng)調(diào)用methodProxy.invokeSuper方法時(shí),實(shí)際上是調(diào)用代理類的CGLIB$add$0方法,CGLIB$add$0直接調(diào)用了委托類的add方法。
jdk和cglib動(dòng)態(tài)代理實(shí)現(xiàn)的區(qū)別
1、jdk動(dòng)態(tài)代理生成的代理類和委托類實(shí)現(xiàn)了相同的接口;
2、cglib動(dòng)態(tài)代理中生成的字節(jié)碼更加復(fù)雜,生成的代理類是委托類的子類,且不能處理被final關(guān)鍵字修飾的方法;
3、jdk采用反射機(jī)制調(diào)用委托類的方法,cglib采用類似索引的方式直接調(diào)用委托類方法;
總結(jié)
以上是生活随笔為你收集整理的说说 Spring AOP 原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: flash怎么设计logo flash
- 下一篇: Spring 三种注入方式