2013-02-28 9 views
8

注釈付きフィールドにパブリックメソッドの実行に一致するポイントカットを書きたいと思います。これはどのように動作していないようです。 get(@Important)は期待通りに機能しますが、それはもちろんフィールドへのすべてのアクセスと一致します。私はこれを公開メソッドの実行のみに制限したいと思います。Aspectj注釈付きフィールドのパブリックメソッド呼び出しを一致させるためのポイントカット

これはまったく可能ですか?

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

動作しません:


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 * *) && 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)を指すことができるため、これは必要です。以前のバージョンのサンプルコードでは、「重要なオブジェクト」をシンプルなセットに記録したとき、もはや参照されなくても、または常にそれらを削除しても、以前は「重要なオブジェクト」をセットから削除することはできませんでした2番目のリファラーが依然として「重要なオブジェクト」を指している場合マップ手法を使用すると、「重要なオブジェクト」ごとに複数の参照元を記録できます。

あなたが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のようなプリミティブ型を持っている場合、何が起こるかテストしていません無関係な重要なメンバーがいくつか等しいオブジェクトを作成するためです。私はまた、オート(アン)ボクシングについて何が起こるか、自分で試してみてくださいをテストしていません。
  • アスペクトコードはやや複雑で、おそらく驚くほど速くありません。
  • 私はまだ考えていない他の問題がないかもしれないことを保証することはできません。

境界条件とユースケースを管理している場合、必要なものを達成するために、情報に基づいた決定を下してそのコードをそのまま使用することもできます。コードは、おそらく改善の可能性を秘めている、私は興味があったし、概念実証をハックしたかったです。

+1

私が疑ったことを確認してくれてありがとう、すぐに使えるAJでやりたいことをすることはできません。 私はAJにはとても新しいですが、POCで何をしようとしているのか分かります。非常に面白くて教育的! ありがとうございます! – JustOneMoreQuestion

0

あなたはこのように、withinCodeポイントカットを使用したい:

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

AspectJ programming guideを見てください。

+1

これでも、注釈付きフィールドへのすべてのアクセスがキャッチされます。私は、注釈付きフィールド上のパブリックメソッドの実行を合わせたいです。 – JustOneMoreQuestion

関連する問題