禁止基于另一个注释的注释

时间:2018-03-22 22:12:20

标签: java annotations aspectj

我有一个适用于类型(即类级别)的注释和另一个适用于字段变量的注释。如果已经应用了类型注释,是否有办法禁止字段注释?

我正在开发一个AspectJ应用程序,它具有基于注释的切入点。目前,我有两个注释 - @Trace和@TraceAll。 @Trace允许用于字段变量,@ TraceAll允许用于类型。

如果应用了@Trace,我使用set()来跟踪对它所应用的字段变量的写入。如果应用@TraceAll,我使用within()来跟踪所有字段变量和方法。

如果用户在单个类上同时应用两个注释,我不想复制跟踪。

1 个答案:

答案 0 :(得分:0)

我在这里工作,提供MCVE。这是你的免费拍摄,因为你是SO的新手,下次请自己动手:

标记注释:

package de.scrum_master.app;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(TYPE)
public @interface TraceAll {}
package de.scrum_master.app;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(METHOD)
public @interface Trace {}

这里首先想到的是:你为什么需要两个注释?为什么不只是一个注释@Trace可以应用于这两种类型和方法?保持简单,有两个注释没有额外的价值。如果将@Trace应用于某个类型,那么您是否要隐含地想要跟踪该类中的所有方法?

带注释的类+驱动程序应用程序:

package de.scrum_master.app;

@TraceAll
public class SomeClass {
  public void doSomething() {
    foo();
    bar();
    zot();
  }

  @Trace
  public void foo() {}

  public void bar() {}

  @Trace
  public void zot() {}
}
package de.scrum_master.app;

public class Application {
  public void doSomething() {
    foo();
    bar();
    zot();
  }

  @Trace
  public void foo() {}

  public void bar() {}

  @Trace
  public void zot() {}

  public static void main(String[] args) {
    new Application().doSomething();
    new SomeClass().doSomething();
  }
}

这里我复制了你的情况:虽然Application只使用方法级注释,但SomeClass混合了两种注释类型。

原创方面:

由于您没有显示任何代码,我必须猜测您在自己的方面做了什么。我认为它是这样的:

package de.scrum_master.aspect;

import de.scrum_master.app.Trace;
import de.scrum_master.app.TraceAll;

public aspect TraceAspect {
  before() : @target(TraceAll) && execution(* *(..)) {
    System.out.println(thisJoinPoint);
  }

  before() : @annotation(Trace) && execution(* *(..)) {
    System.out.println(thisJoinPoint);
  }
}

这会导致您在问题中描述的问题:

原始方面的控制台日志:

execution(void de.scrum_master.app.Application.foo())
execution(void de.scrum_master.app.Application.zot())
execution(void de.scrum_master.app.SomeClass.doSomething())
execution(void de.scrum_master.app.SomeClass.foo())
execution(void de.scrum_master.app.SomeClass.foo())
execution(void de.scrum_master.app.SomeClass.bar())
execution(void de.scrum_master.app.SomeClass.zot())
execution(void de.scrum_master.app.SomeClass.zot())

我们可以看到这两个建议都会被触发,SomeClass.foo()以及SomeClass.zot()会被记录两次。这是你想要避免的,对吗?

如果用户这样做,可以使用以下方法触发编译错误:

改进方面:

package de.scrum_master.aspect;

import de.scrum_master.app.Trace;
import de.scrum_master.app.TraceAll;

public aspect TraceAspect {
  before() : @target(TraceAll) && execution(* *(..)) {
    System.out.println(thisJoinPoint);
  }

  before() : @annotation(Trace) && execution(* *(..)) {
    System.out.println(thisJoinPoint);
  }

  declare error : execution(@Trace * (@TraceAll *).*(..))
    : "Don't combine @TraceAll and @Trace annotations"; 
}

现在,在删除方法注释之前,类SomeClass不再编译,只留下类型注释。在Eclipse中它看起来像这样:

Eclipse compile errors

但实际上有一种更简单的方法可以避免双重记录:

改进方面,迭代2:

只需通过||运算符将两个建议合并为逻辑OR。

package de.scrum_master.aspect;

import de.scrum_master.app.Trace;
import de.scrum_master.app.TraceAll;

public aspect TraceAspect {
  before() : (@target(TraceAll) || @annotation(Trace)) && execution(* *(..)) {
    System.out.println(thisJoinPoint);
  }
}

改进方面的控制台日志,迭代2:

execution(void de.scrum_master.app.Application.foo())
execution(void de.scrum_master.app.Application.zot())
execution(void de.scrum_master.app.SomeClass.doSomething())
execution(void de.scrum_master.app.SomeClass.foo())
execution(void de.scrum_master.app.SomeClass.bar())
execution(void de.scrum_master.app.SomeClass.zot())

请参阅?如果没有强制用户按照您施加的规则进行游戏,就不会再进行双重记录。为什么不使用这两种类型的注释?也许开发人员总是想跟踪某些方法。对于调试,她暂时添加了一个类型级别的注释,而不想强制删除所有方法级别的注释,因为她可能会忘记稍后再添加它们。

迭代3:只有一个注释

现在如何使用这样的注释:

package de.scrum_master.app;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target({ TYPE, METHOD })
public @interface Trace {}
package de.scrum_master.app;

@Trace
public class SomeClass {
  public void doSomething() {
    foo();
    bar();
    zot();
  }

  @Trace
  public void foo() {}

  public void bar() {}

  @Trace
  public void zot() {}
}
package de.scrum_master.aspect;

import de.scrum_master.app.Trace;

public aspect TraceAspect {
  before() : (@target(Trace) || @annotation(Trace)) && execution(* *(..)) {
    System.out.println(thisJoinPoint);
  }
}

结果将是相同的。