之前一論壇朋友問的,復制保存下。 原文地址。
?
問題
貌似不能攔截私有方法??
試了很多次,都失敗了,是不是不行?。?
我想了一下,因為aop底層是代理,?
jdk是代理接口,私有方法必然不會存在在接口里,所以就不會被攔截到;?
cglib是子類,private的方法照樣不會出現在子類里,也不能被攔截。?
我不是類內部直接調用方法,而是通過維護一個自身實例的代理?
execution(* test.aop.ServiceA.*(..))?
- public ? class ?ServiceA?{??
- ??
- ???? private ?ServiceA??self;??
- ??
- ???? public ? void ?setSelf(ServiceA?self)?{??
- ???????? this .self?=?self;??
- ????}??
- ??
- ???? public ?String?methodA(String?str)?{??
- ????????System.out.println( "methodA:?args=" ?+?str);??
- ????????self.methodB( "b" );??
- ???????? return ? "12345" ?+?str;??
- ????}??
- ??
- ???? private ?String?methodB(String?str)?{??
- ????????System.out.println( "methodB:?args=" ?+?str);??
- ????????self.methodC( "c" );??
- ???????? return ? "12345" ?+?str;??
- ????}??
- ??
- ???? public ?String?methodC(String?str)?{??
- ????????System.out.println( "methodC:?args=" ?+?str);??
- ???????? return ? "12345" ?+?str;??
- ????}??
- }??
如果外部調用methodA,那么methodA和methodC會被攔截到,methodB不行?
是不是這么回事??
但是stackoverflow上,有人說 it works fine?
http://stackoverflow.com/questions/4402009/aspectj-and-catching-private-or-inner-methods?
execution(
public
?* test.aop.ServiceA.*(..))?
還有個奇怪的現象,execution里如果不寫權限,那么public protected package的方法都能被攔截到?
如果寫了public,那就只攔截public方法這個沒問題,?
如果寫了protected,他就什么事情都不做,連protected的方法也不攔截。
?
分析
private方法 在Spring使用純Spring AOP(只能攔截public/protected/包)都是無法被攔截的 因為子類無法覆蓋;包級別能被攔截的原因是,如果子類和父類在同一個包中是能覆蓋的。?
在cglib代理情況下, execution(* *(..)) 可以攔截 public/protected/包級別方法(即這些方法都是能代理的)。?
- private ? static ? boolean ?isOverridable(Method?method,?Class?targetClass)?{??
- ???????? if ?(Modifier.isPrivate(method.getModifiers()))?{??
- ???????????? return ? false ;??
- ????????}??
- ???????? if ?(Modifier.isPublic(method.getModifiers())?||?Modifier.isProtected(method.getModifiers()))?{??
- ???????????? return ? true ;??
- ????????}??
- ???????? return ?getPackageName(method.getDeclaringClass()).equals(getPackageName(targetClass));??
- ????}??
如果想要實現攔截private方法的 可以使用 原生 AspectJ 編譯期/運行期織入。?
原因基本分析明白了:?
是否能應用增強的判斷代碼如下(org.springframework.aop.support.AopUtils):?
- public ? static ? boolean ?canApply(Pointcut?pc,?Class?targetClass,? boolean ?hasIntroductions)?{??
- ???? if ?(!pc.getClassFilter().matches(targetClass))?{??
- ???????? return ? false ;??
- ????}??
- ??
- ????MethodMatcher?methodMatcher?=?pc.getMethodMatcher();??
- ????IntroductionAwareMethodMatcher?introductionAwareMethodMatcher?=? null ;??
- ???? if ?(methodMatcher? instanceof ?IntroductionAwareMethodMatcher)?{??
- ????????introductionAwareMethodMatcher?=?(IntroductionAwareMethodMatcher)?methodMatcher;??
- ????}??
- ??
- ????Set?classes?=? new ?HashSet(ClassUtils.getAllInterfacesForClassAsSet(targetClass));??
- ????classes.add(targetClass);??
- ???? for ?(Iterator?it?=?classes.iterator();?it.hasNext();)?{??
- ????????Class?clazz?=?(Class)?it.next();??
- ????????Method[]?methods?=?clazz.getMethods();??
- ???????? for ?( int ?j?=? 0 ;?j?<?methods.length;?j++)?{??
- ???????????? if ?((introductionAwareMethodMatcher?!=? null ?&&??
- ????????????????????introductionAwareMethodMatcher.matches(methods[j],?targetClass,?hasIntroductions))?||??
- ????????????????????methodMatcher.matches(methods[j],?targetClass))?{??
- ???????????????? return ? true ;??
- ????????????}??
- ????????}??
- ????}??
- ??
- ???? return ? false ;??
- }??
此處Method[] methods = clazz.getMethods();只能拿到public方法。。?
場景1:execution(* *(..))?
- public ? class ?Impl2??{??
- ??????
- ???? protected / public ?String?testAop2()?{??
- ????????System.out.println( "234" );??
- ???????? return ? "1233" ;??
- ????}??
- }??
因為切入點沒有訪問修飾符,即可以是任意,因此canApply方法能拿到如wait這種public方法,即可以實施代理。?
場景2:execution(public * *(..))?
- public ? class ?Impl2??{??
- ??????
- ???? public ?String?testAop2()?{??
- ????????System.out.println( "234" );??
- ???????? return ? "1233" ;??
- ????}??
- }??
因為攔截public的,因此canApply方法能拿到如wait這種public方法,即可以實施代理。?
場景3:execution(protected * *(..))?
- public ? class ?Impl2??{??
- ??????
- ???? protected ?String?testAop2()?{??
- ????????System.out.println( "234" );??
- ???????? return ? "1233" ;??
- ????}??
- }??
還記得之前說過,在canApply方法中 的 Method[] methods = clazz.getMethods();只能拿到public方法的,因此跟protected訪問修飾符是無法匹配的,所以如果“execution(protected * *(..))” 是 無法代理的。?
這就是為什么execution(protected * *(..))在純Spring AOP環境下不行的原因。?
注,@Transactional注解事務的特殊情況:?
在使用代理的時候,@Transactional 注解應該只被應用到 public 可見度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,系統也不會報錯, 但是這個被注解的方法將不會執行已配置的事務設置。如果你非要注解非公共方法的話,請參考使用AspectJ?
關于spring切入點語法可以參考我的博客 【
http://jinnianshilongnian.iteye.com/blog/1420691
】
?
?
?
實際運行時在方法攔截的時候,如果某個類不需要被代理,就直接調用這個類實例的方法,而不是這個類的代理的方法,?
如果需要代理,再匹配方法名和修飾符??
對于上面這個帖子里,之所以protected方法能被無訪問修修飾符的execution攔截,是因為這個類里面其他public方法被execution匹配了,導致spring認為這個類可以被代理,而不是protected的方法本身被execution匹配?
?
是的。?
?
如果需要代理,再匹配方法名和修飾符?
這個只看Cglib2AopProxy吧:?
- public ? int ?accept(Method?method)?{??
- ???????????? if ?(AopUtils.isFinalizeMethod(method))?{??
- ????????????????logger.debug( "Found?finalize()?method?-?using?NO_OVERRIDE" );??
- ???????????????? return ?NO_OVERRIDE;??
- ????????????}??
- ???????????? if ?(! this .advised.isOpaque()?&&?method.getDeclaringClass().isInterface()?&&??
- ????????????????????method.getDeclaringClass().isAssignableFrom(Advised. class ))?{??
- ???????????????? if ?(logger.isDebugEnabled())?{??
- ????????????????????logger.debug( "Method?is?declared?on?Advised?interface:?" ?+?method);??
- ????????????????}??
- ???????????????? return ?DISPATCH_ADVISED;??
- ????????????}??
- ???????????? //?We?must?always?proxy?equals,?to?direct?calls?to?this. ??
- ???????????? if ?(AopUtils.isEqualsMethod(method))?{??
- ????????????????logger.debug( "Found?'equals'?method:?" ?+?method);??
- ???????????????? return ?INVOKE_EQUALS;??
- ????????????}??
- ???????????? //?We?must?always?calculate?hashCode?based?on?the?proxy. ??
- ???????????? if ?(AopUtils.isHashCodeMethod(method))?{??
- ????????????????logger.debug( "Found?'hashCode'?method:?" ?+?method);??
- ???????????????? return ?INVOKE_HASHCODE;??
- ????????????}??
- ????????????Class?targetClass?=? this .advised.getTargetClass();??
- ???????????? //?Proxy?is?not?yet?available,?but?that?shouldn't?matter. ??
- ????????????List?chain?=? this .advised.getInterceptorsAndDynamicInterceptionAdvice(method,?targetClass);??
- ???????????? boolean ?haveAdvice?=?!chain.isEmpty();??
- ???????????? boolean ?exposeProxy?=? this .advised.isExposeProxy();??
- ???????????? boolean ?isStatic?=? this .advised.getTargetSource().isStatic();??
- ???????????? boolean ?isFrozen?=? this .advised.isFrozen();??
- ???????????? if ?(haveAdvice?||?!isFrozen)?{??
- ???????????????? //?If?exposing?the?proxy,?then?AOP_PROXY?must?be?used. ??
- ???????????????? if ?(exposeProxy)?{??
- ???????????????????? if ?(logger.isDebugEnabled())?{??
- ????????????????????????logger.debug( "Must?expose?proxy?on?advised?method:?" ?+?method);??
- ????????????????????}??
- ???????????????????? return ?AOP_PROXY;??
- ????????????????}??
- ????????????????String?key?=?method.toString();??
- ???????????????? //?Check?to?see?if?we?have?fixed?interceptor?to?serve?this?method. ??
- ???????????????? //?Else?use?the?AOP_PROXY. ??
- ???????????????? if ?(isStatic?&&?isFrozen?&&? this .fixedInterceptorMap.containsKey(key))?{??
- ???????????????????? if ?(logger.isDebugEnabled())?{??
- ????????????????????????logger.debug( "Method?has?advice?and?optimisations?are?enabled:?" ?+?method);??
- ????????????????????}??
- ???????????????????? //?We?know?that?we?are?optimising?so?we?can?use?the ??
- ???????????????????? //?FixedStaticChainInterceptors. ??
- ???????????????????? int ?index?=?((Integer)? this .fixedInterceptorMap.get(key)).intValue();??
- ???????????????????? return ?(index?+? this .fixedInterceptorOffset);??
- ????????????????}??
- ???????????????? else ?{??
- ???????????????????? if ?(logger.isDebugEnabled())?{??
- ????????????????????????logger.debug( "Unable?to?apply?any?optimisations?to?advised?method:?" ?+?method);??
- ????????????????????}??
- ???????????????????? return ?AOP_PROXY;??
- ????????????????}??
- ????????????}??
- ???????????? else ?{??
- ???????????????? //?See?if?the?return?type?of?the?method?is?outside?the?class?hierarchy ??
- ???????????????? //?of?the?target?type.?If?so?we?know?it?never?needs?to?have?return?type ??
- ???????????????? //?massage?and?can?use?a?dispatcher. ??
- ???????????????? //?If?the?proxy?is?being?exposed,?then?must?use?the?interceptor?the ??
- ???????????????? //?correct?one?is?already?configured.?If?the?target?is?not?static?cannot ??
- ???????????????? //?use?a?Dispatcher?because?the?target?can?not?then?be?released. ??
- ???????????????? if ?(exposeProxy?||?!isStatic)?{??
- ???????????????????? return ?INVOKE_TARGET;??
- ????????????????}??
- ????????????????Class?returnType?=?method.getReturnType();??
- ???????????????? if ?(targetClass?==?returnType)?{??
- ???????????????????? if ?(logger.isDebugEnabled())?{??
- ????????????????????????logger.debug( "Method?" ?+?method?+??
- ???????????????????????????????? "has?return?type?same?as?target?type?(may?return?this)?-?using?INVOKE_TARGET" );??
- ????????????????????}??
- ???????????????????? return ?INVOKE_TARGET;??
- ????????????????}??
- ???????????????? else ? if ?(returnType.isPrimitive()?||?!returnType.isAssignableFrom(targetClass))?{??
- ???????????????????? if ?(logger.isDebugEnabled())?{??
- ????????????????????????logger.debug( "Method?" ?+?method?+??
- ???????????????????????????????? "?has?return?type?that?ensures?this?cannot?be?returned-?using?DISPATCH_TARGET" );??
- ????????????????????}??
- ???????????????????? return ?DISPATCH_TARGET;??
- ????????????????}??
- ???????????????? else ?{??
- ???????????????????? if ?(logger.isDebugEnabled())?{??
- ????????????????????????logger.debug( "Method?" ?+?method?+??
- ???????????????????????????????? "has?return?type?that?is?assignable?from?the?target?type?(may?return?this)?-?" ?+??
- ???????????????????????????????? "using?INVOKE_TARGET" );??
- ????????????????????}??
- ???????????????????? return ?INVOKE_TARGET;??
- ????????????????}??
- ????????????}??
- ????????}??
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
?
即如果此方法有對應的advice就走代理。?
//getInterceptorsAndDynamicInterceptionAdvice代碼如下所示:?
- public ?List?getInterceptorsAndDynamicInterceptionAdvice(Method?method,?Class?targetClass)?{??
- ????MethodCacheKey?cacheKey?=? new ?MethodCacheKey(method);??
- ????List?cached?=?(List)? this .methodCache.get(cacheKey);??
- ???? if ?(cached?==? null )?{??
- ????????cached?=? this .advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(??
- ???????????????? this ,?method,?targetClass);? //轉調DefaultAdvisorChainFactory ??
- ???????? this .methodCache.put(cacheKey,?cached);??
- ????}??
- ???? return ?cached;??
- }??
也就是說需要一次切入點的匹配,即如果方法有切入點就走代理方法 否則目標方法。?
再來看CglibMethodInvocation(cglib的 DynamicAdvisedInterceptor使用):?
?
// We start with an index of -1 and increment early.?
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {?
return invokeJoinpoint();?
}?
Object interceptorOrInterceptionAdvice =?
??? this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);?
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {?
// Evaluate dynamic method matcher here: static part will already have?
// been evaluated and found to match.?
InterceptorAndDynamicMethodMatcher dm =?
??? (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;?
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {?
return dm.interceptor.invoke(this);?
}?
else {?
// Dynamic matching failed.?
// Skip this interceptor and invoke the next in the chain.?
return proceed();?
}?
}?
else {?
// It's an interceptor, so we just invoke it: The pointcut will have?
// been evaluated statically before this object was constructed.?
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);?
}?
}?
???????????????? /**?
* Gives a marginal performance improvement versus using reflection to?
* invoke the target when invoking public methods.?
*/?
protected Object invokeJoinpoint() throws Throwable {?
if (this.protectedMethod) {?
return super.invokeJoinpoint();?
}?
else {?
return this.methodProxy.invoke(this.target, this.arguments);?
}?
}?
}?
即如果有InterceptorAndDynamicMethodMatcher 這是動態切入點切入點匹配器:?
引用spring文檔?
?
動態切入點比起靜態切入點在執行時要消耗更多的資源。它們同時計算方法參數和靜態信息。 這意味著它們必須在每次方法調用時都被計算;由于參數的不同,結果不能被緩存。?
動態切入點的主要例子是控制流切入點。?
這個在spring aop中只有一種情況:PerTargetInstantiationModelPointcut 這個切入點;這個可以參考《
【第六章】 AOP 之 6.8 切面實例化模型 ——跟我學spring3?
》 pertarget。?
也就是說如果是?
靜態切入點代理:如果有匹配的advice就走代理;?
動態切入點代理:需要在運行時進行匹配。?
綜上所述:?
execution(* *(..)) 可以匹配public/protected的,因為public的有匹配的了,目標類就代理了,,,再進行切入點匹配時也是能匹配的,而且cglib方式能拿到包級別/protected方法,而且包級別/protected方法可以直接通過反射調用。?
?
對于上面這個帖子里,之所以protected方法能被無訪問修修飾符的execution攔截,是因為這個類里面其他public方法被execution匹配了,導致spring認為這個類可以被代理,而不是protected的方法本身被execution匹配?
這個是因為protected 修飾符的切入點 無法匹配 Method[] methods = clazz.getMethods(); 這里的任何一個,因此無法代理的。?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
