關於SpingAop切面攔截的問題 protect方法代理問題
問題
貌似不能攔截私有方法?
試了很多次,都失敗了,是不是不行啊?
我想了一下,因為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方法這個沒問題,
分析
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 編譯期/執行期織入。
引用
如果寫了protected,他就什麼事情都不做,連protected的方法也不攔截;這個應該不會
原因基本分析明白了:
是否能應用增強的判斷程式碼如下(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
在使用代理的時候,@Transactional 註解應該只被應用到 public 可見度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,系統也不會報錯, 但是這個被註解的方法將不會執行已配置的事務設定。如果你非要註解非公共方法的話,請參考使用AspectJ