2

以下示例是对实际问题的简化,因为它试图尽可能地简化。

我有一个 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;
    }
}

如果我使用以下方案创建形状,则此代码将起作用:

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

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

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

通过这样做,AspectJ 无法检测到我正在创建形状。我该如何解决?

4

2 回答 2

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(..));

这样您就可以编织到被调用代码(被调用者),而不是调用代码(调用者)。因此,呼叫者是发出​​反射呼叫还是正常呼叫都没有关系。

于 2013-05-02T21:38:35.110 回答
0

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

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

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

于 2013-05-01T14:10:11.640 回答