当前位置:网站首页>面试官:你天天用 Lombok,说说它什么原理?我竟然答不上来…
面试官:你天天用 Lombok,说说它什么原理?我竟然答不上来…
2022-06-27 06:28:00 【xy29981】
Lombok如何使用
功能
编译时注解
注解处理工具apt
定义注解
定义注解处理器
定义使用注解的类(测试类)
相信大家在项目中都使用过Lombok,因为能够简化我们许多的代码,但是该有的功能一点也不少。
那么lombok到底是个什么呢,lombok是一个可以通过简单的注解的形式来帮助我们简化消除一些必须有但显得很臃肿的 Java 代码的工具,简单来说,比如我们新建了一个类,然后在其中写了几个字段,然后通常情况下我们需要手动去建立getter和setter方法啊,构造函数啊之类的,lombok的作用就是为了省去我们手动创建这些代码的麻烦,它能够在我们编译源码的时候自动帮我们生成这些方法。
那么Lombok到底是如何做到这些的呢?其实底层就是用到了编译时注解的功能。
Lombok如何使用
Lombok是一个开源项目,代码是在lombok中,如果是gradle项目的话直接在项目中引用如下即可。
compile ("org.projectlombok:lombok:1.16.6")
功能
那么Lombok是做什么呢?其实很简单,一个最简单的例子就是能够通过添加注解自动生成一些方法,使我们代码更加简洁易懂。例如下面一个类。
1 @Data
2 public class TestLombok {
3 private String name;
4 private Integer age;
5
6 public static void main(String[] args) {
7 TestLombok testLombok = new TestLombok();
8 testLombok.setAge(12);
9 testLombok.setName("zs");
10 }
11 }
我们使用Lombok提供的Data注解,在没有写get、set方法的时候也能够使用其get、set方法。我们看它编译过后的class文件,可以看到它给我们自动生成了get、set方法。
1 public class TestLombok {
2 private String name;
3 private Integer age;
4
5 public static void main(String[] args) {
6 TestLombok testLombok = new TestLombok();
7 testLombok.setAge(12);
8 testLombok.setName("zs");
9 }
10
11 public TestLombok() {
12 }
13
14 public String getName() {
15 return this.name;
16 }
17
18 public Integer getAge() {
19 return this.age;
20 }
21
22 public void setName(String name) {
23 this.name = name;
24 }
25
26 public void setAge(Integer age) {
27 this.age = age;
28 }
29
30}
当然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是如何修改已存在的类中的属性的。
定义注解
首先当然我们需要定义自己的注解了
1 @Retention(RetentionPolicy.SOURCE)
2 @Target(ElementType.TYPE)
3 public @interface GeneratePrint {
4
5 String value();
6 }
Retention注解上面有一个属性value,它是RetentionPolicy类型的枚举类,RetentionPolicy枚举类中有三个值。
1 public enum RetentionPolicy {
2
3 SOURCE,
4
5 CLASS,
6
7 RUNTIME
8 }
SOURCE修饰的注解:修饰的注解,表示注解的信息会被编译器抛弃,不会留在class文件中,注解的信息只会留在源文件中CLASS修饰的注解:表示注解的信息被保留在class文件(字节码文件)中当程序编译时,但不会被虚拟机读取在运行的时候RUNTIME修饰的注解:表示注解的信息被保留在class文件(字节码文件)中当程序编译时,会被虚拟机保留在运行时。所以它能够通过反射调用,所以正常运行时注解都是使用的这个参数
Target注解上面也有个属性value,它是ElementType类型的枚举。是用来修饰此注解作用在哪的。
1 public enum ElementType {
2 TYPE,
3
4 FIELD,
5
6 METHOD,
7
8 PARAMETER,
9
10 CONSTRUCTOR,
11
12 LOCAL_VARIABLE,
13
14 ANNOTATION_TYPE,
15
16 PACKAGE,
17
18 TYPE_PARAMETER,
19
20 TYPE_USE
21 }
定义注解处理器
我们要定义注解处理器的话,那么就需要继承AbstractProcessor类。继承完以后基本的框架类型如下
1 @SupportedSourceVersion(SourceVersion.RELEASE_8)
2 @SupportedAnnotationTypes("aboutjava.annotion.MyGetter")
3 public class MyGetterProcessor extends AbstractProcessor {
4 @Override
5 public synchronized void init(ProcessingEnvironment processingEnv) {
6 super.init(processingEnv);
7 }
8
9 @Override
10 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
11 return true;
12 }
13 }
我们可以看到在子类中上面有两个注解,注解描述如下
@SupportedSourceVersion:表示所支持的Java版本@SupportedAnnotationTypes:表示该处理器要处理的注解
继承了父类的两个方法,方法描述如下
init方法:主要是获得编译时期的一些环境信息
process方法:在编译时,编译器执行的方法。也就是我们写具体逻辑的地方
我们是演示一下如何通过继承AbstractProcessor类来实现在编译时生成类,所以我们在process方法中书写我们生成类的代码。如下所示。
1 @Override
2 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
3 StringBuilder builder = new StringBuilder()
4 .append("package aboutjava.annotion;\n\n")
5 .append("public class GeneratedClass {\n\n")
6 .append("\tpublic String getMessage() {\n")
7 .append("\t\treturn \"");
8
9 for (Element element : roundEnv.getElementsAnnotatedWith(MyGetter.class)) {
10 String objectType = element.getSimpleName().toString();
11
12 builder.append(objectType).append(" says hello!\\n");
13 }
14 builder.append("\";\n")
15 .append("\t}\n")
16 .append("}\n");
17 try {
18 JavaFileObject source = processingEnv.getFiler().createSourceFile("aboutjava.annotion.GeneratedClass");
19 Writer writer = source.openWriter();
20 writer.write(builder.toString());
21 writer.flush();
22 writer.close();
23 } catch (IOException e) {
24
25
26 }
27 return true;
28 }
定义使用注解的类(测试类)
上面的两个类就是基本的工具类了,一个是定义了注解,一个是定义了注解处理器,接下来我们来定义一个测试类(TestAno.java)。我们在类上面加上我们自定的注解类。
1 @MyGetter
2 public class TestAno {
3
4 public static void main(String[] args) {
5 System.out.printf("1");
6 }
7 }
这样我们在编译期就能生成文件了,接下来演示一下在编译时生成文件,此时不要着急直接进行javac编译,MyGetter类是注解类没错,而MyGetterProcessor是注解类的处理器,那么我们在编译TestAnoJava文件的时候就会触发处理器。因此这两个类是无法一起编译的。
先给大家看一下我的目录结构
aboutjava2
-- annotion3
-- MyGetter.java4
-- MyGetterProcessor.java5
-- TestAno.java
所以我们先将注解类和注解处理器类进行编译
javac aboutjava/annotion/MyGett*
接下来进行编译我们的测试类,此时在编译时需要加上processor参数,用来指定相关的注解处理类。
javac -processor aboutjava.annotion.MyGetterProcessor aboutjava/annotion/TestAno.java
大家可以看到动态图中,自动生成了Java文件。

图片
敬请期待我的下一篇文章,谢谢。

更多java进阶资料,面试资料,关注公众号
边栏推荐
- 快速实现蓝牙iBeacn功能详解
- 使用 WordPress快速个人建站指南
- Program ape learning Tiktok short video production
- 浅谈GPU:历史发展,架构
- [QT dot] QT download link
- Caldera installation and simple use
- JVM常用指令
- Matlab quickly converts two-dimensional coordinates of images into longitude and latitude coordinates
- Sqlsever 字段相乘后保留2位小数
- When there are multiple El select, the selected value is filtered by El select, and the last selected value is filtered by the second El select
猜你喜欢

matlab GUI界面仿真直流电机和交流电机转速仿真

Assembly language - Wang Shuang Chapter 8 two basic problems in data processing - Notes

Inter thread wait and wake-up mechanism, singleton mode, blocking queue, timer

古典密码体制--代换和置换

多线程带来的的风险——线程安全

JVM object composition and storage

thrift

Meaning of 0.0.0.0:x

Fractional Order PID control

Quick realization of Bluetooth ibeacn function
随机推荐
TiDB的使用限制
Free SSH and telnet client putty
JVM garbage collection mechanism
The SCP command is used in the expect script. The perfect solution to the problem that the SCP command in the expect script cannot obtain the value
力扣 179、最大数
第 299 场周赛 第四题 6103. 从树中删除边的最小分数
Compatibility comparison between tidb and MySQL
【QT小记】QT元对象系统简单认识
Ora-00909: invalid number of parameters, caused by concat
Meaning of 0.0.0.0:x
[getting started] regular expression Basics
TiDB 中的SQL 基本操作
TiDB 数据库快速上手指南
Mathematical modeling contest for graduate students - optimal application of UAV in rescue and disaster relief
The risk of multithreading -- thread safety
multiprocessing.pool详解
ORA-00909: 参数个数无效,concat引起
Once spark reported an error: failed to allocate a page (67108864 bytes), try again
Block level elements & inline elements
multiprocessing. Detailed explanation of pool