2013-02-28 55 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