8

我想编写一个切入点,该切入点与注释字段上公共方法的执行相匹配。这似乎不起作用。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() {
}

4

2 回答 2

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和日志输出,看看我测试了哪些常规和特殊情况。

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

  • 我没有测试如果重要字段具有原始类型int或 are Strings 会发生什么,理论上可以多次作为“重要对象”出现,因为几个不相关的重要成员创建相同的对象。我也没有测试过自动(取消)装箱会发生什么,请自行尝试。
  • 方面代码有些复杂,可能不会很快。
  • 我不能保证可能没有其他我还没有想到的问题。

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

于 2013-03-17T17:50:30.720 回答
0

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

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

查看AspectJ 编程指南

于 2013-03-01T05:04:47.773 回答