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

Jus*_*ion 8 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);
  }
}
Run Code Online (Sandbox Code Playgroud)

作品:

@Pointcut(value = "get(@Important * *)")
void testPointCut() {
}
Run Code Online (Sandbox Code Playgroud)

不起作用:

@Pointcut(value = "get(@Important * *) && execution(public * *(..))")
void testPointCut() {
}
Run Code Online (Sandbox Code Playgroud)

kri*_*aex 2

对于您想要的,没有开箱即用的 AspectJ 解决方案,因为如果您拦截任何对象的方法执行,则不会连接到可能指向这些对象的带注释字段。拦截带注释的类或带注释的方法的方法执行会更容易,但这不是您想要做的。

这是一个小代码示例,向您展示了一种解决方法,但也显示了其局限性:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Important {}
Run Code Online (Sandbox Code Playgroud)
public class Counter {
    private int count = 0;

    public void add(int value) {
        count = count + value;
    }

    @Override
    public String toString() {
        return super.toString() + "[count=" + count + "]";
    }
}
Run Code Online (Sandbox Code Playgroud)
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);
    }
}
Run Code Online (Sandbox Code Playgroud)
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); }
    } 
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我的方面保留了一组“重要对象”。更准确地说,它的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]
Run Code Online (Sandbox Code Playgroud)

请仔细将代码与main日志输出进行比较,以了解我测试了哪些常规情况和特殊情况。

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

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

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