你知道Lombok 的實現(xiàn)原理嗎?
不點藍字關注,我們哪來故事?
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等等這類注解都是運行時注解,運行時注解大部分都是通過反射來實現(xiàn)的。而Lombok是使用編譯時注解實現(xiàn)的。那么編譯時注解是什么呢?編譯時注解
注解(也被成為元數(shù)據(jù))為我們在代碼中添加信息提供了一種形式化的方法,使我們可以在稍后某個時刻非常方便地使用這些數(shù)據(jù)。——————摘自《Thinking in Java》
編譯期:Java語言的編譯期是一段不確定的操作過程,因為它可能是將 *.java文件轉(zhuǎn)化成*.class文件的過程;也可能是指將字節(jié)碼轉(zhuǎn)變成機器碼的過程;還可能是直接將*.java編譯成本地機器代碼的過程運行期:從JVM加載字節(jié)碼文件到內(nèi)存中,到最后使用完畢以后卸載的過程都屬于運行期的范疇。
注解處理工具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文件(字節(jié)碼文件)中當程序編譯時,但不會被虛擬機讀取在運行的時候RUNTIME修飾的注解:表示注解的信息被保留在class文件(字節(jié)碼文件)中當程序編譯時,會被虛擬機保留在運行時。所以它能夠通過反射調(diào)用,所以正常運行時注解都是使用的這個參數(shù)
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方法:主要是獲得編譯時期的一些環(huán)境信息 process方法:在編譯時,編譯器執(zhí)行的方法。也就是我們寫具體邏輯的地方
AbstractProcessor類來實現(xiàn)在編譯時生成類,所以我們在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是注解類的處理器,那么我們在編譯TestAnoJava文件的時候就會觸發(fā)處理器。因此這兩個類是無法一起編譯的。aboutjava
-- annotion
-- MyGetter.java
-- MyGetterProcessor.java
-- TestAno.java
javac aboutjava/annotion/MyGett*
processor參數(shù),用來指定相關的注解處理類。javac -processor aboutjava.annotion.MyGetterProcessor aboutjava/annotion/TestAno.java


評論
圖片
表情
