- A+
目录
一、前言
Lombok是一个可以通过简单的注解的形式来帮助我们简化消除一些必须有但显得很臃肿的 Java 代码的工具。
简单来说,比如我们新建了一个类,然后在其中写了几个字段,然后通常情况下我们需要手动去建立getter和setter方法啊,构造函数啊之类的,lombok的作用就是为了省去我们手动创建这些代码的麻烦,它能够在我们编译源码的时候自动帮我们生成这些方法。那么Lombok到底是如何做到这些的呢?
二、Lombok如何使用
- Lombok是一个开源项目,代码是在lombok中。打开IDEA的Setting –> 选择Plugins选项 –> 搜索lombok –> 点击安装 –> 安装完成重启IDEA。
- 添加依赖,在项目中添加Lombok依赖jar,在pom文件中添加如下部分
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
那么Lombok是做什么呢?其实很简单,一个最简单的例子就是能够通过添加注解自动生成一些方法,使我们代码更加简洁易懂。例如下面一个类。
@Data
public class TestLombok {
private String name;
private Integer age;
public static void main(String[] args) {
TestLombok testLombok = new TestLombok();
testLombok.setAge(12);
testLombok.setName("zs");
}
}
我们使用Lombok提供的Data注解,在没有写get、set方法的时候也能够使用其get、set方法。它编译过后的class文件,可以看到它给我们自动生成了get、set方法。当然Lombok的功能不止如此,还有很多其他的注解帮助我们简便开发,网上有许多的关于Lombok的使用方法。使用Spring框架中@Controller、@Service等等这类注解都是运行时注解,运行时注解大部分都是通过反射来实现的。而Lombok是使用编译时注解实现的。那么编译时注解是什么呢?
三、编译时注解
注解(也被成为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。—————《Thinking in Java》
Java中的注解分为运行时注解和编译时注解,运行时注解就是我们经常使用的在程序运行时通过反射得到我们注解的信息,然后再做一些操作。而编译时注解是什么呢?就是在程序在编译期间通过注解处理器进行处理。
- 编译期:Java语言的编译期是一段不确定的操作过程,因为它可能是将*.java文件转化成*.class文件的过程;也可能是指将字节码转变成机器码的过程;还可能是直接将*.java编译成本地机器代码的过程
- 运行期:从JVM加载字节码文件到内存中,到最后使用完毕以后卸载的过程都属于运行期的范畴。
四、注解处理工具APT
注解处理工具APT(Annotation Processing Tool),这是Sun为了帮助注解的处理过程而提供的工具,apt被设计为操作Java源文件,而不是编译后的类。
它是javac的一个工具,中文意思为编译时注解处理器。APT可以用来在编译时扫描和处理注解。通过APT可以获取到注解和被注解对象的相关信息,在拿到这些信息后我们可以根据需求来自动的生成一些代码,省去了手动编写。注意,获取注解及生成代码都是在代码编译时候完成的,相比反射在运行时处理注解大大提高了程序性能。APT的核心是AbstractProcessor类。
正常情况下使用APT工具只是能够生成一些文件(不仅仅是我们想象的class文件,还包括xml文件等等之类的),并不能修改原有的文件信息。
但是此时估计会有疑问,那么Lombok不就是在我们原有的文件中新增了一些信息吗?其实Lombok
是修改了Java中的抽象语法树AST才做到了修改其原有类的信息。
接下来我们演示一下如何用APT
工具生成一个class文件,然后我们再说Lombok
是如何修改已存在的类中的属性的。
五、自定义注解
首先当然我们需要定义自己的注解了
@Retention(RetentionPolicy.SOURCE) // 注解只在源码中保留
@Target(ElementType.TYPE) // 用于修饰类
public @interface GeneratePrint {
String value();
}
Retention注解上面有一个属性value,它是RetentionPolicy类型的枚举类,RetentionPolicy枚举类中有三个值。
public enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}
- SOURCE修饰的注解:修饰的注解,表示注解的信息会被编译器抛弃,不会留在class文件中,注解的信息只会留在源文件中
- CLASS修饰的注解:表示注解的信息被保留在class文件(字节码文件)中当程序编译时,但不会被虚拟机读取在运行的时候
- RUNTIME修饰的注解:表示注解的信息被保留在class文件(字节码文件)中当程序编译时,会被虚拟机保留在运行时。所以它能够通过反射调用,所以正常运行时注解都是使用的这个参数
Target注解上面也有个属性value,它是ElementType类型的枚举。是用来修饰此注解作用在哪的。
public enum ElementType {
TYPE,
FIELD,
METHOD,
PARAMETER,
CONSTRUCTOR,
LOCAL_VARIABLE,
ANNOTATION_TYPE,
PACKAGE,
TYPE_PARAMETER,
TYPE_USE
}
六、自定义注解处理器
我们要定义注解处理器的话,那么就需要继承AbstractProcessor
类。继承完以后基本的框架类型如下。
Spring Boot 基础教程及实战示例https://github.com/javastacks/spring-boot-best-practice
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("aboutjava.annotion.MyGetter")
public class MyGetterProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return true;
}
}
我们可以看到在子类中上面有两个注解,注解描述如下
@SupportedSourceVersion:表示所支持的Java版本
@SupportedAnnotationTypes:表示该处理器要处理的注解
继承了父类的两个方法,方法描述如下
init方法:主要是获得编译时期的一些环境信息
process方法:在编译时,编译器执行的方法。也就是我们写具体逻辑的地方