Aspectj Pointcut用于在带注释的字段上匹配公共方法调用

时间:2013-02-28 15:02:31

标签: java aop aspectj

我想写一个与注释字段上的公共方法执行相匹配的切入点。这怎么可能不起作用。 get(@Important)按预期(单独)工作,但它当然会匹配对该字段的所有访问。我想将此限制为仅公共方法执行。

这有可能吗?我没有编译错误,但另一方面它似乎没有工作..


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 个答案:

答案 0 :(得分: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 Visitors@1404536: null -> Counter@7fdcde[count=0]
Unimportant method on Visitors@1404536: execution(void Visitors.increaseCounter())
Important method on Counter@7fdcde[count=0]: execution(void Counter.add(int))
Important method on Counter@7fdcde[count=1]: execution(void Counter.add(int))
visitors.counter = Counter@7fdcde[count=4]
--------------------
Unimportant method on Counter@18ac738[count=0]: execution(void Counter.add(int))
Unimportant method on Counter@18ac738[count=11]: execution(void Counter.add(int))
unimportantCounter = Counter@18ac738[count=33]
--------------------
Important method on Counter@7fdcde[count=4]: execution(void Counter.add(int))
visitors.counter = Counter@7fdcde[count=9]
unimportantCounter = Counter@7fdcde[count=9]
--------------------
Important object for target Visitors@1404536: Counter@7fdcde[count=9] -> Counter@1d6096[count=0]
Unimportant method on Visitors@1404536: execution(void Visitors.increaseCounter())
Important method on Counter@1d6096[count=0]: execution(void Counter.add(int))
Important method on Counter@1d6096[count=1]: execution(void Counter.add(int))
Unimportant method on Counter@7fdcde[count=9]: execution(void Counter.add(int))
visitors.counter = Counter@1d6096[count=4]
unimportantCounter = Counter@7fdcde[count=109]
--------------------
Important object for target Visitors@b02e7a: null -> Counter@bb6ab6[count=0]
Unimportant method on Visitors@b02e7a: execution(void Visitors.increaseCounter())
Important method on Counter@bb6ab6[count=0]: execution(void Counter.add(int))
Important method on Counter@bb6ab6[count=1]: execution(void Counter.add(int))
otherVisitors.counter = Counter@bb6ab6[count=51]
--------------------
Important object for target Visitors@b02e7a: Counter@bb6ab6[count=51] -> Counter@1d6096[count=4]
visitors.counter = Counter@1d6096[count=4]
otherVisitors.counter = Counter@1d6096[count=4]
--------------------
Important object for target Visitors@b02e7a: Counter@1d6096[count=4] -> Counter@5afd29[count=0]
Unimportant method on Visitors@1404536: execution(void Visitors.increaseCounter())
Important method on Counter@1d6096[count=4]: execution(void Counter.add(int))
Unimportant method on Visitors@b02e7a: execution(void Visitors.increaseCounter())
Important method on Counter@5afd29[count=0]: execution(void Counter.add(int))
visitors.counter = Counter@1d6096[count=5]
otherVisitors.counter = Counter@5afd29[count=1]

请仔细比较main中的代码与日志输出,以了解我测试过的常规和特殊情况。

正如我所说,这种方法有其局限性:

  • 我没有测试过,如果重要字段具有intString这样的原始类型,理论上可以多次作为“重要对象”出现会发生什么,因为几个不相关的重要成员创建了相同的对象。我还没有测试过有关自动(联合)拳击的事情,请自己尝试。
  • 方面代码有点复杂,可能不会非常快。
  • 我不能保证可能还有其他我没有想过的问题。

但是,如果您控制边界条件和用例,您可以做出明智的决定并按原样使用代码或其变体,以实现您的需求。代码可能有改进的潜力,我只是很好奇,想要破解概念证明。

答案 1 :(得分:0)

您想要使用withinCode切入点,如下所示:

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

查看AspectJ programming guide

相关问题