<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 6135字,需瀏覽 13分鐘

           ·

          2020-11-07 04:10

          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);??
          }??

          通常,我們在業(yè)務(wù)中編寫 AOP 攔截代碼時,都會接觸到這個 ProceedingJoinPoint 參數(shù),然后調(diào)用他的 proceed 方法調(diào)用目標方法。

          這個 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>



          戳原文,獲取精選面試題!
          瀏覽 66
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  婷婷免费av | 男人的天堂久草视频 | 欧美操在线观看视频 | 日韩人人操 | 人人夜夜i日日 |