想在编译期强制团队约定,又不想写注解处理器?javac 的 -Xplugin 可以让你直接访问语法树与类型信息。这篇写一个真实的小插件,看它在命令行与 Maven 里怎么被加载、怎么报告问题、怎么让构建失败。
javac -Xplugin:NoPrintln:返回码 0(警告不让编译失败),输出:
[NoPrintln] init,参数 []
[NoPrintln] 各阶段事件次数(到 Main 生成结束时为止,开始事件): PARSE=2 ENTER=2 ANALYZE=1 GENERATE=1 ANNOTATION_PROCESSING=0 ANNOTATION_PROCESSING_ROUND=0 COMPILATION=1
[NoPrintln] 前 14 个事件:[S:COMPILATION, S:PARSE:Main.java, F:PARSE, S:PARSE:Helper.java, F:PARSE, S:ENTER:Main.java, S:ENTER:Helper.java, F:ENTER, F:ENTER, S:ANALYZE:Main, F:ANALYZE:Main, S:GENERATE:Main, F:GENERATE:Main]
<jtmp>/j132w/app/demo/Main.java:4: warning: 不要在 Main 里用 System.out.println,请使用日志框架
System.out.println("hello");
^
<jtmp>/j132w/app/demo/Helper.java:4: warning: 不要在 Helper 里用 System.out.print,请使用日志框架
System.out.print(s);
^
2 warnings
javac "-Xplugin:NoPrintln error"(插件参数 error → 报错):返回码 1;生成的 class 数 0
[NoPrintln] init,参数 [error]
<jtmp>/j132w/app/demo/Main.java:4: error: 不要在 Main 里用 System.out.println,请使用日志框架
<jtmp>/j132w/app/demo/Helper.java:4: error: 不要在 Helper 里用 System.out.print,请使用日志框架
2 errors
"-Xplugin:NoPrintln allow=Main,Helper"(插件参数白名单):返回码 0,诊断条数 0
不给 -processorpath、-cp 也没有插件:返回码 1 ; ['error: plug-in not found: NoPrintln', '1 error']
只给 -cp(没有 -processorpath):返回码 0 ;警告数 2 (插件也能从 classpath 上找到)
-Xplugin:NoSuchPlugin:返回码 1 ; ['[NoPrintln] init,参数 []', 'error: plug-in not found: NoSuchPlugin']各阶段事件次数(到 Main 生成结束时为止,开始事件): PARSE=2 ENTER=2 ANALYZE=1 GENERATE=1 ANNOTATION_PROCESSING=0 ANNOTATION_PROCESSING_ROUND=0 COMPILATION=1 前 14 个事件:[S:COMPILATION, S:PARSE:Main.java, F:PARSE, S:PARSE:Helper.java, F:PARSE, S:ENTER:Main.java, S:ENTER:Helper.java, F:ENTER, F:ENTER, S:ANALYZE:Main, F:ANALYZE:Main, S:GENERATE:Main, F:GENERATE:Main]
两个小文件,javac 总耗时(7 次去掉前 2 次的中位):不带插件 233 ms;带 NoPrintln 插件 248 ms
mvn compile(插件以警告方式报告):返回码 0 ;Maven 输出里的相关行:
[NoPrintln] init,参数 []
[NoPrintln] 各阶段事件次数(到 Main 生成结束时为止,开始事件): PARSE=2 ENTER=2 ANALYZE=2 GENERATE=2 ANNOTATION_PROCESSING=0 ANNOTATION_PROCESSING_ROUND=0 COMPILATION=1
[NoPrintln] 前 14 个事件:[S:COMPILATION, S:PARSE:Helper.java, F:PARSE, S:PARSE:Main.java, F:PARSE, S:ENTER:Helper.java, S:ENTER:Main.java, F:ENTER, F:ENTER, S:ANALYZE:Helper, F:ANALYZE:Helper, S:GENERATE:
[WARNING] /mvn/src/main/java/demo/Helper.java:[4,25] 不要在 Helper 里用 System.out.print,请使用日志框架
[WARNING] /mvn/src/main/java/demo/Main.java:[4,27] 不要在 Main 里用 System.out.println,请使用日志框架
[INFO] BUILD SUCCESS
fork=false(进程内的 Compiler API),参数写成一个字符串 "-Xplugin:NoPrintln error":返回码 1;相关行:
[NoPrintln] init,参数 [error]
[ERROR] /mvn/src/main/java/demo/Helper.java:[4,25] 不要在 Helper 里用 System.out.print,请使用日志框架
[ERROR] /mvn/src/main/java/demo/Main.java:[4,27] 不要在 Main 里用 System.out.println,请使用日志框架
[INFO] BUILD FAILURE
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.13.0:compile (default-compile) on project app: Compilation failure: Compilation failure:
[ERROR] /mvn/src/main/java/demo/Helper.java:[4,25] 不要在 Helper 里用 System.out.print,请使用日志框架
[ERROR] /mvn/src/main/java/demo/Main.java:[4,27] 不要在 Main 里用 System.out.println,请使用日志框架
fork=true(外部 javac 进程),同样的写法:返回码 1;相关行:
[INFO] [NoPrintln] init,参数 [error]
[ERROR] /mvn/src/main/java/demo/Helper.java:[4,24] error: 不要在 Helper 里用 System.out.print,请使用日志框架
[ERROR] /mvn/src/main/java/demo/Main.java:[4,26] error: 不要在 Main 里用 System.out.println,请使用日志框架
[INFO] BUILD FAILURE
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.13.0:compile (default-compile) on project app: Compilation failure: Compilation failure:
[ERROR] /mvn/src/main/java/demo/Helper.java:[4,24] error: 不要在 Helper 里用 System.out.print,请使用日志框架
[ERROR] /mvn/src/main/java/demo/Main.java:[4,26] error: 不要在 Main 里用 System.out.println,请使用日志框架