使用javac编译
这段时间看On Java 8进阶卷,看到注解章节4.3.1的时候,书中引入了一个使用注解处理器的例子SimpleTest,并且提到了使用如下命令来编译
javac -processor annotation.simplest.SimpleProcessor SimpleTest.java
但是直接这样做无法得到书中提到的结果。因为SimpleTest.java使用的注解处理器SimpleProcessor和Simple注解都还未编译,在这之前需要先编译这2个文件才行。这2个文件使用普通的javac命令编译,但编译SimpleTest时还需指定注解处理器和@Simple所在路径,命令如下:
D:\books\java\onJava\OnJava8-Examples-master\annotations\simplest>javac Simple.java
D:\books\java\onJava\OnJava8-Examples-master\annotations\simplest>javac -cp D:\books\java\onJava\OnJava8-Examples-master SimpleProcessor.java
D:\books\java\onJava\OnJava8-Examples-master\annotations\simplest>javac -cp D:\books\java\onJava\OnJava8-Examples-master -processor annotations.simplest.SimpleProcessor SimpleTest.java
这样就能得到书中的结果,即在编译SimpleTest时,SimpleProcessor处理了@Simple注解。
使用gradle编译
上述步骤实在是太过麻烦了,并且On Java8的源码是使用gradle来构建的。既然如此,为啥不用gradle来编译呢?这不就是gradle这类工具的意义吗?
这里有点特殊在于On Java8的源码中,注解、注解处理器、使用注解处理器的类都在同一个工程下,注解处理器并没有作为依赖引入。我的思路是先用一个task编译Simple注解和SimpleProcessor,这样另一个task就有资源能够编译SimpleTest了。
这2个task都是JavaCompile任务类型。
在源码的annotations目录下新增一个build.gradle即可,内容如下:
plugins {
id 'java'
}
tasks.register('compileAnnotationBase', JavaCompile) {
source = files(
"${projectDir}/simplest/Simple.java",
"${projectDir}/simplest/SimpleProcessor.java"
)
destinationDirectory = sourceSets.main.java.destinationDirectory
classpath = sourceSets.main.compileClasspath
options.encoding = 'UTF-8'
options.debug = true
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
}
tasks.register('compileAnnotationApp', JavaCompile) {
dependsOn compileAnnotationBase
source = files(
"${projectDir}/simplest/SimpleTest.java"
)
destinationDirectory = sourceSets.main.java.destinationDirectory
classpath = files(compileAnnotationBase.destinationDirectory)
options.encoding = 'UTF-8'
options.debug = true
options.annotationProcessorPath = files(compileAnnotationBase.destinationDirectory)
options.compilerArgs << '-processor' << 'annotations.simplest.SimpleProcessor'
}
执行compileAnnotationApp,可以得到预期的执行结果(这里我把SimpleProcessor的返回值改为了true,请忽略模拟命令行它是调试脚本时的输出):
options.annotationProcessorPath
使用gradle时需要注意要配置这个属性,否则脚本一直报找不到注解处理器。
这点其实困扰了我很久,之前使用javac直接编译时,我指定了classpath就能正确编译。所以我一直觉得配置好了JavaCompile的classpath就能正常运行,结果一直报错,把我卡了2天。。。后来查看JavaCompile文档看到了options.annotationProcessorPath配置,加上它才能正常编译。
这个配置,其实对应javac的-processorpath选项,它的作用是用于指定查找注解处理器的路径。但如果将上述的build.gradle代码中,注释options.annotationProcessorPath所在行,加上如下代码:
options.compilerArgs << '-processorpath' << compileAnnotationBase.destinationDirectory.get().asFile.absolutePath
依然是不成功的,gradle会让你使用options.annotationProcessorPath
看起来这是JavaCompile的一个强制用法。
-processorPath的作用
直接使用javac编译时,我并没有使用-processorpath选项,只设置了-classpath也能编译成功。如果通过classpath也能找到注解处理器,那么这个编译选项是不是多余存在的?
其实并不是多余的,它的作用有如下:
-
隔离注解处理器和应用程序依赖。注解处理器可能有自己的依赖库,这些依赖库可能与正在编译的应用程序的依赖库存在版本冲突。通过使用 -processorpath,可以将注解处理器及其依赖与应用程序的类路径隔离开来。
-
提高编译性能。因为-processorpath指定的路径包含的jar报或class文件比较少,在这里进行查找肯定比从classpath查找要快。
-
便于维护和惯例。使用-processorpath可以使注解处理器的配置更加清晰和明确,-processorpath指向的仅仅是编译时用到的目录而已。
JavaCompile把-processorpath配置单独提出来,成为options.annotationProcessorPath似乎也不无道理。
总结
之前没有系统学习过注解处理器,一些原生提供的选项也不熟悉,忽略了-processorpath这个配置选项。导致在编写gradle脚本时,一直报找不到processor,耽误了很多时间。而且JavaCompile文档也没有看仔细,遗漏了options.annotationProcessorPath配置。
下次遇到类似问题要先打好原生java的基础,并且看文档要仔细。