AspectJ pointcut for constructor using java.lang.reflection

可紊 提交于 2019-12-23 01:13:11

问题


The following example is a reduction of the real problem in that it tries to simplify is as much as possible.

I have a java interface, and several objects that implement that interface, like:

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
}

This is the structure of my program. By using AspectJ I want to have a map that holds each object that implements the interface. To do so I was trying to capture the constructors by using the following aspect:

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;
    }
}

This code will work if I create a Shape using the following scenario:

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

However, I am using java language reflection, and so technically I don't call the "new" constructor. Instead I locate the path of the package, and create the object passing a string with the name of the class:

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

By doing this way, AspectJ cannot detect that I am creating shapes. How do I fix this?


回答1:


New, better version:

Well, while the old version below actually catches all constructor executions, an around advice on constructor execution returns null because the object in question has not been initialised yet. So you would end up with a map of null pointers in your aspect. In order to fix this you need to bind this() to a variable (sample code uses default package name):

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));
    }
}

The output looks like this:

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

List of shapes:
  1 -> Circle@1a2961b
  2 -> Triangle@12d03f9
  3 -> Square@5ffb18

BTW, if you absolutely need an around advice because you want to do other things before and after object creation, it would look like this:

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

Old, incomplete version:

Quite simply, just intercept constructor execution instead of call:

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

This way you weave into the called code (callee), not the calling code (caller). Consequently, it does not matter if the caller issues a reflective or normal call.




回答2:


Found that the following pointcut will do the job:

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

This will however catch ALL calls of newInstance, and not just the ones that return Shape =(



来源:https://stackoverflow.com/questions/16318426/aspectj-pointcut-for-constructor-using-java-lang-reflection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!