# 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的, 而是写的注解处理器有问题