Cglib 如何實現(xiàn)多重代理?

Java技術(shù)棧
www.javastack.cn
關(guān)注閱讀更多優(yōu)質(zhì)文章
作者:莫那·魯?shù)?/span>
來源:https://www.cnblogs.com/stateis0/p/9744123.html
由于 Cglib 本身的設(shè)計,無法實現(xiàn)在 Proxy 外面再包裝一層 Proxy(JDK ?Proxy 可以),通常會報如下錯誤:
Caused?by:?java.lang.ClassFormatError:?Duplicate?method?name?"newInstance"?with?signature?"..........??
at?java.lang.ClassLoader.defineClass1(Native?Method)??
at?java.lang.ClassLoader.defineClass(ClassLoader.java:763)??
...?10?more??
錯誤來源代碼:
net.sf.cglib.proxy.Enhancer#generateClass(ClassVisitor v)
......省略代碼??
//?以下部分的字節(jié)碼,每次生成 Proxy 實例都會插入。JVM 驗證字節(jié)碼時則會報錯。?
if?(useFactory?||?currentData?!=?null)?{??
????int[]?keys?=?getCallbackKeys();??
????emitNewInstanceCallbacks(e);??
????emitNewInstanceCallback(e);??
????emitNewInstanceMultiarg(e,?constructorInfo);??
????emitGetCallback(e,?keys);??
????emitSetCallback(e,?keys);??
????emitGetCallbacks(e);??
????emitSetCallbacks(e);??
}??
通過 dump 出來的字節(jié)碼查看則更為直觀:


生成的字節(jié)碼中,newInstance 方法是重復(fù)的。如何查看字節(jié)碼,我在公眾號Java技術(shù)棧有發(fā)布過三種方法,關(guān)注后搜索閱讀吧。
dump 方法:System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./");
如何處理?
實現(xiàn)多重代理,有一種蹩腳的方法,例如 JDK 和 Cglib 組合使用?;蛘吣阒苯邮褂?JDK 代理。但有時候,針對類的操作還行不通。
筆者參考 Spring 的做法,實現(xiàn)了一個簡單的多重代理。
Spring 的場景是:一個目標方法被多個 AOP 攔截,此時就需要多重代理。
Spring 創(chuàng)建代理的代碼位于 :org.springframework.aop.framework.CglibAopProxy#getProxy
Spring ?AOP 攔截器類:org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor
該類的 intercept 方法是實現(xiàn)多重代理的核心。
每次調(diào)用目標方法,都會根據(jù)目標方法,和目標方法的多個攔截點生成一個調(diào)用對象。
//?生成調(diào)用對象??
CglibMethodInvocation?c?=?new?CglibMethodInvocation(proxy,?target,?method,?args,?targetClass,?chain,?methodProxy);??
//?調(diào)用???
c.proceed();??
然后調(diào)用父類 ?proceed 方法,其實就是一個過濾器模式:
public?Object?proceed()?throws?Throwable?{??
????if?(this.currentInterceptorIndex?==?this.interceptorsAndDynamicMethodMatchers.size()?-?1)?{??
????????return?invokeJoinpoint();??
????}??
????Object?interceptorOrInterceptionAdvice?=??
????????????this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);??
????if?(interceptorOrInterceptionAdvice?instanceof?InterceptorAndDynamicMethodMatcher)?{??
????????InterceptorAndDynamicMethodMatcher?dm?=??
????????????????(InterceptorAndDynamicMethodMatcher)?interceptorOrInterceptionAdvice;??
????????if?(dm.methodMatcher.matches(this.method,?this.targetClass,?this.arguments))?{??
????????????return?dm.interceptor.invoke(this);??
????????}??
????????else?{??
????????????//?Skip?this?interceptor?and?invoke?the?next?in?the?chain.?遞歸.??
????????????return?proceed();??
????????}??
????}??
????else?{??
????????return?((MethodInterceptor)?interceptorOrInterceptionAdvice).invoke(this);??
????}??
}??
注意最后一行,這里就是調(diào)用攔截點的 invoke 方法,這個攔截點的具體實現(xiàn)類:AspectJAroundAdvice。
看下他的 invoke 方法:
public?Object?invoke(MethodInvocation?mi)?throws?Throwable?{??
????ProxyMethodInvocation?pmi?=?(ProxyMethodInvocation)?mi;??
???//?AOP?里熟悉的?ProceedingJoinPoint?參數(shù)!!!!??
????ProceedingJoinPoint?pjp?=?lazyGetProceedingJoinPoint(pmi);??
????JoinPointMatch?jpm?=?getJoinPointMatch(pmi);??
????return?invokeAdviceMethod(pjp,?jpm,?null,?null);??
}??
這個 ProceedingJoinPoint 類的 proceed 方法最終會回調(diào) DynamicAdvisedInterceptor 對的 proceed 方法。直到所有的攔截點全部執(zhí)行完畢。最終執(zhí)行目標類的方法。
所以,你設(shè)置的每個被攔截的方法,如果這個方法會被攔截多次,那么就會有多個 MethodInterceptor(不是 cglib 的)實例形成調(diào)用鏈。然后通過 ?ProceedingJoinPoint 傳遞給你攔截使用。
鋪墊了這么多,我們自己來實現(xiàn)一個簡單的,不能像 Spring 這么復(fù)雜?。。?!
簡單實現(xiàn) Cglib 多重代理
先說一下思路:事實上很簡單,只需要再攔截器里放一個過濾器鏈即可,用戶在過濾器里攔截多重調(diào)用。這些攔截器,就像你加 @Around 注解的方法,只不過我們這里沒有 Spring 那么方便而已。
畫個 UML 圖 :

代碼如下:
Test.java & SayHello.java
public?class?Test?{??
??
????public?static?void?main(String[]?args)?{??
????????Object?proxy?=?ProxyFactory.create().getProxy(new?SayHello());??
????????proxy.toString();??
????}??
??
??
????static?class?SayHello?{??
??
????????@Override??
????????public?String?toString()?{??
????????????return?"hello?cglib?!";??
????????}??
????}??
}??
?
ProxyFactory.java & Interceptor.java
public?class?ProxyFactory?{??
????private?ProxyFactory()?{}??
????public?static?ProxyFactory?create()?{??
????????return?new?ProxyFactory();??
????}??
????public?Object?getProxy(Object?origin)?{??
????????final?Enhancer?en?=?new?Enhancer();??
????????en.setSuperclass(origin.getClass());??
????????List?list?=?new?ArrayList<>();??
????????list.add(new?Point1());??
????????list.add(new?Point2());??
????????en.setCallback(new?Interceptor(new?Chain(list,?origin)));??
????????return?en.create();??
????}??
????private?class?Interceptor??
????????implements?MethodInterceptor?{??
????????Chain?chain;??
????????public?Interceptor(Chain?chain)?{??
????????????this.chain?=?chain;??
????????}??
????????@Override??
????????public?Object?intercept(Object?o,?Method?method,?Object[]?objects,?MethodProxy?methodProxy)??
????????????throws?Throwable?{??
????????????return?chain.proceed();??
????????}??
????}??
}??
?
Chain.java & Point.java
public?class?Chain?{??
????private?List?list;??
????private?int?index?=?-1;??
????private?Object?target;??
??
????public?Chain(List?list,?Object?target)?{??
????????this.list?=?list;??
????????this.target?=?target;??
????}??
??
????public?Object?proceed()?{??
????????Object?result;??
????????if?(++index?==?list.size())?{??
????????????result?=?(target.toString());??
????????????System.err.println("Target?Method?invoke?result?:?"?+?result);??
????????}?else?{??
????????????Point?point?=?list.get(index);??
????????????result?=?point.proceed(this);??
????????}??
????????return?result;??
????}??
????interface?Point?{??
????????Object?proceed(Chain?chain);??
????}??
}??
?
Point1.java & Point2.java
public?class?Point1?implements?Chain.Point?{??
??
????@Override??
????public?Object?proceed(Chain?chain)?{??
????????System.out.println("point?1?before");??
????????Sleep.sleep(20);??
????????Object?result?=?chain.proceed();??
????????Sleep.sleep(20);??
????????System.out.println("point?1?after");??
????????return?result;??
????}??
}??
public?class?Point2?implements?Chain.Point?{??
??
????@Override??
????public?Object?proceed(Chain?chain)?{??
????????System.out.println("point?2?before");??
????????Sleep.sleep(20);??
????????Object?result?=?chain.proceed();??
????????Sleep.sleep(20);??
????????System.out.println("point?2?after");??
????????return?result;??
????}??
}??
?
運行 Test main 結(jié)果:

符合預(yù)期。






關(guān)注Java技術(shù)??锤喔韶?/strong>


