2013-02-28 78 views
8

公共方法調用我想寫一個匹配上標註的字段的公共方法執行的切入點。這似乎並沒有工作。 get(@Important)按照您的預期(獨立)工作,但它當然會匹配對該字段的所有訪問權限。我想限制這隻公開方法執行。AspectJ切入點匹配上標註的字段

這可能嗎?我沒有得到任何編譯錯誤,但在另一方面,它似乎並沒有工作..


public class Counter { 
    private int count = 0; 

    public void add(int value) { 
    count = count + value; 
    } 
} 

public class Visitors { 
    @Important 
    Counter counter = new Counter() 

    public void increaseCounter() { 
    counter.add(1); 
    } 
} 

作品:

@Pointcut(value = "get(@Important * *)") 
void testPointCut() { 
} 

不起作用:

@Pointcut(value = "get(@Important * *) && execution(public * *(..))") 
void testPointCut() { 
} 

回答

2

對於你想要的,沒有開箱即用的AspectJ解決方案,因爲如果你攔截任何對象的方法執行,那麼就沒有連接到可能指向這些對象的帶註釋的字段。攔截註釋類或註釋方法的方法執行會更容易,但這不是您想要執行的操作。

這裏是一個小的代碼例子顯示了一個解決辦法,而且它的侷限性:

import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 

@Retention(RetentionPolicy.RUNTIME) 
public @interface Important {} 
public class Counter { 
    private int count = 0; 

    public void add(int value) { 
     count = count + value; 
    } 

    @Override 
    public String toString() { 
     return super.toString() + "[count=" + count + "]"; 
    } 
} 
public class Visitors { 
    @Important 
    Counter counter = new Counter(); 

    public void increaseCounter() { 
     counter.add(1); 
    } 

    public static void main(String[] args) { 
     Visitors visitors = new Visitors(); 
     visitors.increaseCounter(); 
     visitors.counter.add(3); 
     System.out.println("visitors.counter = " + visitors.counter); 
     System.out.println("--------------------"); 

     Counter unimportantCounter = new Counter(); 
     unimportantCounter.add(11); 
     unimportantCounter.add(22); 
     System.out.println("unimportantCounter = " + unimportantCounter); 
     System.out.println("--------------------"); 

     unimportantCounter = visitors.counter; 
     unimportantCounter.add(5); 
     System.out.println("visitors.counter = " + visitors.counter); 
     System.out.println("unimportantCounter = " + unimportantCounter); 
     System.out.println("--------------------"); 

     visitors.counter = new Counter(); 
     visitors.increaseCounter(); 
     visitors.counter.add(3); 
     unimportantCounter.add(100); 
     System.out.println("visitors.counter = " + visitors.counter); 
     System.out.println("unimportantCounter = " + unimportantCounter); 
     System.out.println("--------------------"); 

     Visitors otherVisitors = new Visitors(); 
     otherVisitors.increaseCounter(); 
     otherVisitors.counter.add(50); 
     System.out.println("otherVisitors.counter = " + otherVisitors.counter); 
     System.out.println("--------------------"); 

     otherVisitors.counter = visitors.counter; 
     System.out.println("visitors.counter = " + visitors.counter); 
     System.out.println("otherVisitors.counter = " + otherVisitors.counter); 
     System.out.println("--------------------"); 

     otherVisitors.counter = new Counter(); 
     visitors.increaseCounter(); 
     otherVisitors.increaseCounter(); 
     System.out.println("visitors.counter = " + visitors.counter); 
     System.out.println("otherVisitors.counter = " + otherVisitors.counter); 
    } 
} 
import java.lang.reflect.Field; 
import java.util.HashMap; 
import java.util.HashSet; 
import java.util.Map; 
import java.util.Set; 

import org.aspectj.lang.Signature; 
import org.aspectj.lang.SoftException; 

public aspect ImportantMethodInterceptor { 
    Map<Object, Set<Object>> importantObjects = new HashMap<Object, Set<Object>>(); 

    pointcut importantSetter(Object newValue, Object target) : 
     set(@Important * *) && args(newValue) && target(target); 
    pointcut unimportantSetter(Object newValue, Object target) : 
     !set(@Important * *) && set(* *) && !withincode(*.new(..)) && args(newValue) && target(target); 
    pointcut publicMethod(Object target) : 
     execution(public * *(..)) && target(target) && !execution(public String *..toString()); 

    before(Object newValue, Object target) : importantSetter(newValue, target) { 
     Object oldValue = getFieldValue(thisJoinPoint.getSignature(), target); 
     System.out.println("Important object for target " + target + ": " + oldValue + " -> " + newValue); 
     synchronized (importantObjects) { 
      Set<Object> referrers; 
      if (oldValue != null) { 
       referrers = importantObjects.get(oldValue); 
       if (referrers != null) { 
        referrers.remove(target); 
        if (referrers.size() == 0) 
         importantObjects.remove(oldValue); 
       } 
      } 
      if (newValue != null) { 
       referrers = importantObjects.get(newValue); 
       if (referrers == null) { 
        referrers = new HashSet<Object>(); 
        importantObjects.put(newValue, referrers); 
       } 
       referrers.add(target); 
      } 
     } 
    } 

// before(Object newValue, Object target) : unimportantSetter(newValue, target) { 
//  Object oldValue = getFieldValue(thisJoinPoint.getSignature(), target); 
//  System.out.println("Unimportant object for target " + target + ": " + oldValue + " -> " + newValue); 
// } 

    before(Object target) : publicMethod(target) { 
     synchronized (importantObjects) { 
      if (importantObjects.get(target) != null) 
       System.out.println("Important method on " + target + ": " + thisJoinPointStaticPart); 
      else 
       System.out.println("Unimportant method on " + target + ": " + thisJoinPointStaticPart); 
     } 
    } 

    private Object getFieldValue(Signature signature, Object target) { 
     try { 
      Field field = signature.getDeclaringType().getDeclaredField(signature.getName()); 
      field.setAccessible(true); 
      return field.get(target); 
     } 
     catch (Exception e) { throw new SoftException(e); } 
    } 
} 

正如你看到的,我一方面保留了一套「重要對象」。更確切地說,它是一個Map,其中鍵是「重要對象」,值是引用者集合。這是必要的,因爲理論上幾個引薦(例如Visitors對象)可以指向相同的「重要的對象」(例如一個特定的Counter)。在我的示例代碼的早期版本中,當我剛剛在一個簡單的集合中記錄「重要對象」時,我可以選擇永遠不要從集合中刪除以前的「重要對象」,即使它們不再被引用或者始終刪除它們如果第二位引用者仍然指向「重要對象」。地圖方法使我能夠爲每個「重要對象」記錄多個引用者。

如果運行Visitors.main(String[]),你會看到下面的輸出(請取消對before ... : unimportantSetter ...意見,如果你想看到更多的日誌輸出):

Important object for target [email protected]: null -> [email protected][count=0] 
Unimportant method on [email protected]: execution(void Visitors.increaseCounter()) 
Important method on [email protected][count=0]: execution(void Counter.add(int)) 
Important method on [email protected][count=1]: execution(void Counter.add(int)) 
visitors.counter = [email protected][count=4] 
-------------------- 
Unimportant method on [email protected][count=0]: execution(void Counter.add(int)) 
Unimportant method on [email protected][count=11]: execution(void Counter.add(int)) 
unimportantCounter = [email protected][count=33] 
-------------------- 
Important method on [email protected][count=4]: execution(void Counter.add(int)) 
visitors.counter = [email protected][count=9] 
unimportantCounter = [email protected][count=9] 
-------------------- 
Important object for target [email protected]: [email protected][count=9] -> [email protected][count=0] 
Unimportant method on [email protected]: execution(void Visitors.increaseCounter()) 
Important method on [email protected][count=0]: execution(void Counter.add(int)) 
Important method on [email protected][count=1]: execution(void Counter.add(int)) 
Unimportant method on [email protected][count=9]: execution(void Counter.add(int)) 
visitors.counter = [email protected][count=4] 
unimportantCounter = [email protected][count=109] 
-------------------- 
Important object for target [email protected]: null -> [email protected][count=0] 
Unimportant method on [email protected]: execution(void Visitors.increaseCounter()) 
Important method on [email protected][count=0]: execution(void Counter.add(int)) 
Important method on [email protected][count=1]: execution(void Counter.add(int)) 
otherVisitors.counter = [email protected][count=51] 
-------------------- 
Important object for target [email protected]: [email protected][count=51] -> [email protected][count=4] 
visitors.counter = [email protected][count=4] 
otherVisitors.counter = [email protected][count=4] 
-------------------- 
Important object for target [email protected]: [email protected][count=4] -> [email protected][count=0] 
Unimportant method on [email protected]: execution(void Visitors.increaseCounter()) 
Important method on [email protected][count=4]: execution(void Counter.add(int)) 
Unimportant method on [email protected]: execution(void Visitors.increaseCounter()) 
Important method on [email protected][count=0]: execution(void Counter.add(int)) 
visitors.counter = [email protected][count=5] 
otherVisitors.counter = [email protected][count=1] 

請仔細main代碼比較日誌輸出以便查看我測試過哪些常規和特殊情況。

正如我所說的,這種方法有其侷限性:

  • 我沒有測試過,如果重要的領域有原始類型,如int或者String S的理論上可多次出現的「重點關注對象」會發生什麼因爲幾個不相關的重要成員創造了相同的對象我也沒有測試過關於自動(非)拳擊的情況,請自行嘗試。
  • 方面代碼有點複雜,可能不是太快。
  • 我不能保證可能沒有其他問題,我還沒有想過。

但是,如果您是在控制邊界條件和用例,您可以做出明智的決定並按原樣或其變體使用代碼,以實現您所需的功能。代碼可能有改進的潛力,我只是好奇,想要破解一個概念證明。

+1

感謝您確認我所懷疑的事情,不可能通過開箱即用的AJ做我想做的事。 儘管我對AJ很陌生,但我可以在您的POC中看到您要做的事情。非常有趣和教育! 謝謝! – JustOneMoreQuestion 2013-03-26 08:57:26

0

您要使用的withinCode切入點,就像這樣:

@Pointcut(value = "get(@Important * *) && withinCode(public * *(..))") 
void testPointCut() { 
} 

看一看的AspectJ programming guide

+1

這仍然捕獲對帶註釋字段的所有訪問。我只想匹配註釋字段上公共方法的執行。 – JustOneMoreQuestion 2013-03-01 09:46:48