1. 程式人生 > 其它 >關於SpingAop切面攔截的問題 protect方法代理問題

關於SpingAop切面攔截的問題 protect方法代理問題

技術標籤:Springjavaaopjavaspring

問題

貌似不能攔截私有方法?
試了很多次,都失敗了,是不是不行啊?

我想了一下,因為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 編譯期/執行期織入。

引用

如果寫了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