因为写代码想偷懒,最近一直在做梦可以用注解简化一些重复性比较高的代码,所以学习了半天Java的注解处理器怎么用。虽然学完以后我的感叹是为了偷懒而花费了几倍的时间学习了一份我现在应该完全用不到的知识,感到非常忧郁(捂脸)


(资料图)

本着来都来了的精神,还是大体总结下我学了啥知识。毕竟真的不记录就是纯时间浪费了(捂脸)现在网络上的信息实在是太混乱了,很多东西一查全都是复制粘贴,经常一页都是内容完全一样的东西,一眼爬虫自动生成,实在优点没辙,这次查的不记下来,下次真的又找不到了.......

1.注解处理器Processor

Java在定义注解时,可以将注解定义为3个等级:

RentationPolicy.SOURCE:只保留到源代码,编译成.class文件时就丢弃

RentationPolicy.CLASS:编译后保留到.class文件里,但是运行时不加载。

RentationPolicy.RUNTIME:运行时会加载

暂时不是很清楚RentationPolicy.CLASS有什么用,可能我还要学习。

RentationPolicy.SOURCE就是主要用于注解处理器的部分了。

Java在编译代码之前,会通过注解处理器分析代码,并提供持有特定接口的对象集合供注解处理器分析处理,这个时候注解处理器可以基于注解提供的信息生成额外的java文件加入到编译流程里。最终生成的class文件内就会包含额外生成的代码部分。

在编译前对代码进行处理使用的基础类型为接口Processor,一般可以使用实现了大部分功能的类型AbstractProcessor作为代替。

因为还没有探索好AbstractProcessor在什么实际场景应用比较合适,所以这里简单记录一下我建立两个测试项目,并且实现注解处理器自动生成代码,以及对注解处理器进行调试的流程

2.建立主项目

主项目主要只需要建立一个测试注解,以及一个使用了该注解的测试类

成员方法和构造函数都是随便乱写的,主要是为了测试自动生成代码能否进行

理想情况下,注解处理器应当可以自动发现持有TestAnnotation的类BasicClass,并且生成相关代码

3.建立注解处理器项目

注解处理器不能与主项目放到一起,必须独立建立一个项目,并且按照指定方式生成一个jar包放置到主项目的lib目录下。如果注解处理器想要引用主项目的部分定义(例如接口定义等反射信息)需要怎么弄,我这里也搞不清楚,目前是按照写死的方式进行的处理。

(比较重要的部分都写注释里了懒得额外写说明了())

4.修改注解处理器项目的生成方式,并且获得jar包,在主项目中运行

注解处理器制作出来不等于可以立刻使用,需要生成到jar包再导入到主项目。

以idea为例,需要在Project Structure -> Artifacts里添加一个JAR的目标,在jar根目录下创建文件夹 ~/META-INF/services/ ,并添加文件名为“javax.annotation.processing.Processor”的文件,文件内容为当前jar包所有注解处理器的全包名,例如本项目的该文件内容如下:

(当然,通过引入谷歌提供的@AutoService注解,可以不需要自己声明jar包里的这个文件)

在配置好之后,对项目实行build Artifact操作,从~/out/artifacts目录下找到jar包,复制到主项目的lib文件夹下,添加libraries。并且修改Settings -> Build, Execution, Deployment -> Complier -> Annotation Processors,将Enable annotation processing勾选,之后进行build时就会将生成的代码输出了。 

5.对注解处理器进行的调试(以idea为例)

Processor相关的操作在编译前进行,实际上是另开了一个JVM进行编译操作,因此正常方式下无法对其进行断点等方式测试,只能输出日志。在网上稍微查了下,学习了一下在不利用Spring或者gradle,只使用idea进行注解处理器的调试的方法,步骤如下:

1) “Edit Custom VM Options”,添加以下声明(端口可以自定义,后面需要使用相同端口),修改了这个设置之后需要重启idea

-Dcompiler.process.debug.port=8000

2)"Debug Build Process",将其调节为on(每次打开idea都会自动变成off,需要重新打开),之后build主项目,现在主项目就会监听8000端口并等待调试器接入

3)打开注解处理器项目,添加一个remote调试项目,设置好端口以后,设置断点并启动,就可以开始远程调试注解处理器了。

注意:用完以后一定要将Debug Build Process关闭,否则后面idea启动其他编译都会卡住

6.我对AbstractProcessor的评价:

abstractProcessor可以在java编译class文件之前介入,批量生成代码或者资源文件,generate的代码也可以直接在主项目里引用,相对于我们自己的项目里经常必须启动一个ant跑一次来产生代码,注解处理器提前生成代码可以减少一部分操作复杂度。

但是其作用范围感觉还是有一些有限,这些有限性在于:

1.build的过程中并没有启动主项目,所以无法直接应用代码里的反射信息,可以间接利用的反射信息有限。使用编译时注解处理器的时候一般需要通过整个文件对必要的class,field和method都写好注解的方式来指引如何生成指定的java文件。

2.不能修改已有文件,只能生成新的java文件,因此无法对已有代码产生直接影响。

因为游戏服务器对于性能要求经常比较高,运行时注解等反射的方式一般不适合在游戏服务器主流程上运行,而注解处理器也不能修改现有代码。如果想要按照一定的规则,批量性修改已有代码,就需要另外寻找其他方式。之后我学习了JavaParser,就能实现相关功能。这是下次准备记录的内容。

关键词: