使用java.lang.reflection的构造函数的AspectJ切入点 [英] AspectJ pointcut for constructor using java.lang.reflection

查看:101
本文介绍了使用java.lang.reflection的构造函数的AspectJ切入点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的示例是对实际问题的简化,因为它尝试尽可能简化.

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

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
}

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

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

如果我在以下情况下创建Shape,此代码将起作用:

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

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

但是,我使用的是Java语言反射,因此从技术上讲,我不将其称为新"构造函数.相反,我找到了包的路径,并创建了一个传递带有类名的字符串的对象:

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

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

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

推荐答案

更好的新版本:

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

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

输出看起来像这样:

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

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

顺便说一句,如果您绝对需要around建议,因为您想在对象创建后的之前做其他事情,它看起来像这样:

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


旧的不完整版本:

非常简单,仅拦截构造器execution而不是call:

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.

这篇关于使用java.lang.reflection的构造函数的AspectJ切入点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆