Java 是一门"繁琐"的语言,使用 Lombok 可以显著地减少样板代码。比如使用 @Getter
注解可以为你的私有属性创建 get 方法。
源代码
@Getter private int age = 10;
生成后代码
private int age = 10; public int getAge() { return age; }
Lombok 自身已经拥有许多非常实用的注解,例如 @Getter
/ @Value
/ @Data
/ @Builder
等等。但你可能也想定义自己的注解来减少重复代码,本文将讲解如何实现这一目标。
Lombok是如何实现代码注入的?
在使用 javac 编译器时(netbeans,maven,gradle),Lombok 会以 annotation processor 方式运行。 Javac 会以 SPI 方式加载所有 jar 包中 META-INF/services/javax.annotation.processing.Processor
文件所列举的类,并以 annotation processor 的方式运行它。对于 Lombok,这个类是 lombok.launch.AnnotationProcessorHider$AnnotationProcessor
,当它被 javac 加载创建后,会执行 init
方法,在这个方法中会启动一个特殊的类加载器 ShadowClassLoader
,加载同 jar 包下所有以 .SCL.lombok
结尾的类(Lombok 为了对 IDE 隐藏这些类,所以不是通常地以 .class 结尾)。其中就包含各式各样的 handler
。每个 handler
申明并处理一种注解,比如 @Getter
对应 HandleGetter
。
委派给 handler
时,Lombok Annotation Processor 会提供一个被注解节点的Abstract Syntax Tree (AST)节点对象,它可能是一个方法、属性或类。在 handler
中 可以对这个 AST 进行修改,之后编译器将从被修改后的 AST 生成字节码。
下面我们以 @KLog
为例,说明如何编写 Handler
。假设我们希望实现这样的效果:
源代码
@KLog public class Foo { }
生成后代码
public class Foo { private static final com.example.log.KLogger log = com.example.log.KLoggerFactory.getLogger(Foo.class); }
KLog 可能是我们的日志类,在通用日志类的基础上做了一些扩展。 使用 @KLog
可以避免因复制粘贴代码导致入参错误,也有利于统一命名。为了实现这个注解,我们需要实现:
- 创建 Javac Handler
- 创建 Eclipse Handler
- 创建 lombok-intellij-plugin Handler
前期准备:Fork Lombok 工程
我们需要先 fork Lombok 工程,项目中添加 Handler。前面谈到因为 shadow loader类加载的原因,在另外的工程中创建 Handler 将变得非常困难, lombok作者推荐直接fork lombok工程定制自己的 lombok.jar
。
~ git clone https://github.com/rzwitserloot/lombok.git
需要注意的是,lombok 需要使用 JDK9 以上版本进行编译,确保系统路径配置了正确的 JAVA_HOME 路径,然后执行 ant maven
将构建可以用于安装本地仓库的 jar 包。 可以运行以下命令将构建的 jar 包安装到本地仓库进行工程间共享:
~ mvn install:install-file -Dfile=dist/lombok-{lombok-version}.jar -DpomFile=build/mavenPublish/pom.xml<