2013-02-28 10 views
8

Chcę napisać punkt, który dopasowuje wykonywanie publicznych metod do pola z adnotacjami. To jakoś nigdy nie działa. Funkcja get (@Important) działa zgodnie z oczekiwaniami (sama w sobie), ale oczywiście dopasuje cały dostęp do pola. Chcę ograniczyć to do wykonywania tylko publicznych metod.Aspectj Punkt cięcia do dopasowywania publicznych wywołań metod w opisanym polu

Czy to w ogóle możliwe? I nikt nie błąd kompilacji, ale z drugiej strony nie wydaje się do pracy ..


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); 
    } 
} 

działa:

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

nie działa:

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

Odpowiedz

2

Dla tego, czego chcesz, nie ma gotowego rozwiązania AspectJ, ponieważ przechwytując metody wykonywania dowolnych obiektów, nie ma połączenia z opisanymi polami, które mogą wskazywać na te obiekty. Byłoby łatwiej przechwycić metody wykonywania przypisanych klas lub metod z adnotacjami, ale nie jest to tym, co chcesz zrobić.

Oto mały przykład kodu, który pokazuje sposób obejścia problemu, ale także jego ograniczenia:

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); } 
    } 
} 

Jak widać, mój aspekt utrzymuje zestaw „ważne obiekty ". Dokładniej, jest to Map, w którym klucze są "ważnymi obiektami", a wartości są zestawami stron odsyłających. Jest to konieczne, ponieważ teoretycznie kilka obiektów odsyłających (na przykład obiekty Visitors) może wskazywać identyczne "ważne obiekty" (na przykład konkretną Counter). W starszej wersji mojego przykładowego kodu, kiedy właśnie nagrałem "ważne obiekty" w prostym zestawie, miałem do wyboru, albo nigdy nie usuwać wcześniej "ważnych obiektów" z zestawu, nawet gdy nie były już przywoływane, albo zawsze usuwając je nawet jeśli drugi referrer wciąż wskazywał na "ważny obiekt". Podejście mapowe pozwala mi nagrywać wiele odwołań na "ważny obiekt".

Jeśli prowadzisz Visitors.main(String[]), pojawi się następujący komunikat (proszę odkomentuj porady before ... : unimportantSetter ... jeśli chcesz zobaczyć jeszcze zalogować wyjście):

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] 

Proszę uważnie porównać kod w main do wyjścia dziennika aby sprawdzić, które przypadki regularne i specjalne przetestowałem.

Jak powiedziałem, podejście ma swoje ograniczenia:

  • Nie testowałem tego, co się stanie, jeśli ważne pola mają prymitywne typy jak int lub są String s, które teoretycznie mogą wystąpić wiele razy jako „ważnych obiektów” ponieważ kilku niepowiązanych ważnych członków tworzy równe obiekty. Nie testowałem również tego, co dzieje się w przypadku boksów (nie), spróbuj sam.
  • Kod aspektu jest nieco skomplikowany i prawdopodobnie nie jest niesamowicie szybki.
  • Nie mogę zagwarantować, że nie mogą być inne problemy, o których jeszcze nie pomyślałem.

Ale jeśli kontrolujesz warunki brzegowe i przypadki użycia, możesz podjąć świadomą decyzję i użyć kodu w niezmienionej postaci lub w innym wariancie, aby osiągnąć to, czego potrzebujesz. Kod prawdopodobnie ma potencjał do poprawy, byłem po prostu ciekawy i chciałem zhakować dowód koncepcji.

+1

Dzięki za potwierdzenie tego, co podejrzewałem, niemożliwe jest zrobienie tego, co chciałem, z gotowym AJ. Chociaż jestem bardzo nowy w AJ, widzę, co próbujesz zrobić w swoim POC. Bardzo interesujące i edukacyjne! Dziękujemy! – JustOneMoreQuestion

0

chcesz użyć withinCode punkt przekroju, tak:

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

Wystarczy popatrzeć na AspectJ programming guide.

+1

To nadal przechwytuje cały dostęp do pola z adnotacjami. Chcę tylko dopasować wykonywanie publicznych metod w polu z adnotacjami. – JustOneMoreQuestion

Powiązane problemy