使用java.lang.reflection的构造函数的AspectJ切入点

时间:2013-05-01 12:51:18

标签: java aspectj pointcut

以下示例是减少真正的问题,因为它尽可能地尝试简化。

我有一个java接口,以及几个实现该接口的对象,如:

public interface Shape{
    public void draw();
    public void erase();
    public boolean isDrawn();
}

public class Square implements Shape{
    @Override
    public void draw(){
        //TODO: method implementation
    }

    @Override
    public void erase(){
        //TODO: method implementation
    } 

    Override
    public boolean isDrawn(){
        //TODO: method implementation
        return false;
    }
}

public Triangle implements Shape{
    //same as above
}

public Circle implements Shape{
    //same as above
}

这是我程序的结构。通过使用AspectJ,我想拥有一个包含实现接口的每个对象的映射。为此,我尝试使用以下方面捕获构造函数:

public aspect ShapeHolderAspect{
    private Map<Integer, Shape> map = new HashMap<>();
    private int count = 0;    

    pointcut shapeInit(): call((Shape+).new(..));

    Object around(): shapeInit() {
        System.out.println("capturing new");

        Shape shapeType = (Shape)proceed();
        map.put(++count, shapeType);
        return shapeType;
    }
}

如果我使用以下场景创建一个Shape,则此代码将起作用:

public static void main(String[] args){
    Shape myShape = new Circle();
}

但是,我使用的是java语言反射,所以从技术上讲,我不会调用“new”构造函数。相反,我找到了包的路径,并创建了一个传递带有类名称的字符串的对象:

public static void main(String[] args){
    String shapeClassName = args[0];
    Class<?> classType = Class.forName("myPackage.figures" + "." + shapeClassName);
    Shape myShape =(Shape)classType.getConstructor().newInstance();
}

通过这种方式,AspectJ无法检测到我正在创建形状。我该如何解决这个问题?

2 个答案:

答案 0 :(得分:2)

新的,更好的版本:

好吧,虽然下面的旧版本实际上捕获了所有构造函数的执行,但构造函数执行的around建议返回null,因为有问题的对象尚未初始化。所以你最终会在你的方面得到一个空指针的映射。为了解决这个问题,您需要将this()绑定到变量(示例代码使用默认包名称):

public class Application {
    public static void main(String[] args) throws Exception {
        new Circle().draw();
        ((Shape) Class.forName("Triangle").getConstructor().newInstance()).isDrawn();
        ((Shape) Class.forName("Square").getConstructor().newInstance()).erase();
    }
}
import java.util.HashMap;
import java.util.Map;

public aspect ShapeHolderAspect {
    private Map<Integer, Shape> map = new HashMap<Integer, Shape>();
    private int count = 0;

    after(Shape shape): execution(Shape+.new(..)) && this(shape) {
        System.out.println(thisJoinPointStaticPart);
        map.put(++count, shape);
    }

    after() : execution(* Application.main(..)) {
        System.out.println("\nList of shapes:");
        for (int key : map.keySet())
            System.out.println("  " + key + " -> " + map.get(key));
    }
}

输出如下:

initialization(Circle())
initialization(Triangle())
initialization(Square())

List of shapes:
  1 -> Circle@1a2961b
  2 -> Triangle@12d03f9
  3 -> Square@5ffb18
顺便说一句,如果您绝对需要around建议,因为您想在创建对象之前在之前做其他事情,它将如下所示:

void around(Shape shape): execution(Shape+.new(..)) && this(shape) {
    System.out.println(thisJoinPointStaticPart);
    proceed(shape);
    map.put(++count, shape);
}

旧的,不完整的版本:

很简单,只需拦截构造函数execution而不是call

pointcut shapeInit(): execution(Shape+.new(..));

这样你编织成被调用的代码(被调用者),而不是调用代码(调用者)。因此,呼叫者是否发出反射或正常呼叫并不重要。

答案 1 :(得分:0)

发现以下切入点可以完成这项工作:

pointcut lockReflectInit(): call(public Object java.lang.reflect.Constructor.newInstance(..));  

然而,这将捕获newInstance的所有调用,而不仅仅是返回Shape =(