如何横切注释的方法和构造函数?

时间:2012-11-30 09:49:39

标签: java constructor aop aspectj

这就是我正在做的事情:

@Aspect
public class MethodLogger {
  @Around("(execution(* *(..)) || initialization(*.new(..))) && @annotation(Foo)")
  public Object wrap(ProceedingJoinPoint point) throws Throwable {
    // works fine, but only for methods
  }
}

该代码段工作正常,但仅适用于方法调用。这是AspectJ maven plugin在应用方面后所说的内容(不是在编译期间,它工作得很好):

around on initialization not supported (compiler limitation)

任何解决方法?我和OpenJDK 7:

java version "1.7.0_05"
Java(TM) SE Runtime Environment (build 1.7.0_05-b06)
Java HotSpot(TM) 64-Bit Server VM (build 23.1-b03, mixed mode)

2 个答案:

答案 0 :(得分:2)

由于技术限制,around()initialization()切入点上没有preinitialization()建议。并且在输入和退出相应的连接点时的时间顺序存在另一个问题。看看这个例子:

public abstract class ApplicationBase {
    private int id = 0;

    public ApplicationBase(int id) {
        this.id = id;
    }
}
public class Application extends ApplicationBase {
    private String name = "<unnamed>";

    public Application(int id, String name) {
        super(id);
        this.name = name;
    }

    public static void main(String[] args) {
        new Application(1, "Foo");
        new Application(2, "Bar");
    }
}
public aspect ExecutionTimingAspect {
    private String indentText = "";

    pointcut constructorCall() :
        call(*Application*.new(..));

    pointcut constructorRelated() :
        constructorCall() ||
        initialization(*Application*.new(..)) ||
        preinitialization(*Application*.new(..)) ||
        execution(*Application*.new(..));

    after() : constructorRelated() {
        indentText = indentText.substring(2);
        System.out.println(indentText + "<< " + thisJoinPointStaticPart);
    }

    before() : constructorRelated() {
        System.out.println(indentText + ">> " + thisJoinPointStaticPart);
        indentText += "  ";
    }

    Object around() : constructorCall() {
        long startTime = System.nanoTime();
        Object result = proceed();
        System.out.println(indentText + "Constructor runtime = " + (System.nanoTime() - startTime) / 1.0e9 + " s\n");
        return result;
    }
}

您将看到以下输出:

>> call(Application(int, String))
  >> preinitialization(Application(int, String))
  << preinitialization(Application(int, String))
  >> preinitialization(ApplicationBase(int))
  << preinitialization(ApplicationBase(int))
  >> initialization(ApplicationBase(int))
    >> execution(ApplicationBase(int))
    << execution(ApplicationBase(int))
  << initialization(ApplicationBase(int))
  >> initialization(Application(int, String))
    >> execution(Application(int, String))
    << execution(Application(int, String))
  << initialization(Application(int, String))
<< call(Application(int, String))
Constructor runtime = 0.00123172 s

>> call(Application(int, String))
  >> preinitialization(Application(int, String))
  << preinitialization(Application(int, String))
  >> preinitialization(ApplicationBase(int))
  << preinitialization(ApplicationBase(int))
  >> initialization(ApplicationBase(int))
    >> execution(ApplicationBase(int))
    << execution(ApplicationBase(int))
  << initialization(ApplicationBase(int))
  >> initialization(Application(int, String))
    >> execution(Application(int, String))
    << execution(Application(int, String))
  << initialization(Application(int, String))
<< call(Application(int, String))
Constructor runtime = 0.00103393 s

你能看到派生类的预初始化是如何在其基类的预初始化之前开始和结束的吗?初始化如何以相反的方式工作,但作为一个额外的复杂构造函数执行嵌入在初始化中?

现在您可能已经明白,即使通过around()进行初始化,也不会反映构造函数的总体执行时间。因此,如果您足够幸运能够拦截构造函数call()而不是execution(),因为您可以访问调用代码,那么您可以使用around(),就像我在我的顺便说一句(顺便说一句,这不是线程安全的,但我试图保持简单)。如果您不能影响调用者,但只能编织被调用者,则需要使用其他技巧,例如通过before()输入某个构造函数的预初始化时的方面内部簿记,然后在初始化相同的构造函数时通过after()退出通话。即你需要在建议执行之间保持一些内部状态。这是可能的,但有点复杂。如果您想进一步讨论,请告诉我。

答案 1 :(得分:0)

作为解决方法 - 您可以引入工厂以满足这些需求,以应用其方法的建议。