你知道Lombok 的实现原理吗?
泥瓦匠BYSocket
共 9223字,需浏览 19分钟
·
2022-06-17 21:00
不点蓝字关注,我们哪来故事?
Lombok如何使用
compile ("org.projectlombok:lombok:1.16.6")
功能
@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");
}
}
Data
注解,在没有写get、set
方法的时候也能够使用其get、set
方法。我们看它编译过后的class
文件,可以看到它给我们自动生成了get、set
方法。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");
}
public TestLombok() {
}
public String getName() {
return this.name;
}
public Integer getAge() {
return this.age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
}
Spring
框架中@Controller、@Service
等等这类注解都是运行时注解
,运行时注解大部分都是通过反射来实现的。而Lombok
是使用编译时注解实现的。那么编译时注解是什么呢?编译时注解
注解(也被成为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。——————摘自《Thinking in Java》
编译期:Java语言的编译期是一段不确定的操作过程,因为它可能是将 *.java
文件转化成*.class
文件的过程;也可能是指将字节码转变成机器码的过程;还可能是直接将*.java
编译成本地机器代码的过程运行期:从JVM加载字节码文件到内存中,到最后使用完毕以后卸载的过程都属于运行期的范畴。
注解处理工具apt
注解处理工具apt(Annotation Processing Tool),这是Sun为了帮助注解的处理过程而提供的工具,apt被设计为操作Java源文件,而不是编译后的类。
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
类。继承完以后基本的框架类型如下。@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方法:在编译时,编译器执行的方法。也就是我们写具体逻辑的地方
AbstractProcessor
类来实现在编译时生成类,所以我们在process
方法中书写我们生成类的代码。@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
StringBuilder builder = new StringBuilder()
.append("package aboutjava.annotion;\n\n")
.append("public class GeneratedClass {\n\n") // open class
.append("\tpublic String getMessage() {\n") // open method
.append("\t\treturn \"");
// for each javax.lang.model.element.Element annotated with the CustomAnnotation
for (Element element : roundEnv.getElementsAnnotatedWith(MyGetter.class)) {
String objectType = element.getSimpleName().toString();
// this is appending to the return statement
builder.append(objectType).append(" says hello!\\n");
}
builder.append("\";\n") // end return
.append("\t}\n") // close method
.append("}\n"); // close class
try { // write the file
JavaFileObject source = processingEnv.getFiler().createSourceFile("aboutjava.annotion.GeneratedClass");
Writer writer = source.openWriter();
writer.write(builder.toString());
writer.flush();
writer.close();
} catch (IOException e) {
// Note: calling e.printStackTrace() will print IO errors
// that occur from the file already existing after its first run, this is normal
}
return true;
}
定义使用注解的类(测试类)
@MyGetter
public class TestAno {
public static void main(String[] args) {
System.out.printf("1");
}
}
MyGetter
类是注解类没错,而MyGetterProcessor
是注解类的处理器,那么我们在编译TestAno
Java文件的时候就会触发处理器。因此这两个类是无法一起编译的。aboutjava
-- annotion
-- MyGetter.java
-- MyGetterProcessor.java
-- TestAno.java
javac aboutjava/annotion/MyGett*
processor
参数,用来指定相关的注解处理类。javac -processor aboutjava.annotion.MyGetterProcessor aboutjava/annotion/TestAno.java
评论