相信你們在項目中都使用過Lombok,由於可以簡化咱們許多的代碼,可是該有的功能一點也很多。那麼lombok究竟是個什麼呢,lombok是一個能夠經過簡單的註解的形式來幫助咱們簡化消除一些必須有但顯得很臃腫的 Java 代碼的工具,簡單來講,好比咱們新建了一個類,而後在其中寫了幾個字段,而後一般狀況下咱們須要手動去創建getter和setter方法啊,構造函數啊之類的,lombok的做用就是爲了省去咱們手動建立這些代碼的麻煩,它可以在咱們編譯源碼的時候自動幫咱們生成這些方法。css
那麼Lombok究竟是如何作到這些的呢?其實底層就是用到了編譯時註解的功能。java
Lombok是一個開源項目,代碼是在lombok中,若是是gradle項目的話直接在項目中引用以下便可。nginx
1compile ("org.projectlombok:lombok:1.16.6")
複製代碼
那麼Lombok是作什麼呢?其實很簡單,一個最簡單的例子就是可以經過添加註解自動生成一些方法,使咱們代碼更加簡潔易懂。例以下面一個類。git
1@Data
2public 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
方法。github
1public 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
是使用編譯時註解實現的。那麼編譯時註解是什麼呢?sql
註解(也被成爲元數據)爲咱們在代碼中添加信息提供了一種形式化的方法,使咱們能夠在稍後某個時刻很是方便地使用這些數據。 ——————摘自《Thinking in Java》app
Java中的註解分爲運行時註解和編譯時註解,運行時註解就是咱們常用的在程序運行時經過反射獲得咱們註解的信息,而後再作一些操做。而編譯時註解是什麼呢?就是在程序在編譯期間經過註解處理器進行處理。框架
.java`文件轉化成`.class
文件的過程;也多是指將字節碼轉變成機器碼的過程;還多是直接將*.java
編譯成本地機器代碼的過程註解處理工具apt(Annotation Processing Tool),這是Sun爲了幫助註解的處理過程而提供的工具,apt被設計爲操做Java源文件,而不是編譯後的類。ide
它是javac的一個工具,中文意思爲編譯時註解處理器。APT能夠用來在編譯時掃描和處理註解。經過APT能夠獲取到註解和被註解對象的相關信息,在拿到這些信息後咱們能夠根據需求來自動的生成一些代碼,省去了手動編寫。注意,獲取註解及生成代碼都是在代碼編譯時候完成的,相比反射在運行時處理註解大大提升了程序性能。APT的核心是AbstractProcessor類。函數
正常狀況下使用APT工具只是可以生成一些文件(不只僅是咱們想象的class文件,還包括xml文件等等之類的),並不能修改原有的文件信息。
可是此時估計會有疑問,那麼Lombok
不就是在咱們原有的文件中新增了一些信息嗎?我在後面會有詳細的解釋,這裏簡單介紹一下,其實Lombok
是修改了Java中的抽象語法樹AST
才作到了修改其原有類的信息。
接下來咱們演示一下如何用APT
工具生成一個class文件,而後咱們再說Lombok
是如何修改已存在的類中的屬性的。
首先固然咱們須要定義本身的註解了
1@Retention(RetentionPolicy.SOURCE) // 註解只在源碼中保留
2@Target(ElementType.TYPE) // 用於修飾類
3public @interface GeneratePrint {
4
5 String value();
6}
複製代碼
Retention
註解上面有一個屬性value,它是RetentionPolicy
類型的枚舉類,RetentionPolicy
枚舉類中有三個值。
1public enum RetentionPolicy {
2
3 SOURCE,
4
5 CLASS,
6
7 RUNTIME
8}
複製代碼
SOURCE
修飾的註解:修飾的註解,表示註解的信息會被編譯器拋棄,不會留在class文件中,註解的信息只會留在源文件中CLASS
修飾的註解:表示註解的信息被保留在class文件(字節碼文件)中當程序編譯時,但不會被虛擬機讀取在運行的時候RUNTIME
修飾的註解:表示註解的信息被保留在class文件(字節碼文件)中當程序編譯時,會被虛擬機保留在運行時。因此它可以經過反射調用,因此正常運行時註解都是使用的這個參數Target
註解上面也有個屬性value,它是ElementType
類型的枚舉。是用來修飾此註解做用在哪的。
1public 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")
3public 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
:表示該處理器要處理的註解繼承了父類的兩個方法,方法描述以下
咱們是演示一下如何經過繼承AbstractProcessor
類來實如今編譯時生成類,因此咱們在process
方法中書寫咱們生成類的代碼。以下所示。
1@Override
2public 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") // open class
6 .append("\tpublic String getMessage() {\n") // open method
7 .append("\t\treturn \"");
8 // for each javax.lang.model.element.Element annotated with the CustomAnnotation
9 for (Element element : roundEnv.getElementsAnnotatedWith(MyGetter.class)) {
10 String objectType = element.getSimpleName().toString();
11 // this is appending to the return statement
12 builder.append(objectType).append(" says hello!\\n");
13 }
14 builder.append("\";\n") // end return
15 .append("\t}\n") // close method
16 .append("}\n"); // close class
17 try { // write the file
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 // Note: calling e.printStackTrace() will print IO errors
25 // that occur from the file already existing after its first run, this is normal
26 }
27 return true;
28}
複製代碼
上面的兩個類就是基本的工具類了,一個是定義了註解,一個是定義了註解處理器,接下來咱們來定義一個測試類(TestAno.java)。咱們在類上面加上咱們自定的註解類。
1@MyGetter
2public class TestAno {
3
4 public static void main(String[] args) {
5 System.out.printf("1");
6 }
7}
複製代碼
這樣咱們在編譯期就能生成文件了,接下來演示一下在編譯時生成文件,此時不要着急直接進行javac編譯,MyGetter
類是註解類沒錯,而MyGetterProcessor
是註解類的處理器,那麼咱們在編譯TestAno
Java文件的時候就會觸發處理器。所以這兩個類是沒法一塊兒編譯的。
先給你們看一下個人目錄結構
1aboutjava
2 -- annotion
3 -- MyGetter.java
4 -- MyGetterProcessor.java
5 -- TestAno.java
複製代碼
因此咱們先將註解類和註解處理器類進行編譯
1javac aboutjava/annotion/MyGett*
複製代碼
接下來進行編譯咱們的測試類,此時在編譯時須要加上processor
參數,用來指定相關的註解處理類。
1javac -processor aboutjava.annotion.MyGetterProcessor aboutjava/annotion/TestAno.java
複製代碼
你們能夠看到動態圖中,自動生成了Java文件。
本篇文章還會有第二篇進行講解Lombok的原理,如何修改原有類的內容。本篇做爲前置知識,簡單的介紹了註解處理器是什麼,如何利用註解處理器作一些咱們在編譯期纔可以作的事情。但願你們可以本身在本機上試驗一下,若是本篇有任何問題歡迎指出。
有感興趣的能夠關注一下我新建的公衆號,搜索[程序猿的百寶袋]。或者直接掃下面的碼也行。