使用带注释的方法时使java编译器发出警告(如@deprecated)

gre*_*ill 12 java compiler-construction annotations deprecated

假设我定义了一个名为的自定义注释@Unsafe.

我想提供一个注释处理器,它将检测注释方法的引用@Unsafe并打印警告.

例如,鉴于此代码......

public class Foo {
  @Unsafe
  public void doSomething() { ... }
}

public class Bar {
  public static void main(String[] args) {
    new Foo().doSomething();
  }
}
Run Code Online (Sandbox Code Playgroud)

...我希望编译器打印如下:

WARN > Bar.java, line 3 : Call to Unsafe API - Foo.doSomething()
Run Code Online (Sandbox Code Playgroud)

它在精神上非常相似@Deprecated,但我的注释是在传达不同的东西,所以我不能@Deprecated直接使用.有没有办法用注释处理器实现这一目标?注释处理器API似乎更关注应用注释的实体(Foo.java在我的示例中),而不是引用注释成员的实体.

此问题提供了一种使用ASM作为单独构建步骤实现它的技术.但我想知道我是否可以通过javac和注释处理以更自然的方式做到这一点?

gre*_*ill 5

我想我可以使用@mernst 的回复在技术上实现我的目标,所以我很感激这个建议。但是,我发现了另一条更适合我的路线,因为我正在开发商业产品并且无法合并 Checker 框架(它的 GPL 许可证与我们的不兼容)。

在我的解决方案中,我使用我自己的“标准”Java 注释处理器来构建所有用@Unsafe.

然后,我开发了一个 javac 插件。插件 API 可以轻松找到 AST 中任何方法的每次调用。通过使用此问题中的一些提示,我能够从 MethodInvocationTree AST 节点确定类和方法名称。然后,我将这些方法调用与我创建的包含注释的方法的早期“列表”进行比较,@Unsafe并在需要时发出警告。

这是我的 javac 插件的缩写版本。

import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;

import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.Plugin;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskEvent.Kind;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeInfo;
import com.sun.source.util.TaskListener;
import com.sun.source.util.TreeScanner;

public class UnsafePlugin implements Plugin, TaskListener {

  @Override
  public String getName() {
    return "UnsafePlugin";
  }

  @Override
  public void init(JavacTask task, String... args) {
    task.addTaskListener(this);
  }

  @Override
  public void finished(TaskEvent taskEvt) {
    if (taskEvt.getKind() == Kind.ANALYZE) {
      taskEvt.getCompilationUnit().accept(new TreeScanner<Void, Void>() {
        @Override
        public Void visitMethodInvocation(MethodInvocationTree methodInv, Void v) {
          Element method = TreeInfo.symbol((JCTree) methodInv.getMethodSelect());
          TypeElement invokedClass = (TypeElement) method.getEnclosingElement();
          String className = invokedClass.toString();
          String methodName = methodInv.getMethodSelect().toString().replaceAll(".*\\.", "");
          System.out.println("Method Invocation: " + className + " : " + methodName);
          return super.visitMethodInvocation(methodInv, v);
        }
      }, null);
    }
  }

  @Override
  public void started(TaskEvent taskEvt) {
  }

}
Run Code Online (Sandbox Code Playgroud)

注意 - 为了调用 javac 插件,您必须在命令行上提供参数:

javac -processorpath build/unsafe-plugin.jar -Xplugin:UnsafePlugin
Run Code Online (Sandbox Code Playgroud)

此外,您必须META-INF/services/com.sun.source.util.Plugin在 unsafe-plugin.jar 中有一个文件,其中包含插件的完全限定名称:

com.unsafetest.javac.UnsafePlugin
Run Code Online (Sandbox Code Playgroud)

  • 关于许可:正如使用 javac 编译您的代码不会感染您的 GPL 代码一样,使用 Checker Framework 对您的代码进行类型检查不会感染您的 GPL 代码。在开发期间运行 Checker 框架不会影响您的知识产权或许可。*然而*,如果您想将 Checker 框架作为产品的一部分发布,那么您的产品必须根据 GPL 获得许可。因此,在这种情况下,您可能希望避免使用 Checker 框架。无论如何,您的插件足够简单,不需要整个 Checker 框架的强大功能。 (2认同)
  • 太好了,我看到您在我设法建议之前找到了 javac 插件机制。:-) 不幸的是,它没有很好地记录在案,但看起来您可以使用它来完成您想要的。请注意,插件接口是“非标准的”。也就是说,它不是 Java SE 的一部分,而是特定于 Oracle JDK 和 OpenJDK。其他 Java 实现可能没有它。这就是插件选项以`-X` 为前缀的原因。 (2认同)