# apt-demo **Repository Path**: liruohrh/apt-demo ## Basic Information - **Project Name**: apt-demo - **Description**: java注解处理器demo, java8和java9,maven配置 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-10-21 - **Last Updated**: 2023-10-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # apt-demo - apt(Annotation Processor Tool 注解处理器) - demo8:基于java8 - java8的tools.jar需要手动引入,通常在JAVA_HOME/lib/tools.jar - 为了省事,直接放在apt-demo/lib下了 - demo17:基于java17(主要是java9的模块化) - 为了不报错,直接赋值demo8的代码,修改`@SupportedSourceVersion(SourceVersion.RELEASE_8)`为`@SupportedSourceVersion(SourceVersion.RELEASE_17)` - 功能:自动给@Data注解的类添加getter,setter,toString方法(没有的时候才生成) - 不生成源代码,直接修改源代码再让编译器编译 - 执行(注意修改文件中的JAVA8_HOME/JAVA9_HOME) - > java8:执行java8.bat - >java9:执行java9.bat - 结果:一个测试成功,查看target/classes下的对象,可以发现生成了getter,setter,toString方法 - 自定义apt输出: - [INFO] fields:name [INFO] fields:id [INFO] fields:username # 小知识 - tools.jar(<=java8)或者 jdk.compiler模块(>=java9) - > java9的module-info > > Defines the implementation of the system Java compiler and its command line equivalent, javac. > javac > This module provides the equivalent of command-line access to javac via the ToolProvider and javax.tools.Tool service provider interfaces (SPIs), and more flexible access via the JavaCompiler SPI. > Instances of the tools can be obtained by calling ToolProvider.findFirst or the service loader with the name "javac". > In addition, instances of javax.tools.JavaCompiler.CompilationTask obtained from JavaCompiler can be downcast to JavacTask for access to lower level aspects of javac, such as the Abstract Syntax Tree (AST). > This module uses the FileSystemProvider API to locate file system providers. In particular, this means that a jar file system provider, such as that in the jdk.zipfs module, must be available if the compiler is to be able to read JAR files. - 即使用该库等价于使用javac命令行编译源代码,提供修改源码、命令java compiler等功能 # 引用 - [maven/gradle/idea的配置](https://blog.csdn.net/jjxojm/article/details/90349756) - [实现lombox效果](https://juejin.cn/post/7145245047892475934) - [实现lombox效果](https://blog.csdn.net/u014454538/article/details/122849426) - [关于com.sun.tools.javac更详细的知识](https://blog.csdn.net/a_zhenzhen/article/details/86065063#JCTree的介绍) - [Lombok、AutoValue和Immutables](https://www.jianshu.com/p/f850a0b5c4fc) # 报错记录 ## java8 - 无错误,因为没有模块化 - 任意版本编译apt都可以,但是编译使用者的代码时,如果是java9,就必须要对模块化做一些配置才行 - 如果使用java17编译java8,会出现`未与 -source 8 一起设置引导类路径`的问题,不用理会 ## java9 - 因为有模块化,因此报错(但是做了一些处理后还是报错,不知道为什么) - 引入complie插件对应版本的依赖,用来debug - plexus-compiler-javac:编译器 - classworlds:运行maven时的入口,看看参数 ```xml org.codehaus.plexus plexus-compiler-javac 2.13.0 provided classworlds classworlds 1.1 ``` ### 错误1 - 实现注解处理器,编译成功,在使用demo中报错,疑是模块访问问题 - 在编译注解处理器和使用demo中,都加上一样的maven-compiler-plugin配置(demo没有`-proc:none`),还是报这个 - 在com.sun.tools.javac.processing.JavacProcessingEnvironment#discoverAndRunProcs使用ServiceLoad加载javax.annotation.processing.Processor时报错(使用com.sun.tools.javac.processing.JavacProcessingEnvironment.ServiceIterator进行迭代加载服务实现者) > - 该死的maven即便加-X也只是显示错误信息:java.util.ServiceConfigurationError: javax.annotation.processing.Processor: com.liruo.apt.DataProcessor Unable to get public no-arg constructor > - 实际上是:java.lang.IllegalAccessError: superclass access check failed: class com.liruo.apt.DataTreeTranslator (in unnamed module @0x4d8286c4) cannot access class com.sun.tools.javac.tree.TreeTranslator (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.tree to unnamed module @0x4d8286c4 > - debug时把调用栈打印在项目根目录下ServiceLoaderError.txt > - `nextError.printStackTrace(new PrintStream(new FileOutputStream("A:/code/backend/java/demo/apt-demo/ServiceLoaderError.txt")))` - java17, maven3.8.6 - maven-compiler-plugin配置 ```xml -proc:none --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED ``` ### 解决错误1 - 在项目下,添加.mvn/jvm.config添加--add-opens/--add-export选项(这是因为注解处理器是在maven的JVM中运行,所以需要给maven添加启动参数,允许对未导出的模块进行反射,而在测试中因为没有执行有关未导出模块,也就不需要配置运行时) - 另外,编译库时只需要添加--add-exports,不让它报错,编译用户代码时,也需要添加--add-exports,让这个库在运行时不报错 - 详细看unnamed-module.md - 但是却报错误2 ### 错误2 - 从栈 at jdk.compiler/com.sun.tools.javac.comp.Flow$AssignAnalyzer.initParam(Flow.java:2232)得知,传进来的JCVariableDecl.sym.adr=-1(当时debug时,JCVariableDecl是User的username字段)导致在at jdk.compiler/com.sun.tools.javac.util.Assert.error(Assert.java:155)断言失败 - 默认就是-1,也就是说,没有进行修改 - JCVariableDecl.sym.adr注释`The variable's address. Used for different purposes during flow analysis, translation and code generation. Flow analysis: If this is a blank final or local variable, its sequence number. Translation: If this is a private field, its access number. Code generation: If this is a local variable, its logical slot number.` - 如果这是一个空的最终变量或局部变量,它的序列号。 - 如果这是一个私有字段,它的访问号。 - 如果这是一个局部变量,它的逻辑槽号。 - 那么User.username的这个值应该是它的访问号 ```java java.lang.AssertionError at jdk.compiler/com.sun.tools.javac.util.Assert.error(Assert.java:155) at jdk.compiler/com.sun.tools.javac.util.Assert.check(Assert.java:46) at jdk.compiler/com.sun.tools.javac.util.Bits.incl(Bits.java:186) at jdk.compiler/com.sun.tools.javac.comp.Flow$AssignAnalyzer.initParam(Flow.java:2232) at jdk.compiler/com.sun.tools.javac.comp.Flow$AssignAnalyzer.visitMethodDef(Flow.java:2156) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:921) at jdk.compiler/com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49) at jdk.compiler/com.sun.tools.javac.comp.Flow$BaseAnalyzer.scan(Flow.java:444) at jdk.compiler/com.sun.tools.javac.comp.Flow$AssignAnalyzer.scan(Flow.java:1724) at jdk.compiler/com.sun.tools.javac.comp.Flow$AssignAnalyzer.visitClassDef(Flow.java:2098) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:819) at jdk.compiler/com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49) at jdk.compiler/com.sun.tools.javac.comp.Flow$BaseAnalyzer.scan(Flow.java:444) at jdk.compiler/com.sun.tools.javac.comp.Flow$AssignAnalyzer.scan(Flow.java:1724) at jdk.compiler/com.sun.tools.javac.comp.Flow$AssignAnalyzer.analyzeTree(Flow.java:2871) at jdk.compiler/com.sun.tools.javac.comp.Flow$AssignAnalyzer.analyzeTree(Flow.java:2853) at jdk.compiler/com.sun.tools.javac.comp.Flow.analyzeTree(Flow.java:221) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1377) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1351) at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:946) at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.lambda$doCall$0(JavacTaskImpl.java:104) at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.invocationHelper(JavacTaskImpl.java:152) at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:100) at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:94) at org.codehaus.plexus.compiler.javac.JavaxToolsCompiler.compileInProcess(JavaxToolsCompiler.java:136) at org.codehaus.plexus.compiler.javac.JavacCompiler.performCompile(JavacCompiler.java:183) at org.apache.maven.plugin.compiler.AbstractCompilerMojo.execute(AbstractCompilerMojo.java:1140) at org.apache.maven.plugin.compiler.CompilerMojo.execute(CompilerMojo.java:193) at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137) at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute2(MojoExecutor.java:370) at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute(MojoExecutor.java:351) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:215) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:171) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:163) at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:117) at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81) at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:56) at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128) at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:294) at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:192) at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:105) at org.apache.maven.cli.MavenCli.execute(MavenCli.java:960) at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:293) at org.apache.maven.cli.MavenCli.main(MavenCli.java:196) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:282) at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:225) at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:406) at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:347) at org.codehaus.classworlds.Launcher.main(Launcher.java:47) ``` ### 解决错误2 - 从网上找到解决方法:https://www.cnblogs.com/buguge/p/17239766.html - 解释:因为一个注解对应着多个类,而只有一个TreeMaker,因此**需要在修改tree前设置TreeMaker.pos=tree.pos**,让TreeMaker从该tree开始的地方开始make - debug `com.sun.tools.javac.comp.Flow.AssignAnalyzer#visitMethodDef`在其遍历该方法的参数(JCTree.JCVariableDecl)时 - `com.sun.tools.javac.comp.Flow.BaseAnalyzer#scan`,就是`var1.accept(this)`,参数是一个Visitor,this指`com.sun.tools.javac.comp.Flow.AssignAnalyzer` - 然后调用`visitor.visitVarDef(this);` 即`com.sun.tools.javac.comp.Flow.AssignAnalyzer#visitVarDef` - 也就是说使用Visistor模式让AssignAnalyzer访问JCTree.JCVariableDecl - 然后在Flow.AssignAnalyzer#visitVarDef中,如果是trackable且是com.sun.tools.javac.code.Kinds.Kind#MTH/com.sun.tools.javac.code.Kinds.Kind#VAR会调用com.sun.tools.javac.comp.Flow.AssignAnalyzer#newVar给sym.adr赋值 - 不知道为什么 - java8,trackable=true,因为var1.pos >= this.startPos为true,189>163 - java17,trackable=false,因为var1.pos >= this.startPos为false,162>163 - AssignAnalyzer的startPos是JCClassDecl的的mods的pos - 大概意思是一个类是从修饰符Modifier开始的吧 - pos是语法节点在语法树中的位置 - var1.pos >= this.startPos为true,就表示该节点在该类定义之后,这样才是正常的 - 因此需要上面解决方法说的,**在修改tree前设置TreeMaker.pos=tree.pos** - 因此, 错误2不能说是java9的, 而是写的注解处理器有问题