任意のオブジェクトのメソッド実行を傍受すると、それらのオブジェクトを指し示す可能性のある注釈付きフィールドへの接続がないため、すぐに使える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
のようなプリミティブ型を持っている場合、何が起こるかテストしていません無関係な重要なメンバーがいくつか等しいオブジェクトを作成するためです。私はまた、オート(アン)ボクシングについて何が起こるか、自分で試してみてくださいをテストしていません。
- アスペクトコードはやや複雑で、おそらく驚くほど速くありません。
- 私はまだ考えていない他の問題がないかもしれないことを保証することはできません。
境界条件とユースケースを管理している場合、必要なものを達成するために、情報に基づいた決定を下してそのコードをそのまま使用することもできます。コードは、おそらく改善の可能性を秘めている、私は興味があったし、概念実証をハックしたかったです。
私が疑ったことを確認してくれてありがとう、すぐに使えるAJでやりたいことをすることはできません。 私はAJにはとても新しいですが、POCで何をしようとしているのか分かります。非常に面白くて教育的! ありがとうございます! – JustOneMoreQuestion