lombok奇淫之技


簡介:
       當我們在做項目的時候,往往需要定義大量的JavaBean,而且每一個實體的每一個成員變量都需要定義getter/setter方法,顯得比較繁瑣。而Lombok通過注解的方式幫助我們進行去繁瑣化,提供一個簡潔編程方式。Lombok的注解並沒有使用到Java的反射機制,而是通過一些奇淫技巧,在代碼編譯時期動態的將注解轉換為具體的代碼。

依賴添加:
>>Gradle:

provided group: 'org.projectlombok', name: 'lombok', version: '1.16.18'

>>Maven::

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
<scope>provided</scope>
</dependency>

Lombok Idea插件安裝:
>>Step.1、Setting -> plugins -> Browse repositories -> lombok search -> install

>>Step.2、點擊右邊的install,然后重啟Intellij Idea即可。ps:該截圖是因為已經安裝完畢,所以沒有install按鈕

>>Step.3、Lombok安裝完成之后,當第一次啟動項目的時候,會報:Lombok Requires Annotation Processing的錯誤,如下所示:

>>Step.4、這個時候是需要為項目開啟 Enable annotation processing功能,可以點擊藍色的字直接打開編輯頁面,也可以通過setting -> Build,Execution,Deployment -> Annotation Processors打開編輯頁面,然后選擇 Enable annotation processing前面的勾勾,點擊Apply重啟Idea即可

Lombok注解:

  1. @NonNull:在參數中使用時,如果調用時傳了null值,會拋出空異常
  2. @Cleanup:關閉流、連接點
  3. @Setter:注解在類上,為所有的屬性添加set方法、注解在屬性上為該屬性提供set方法
  4. @Getter:注解在類上,為所有的屬性添加get方法、注解在屬性上為該屬性提供get方法
  5. @ToString:創建一個toString方法
  6. @EqualsAndHashCode:重寫equals和hashCode方法
  7. @NoArgsConstructor:創建一個無參構造函數
  8. @AllArgsConstructor:創建一個全參構造函數
  9. @RequiredArgsConstructor:創建一個包含所有非null值字段的構造函數
  10. @Data:注解在類上,將類提供的所有屬性都添加get、set方法,並添加equals、canEquals、hashCode和toString方法
  11. @Value: 所有字段默認為私有和最終的,並且不會生成setter,是@Data的不可變變體
  12. @Builder:使用builder模式創建對象。ps:builder模式請參見effective java第一章或google
  13. @SneakyThrows:代替異常捕獲,只需要將異常例外(任意數量的)傳遞給該注釋
  14. @Synchronized:用於方法,可以鎖定指定的對象,如果不指定,則默認創建一個對象鎖定
  15. @Getter(lazy=true):您可以讓lombok生成一個getter,它會在第一次調用getter時計算一次值,並從此開始緩存。 如果計算該值需要大量的CPU,或者該值需要大量內存,則這可能很有用。 要使用此功能,請創建一個私有最終變量,使用運行費用較高的表達式對其進行初始化,並使用@Getter(lazy = true)對您的字段進行注釋。 該字段將隱藏其余的代碼,並且當第一次調用getter時,表達式將被評估不超過一次。 沒有神奇的標記值(即使您的昂貴計算結果為空,結果被緩存),並且昂貴的計算不需要線程安全,因為lombok負責鎖定。
  16. @Log:作用於類,創建一個Log屬性

官方deamo:

  這里只羅列@NonNull、@Data、@Builder、@SneakyThrows四個注解的demo,從demo可與看出將我們的代碼復雜度簡潔了不少。需要了解其它注解的demo請參考官方網站:https://projectlombok.org/features/

  1. @nonNull 
    • With Lombok
      import lombok.NonNull;
      
      public class NonNullExample extends Something {
        private String name;
        
        public NonNullExample(@NonNull Person person) {
          super("Hello");
          this.name = person.getName();
        }
      }
    • Vanilla Java
      import lombok.NonNull;
      
      public class NonNullExample extends Something {
          private String name;
        
          public NonNullExample(@NonNull Person person) {
              super("Hello");
              if (person == null) {
                  throw new NullPointerException("person");
              }
              this.name = person.getName();
          }
      }
  2. @Data
    • With Lombok
      import lombok.AccessLevel;
      import lombok.Setter;
      import lombok.Data;
      import lombok.ToString;
      
      @Data public class DataExample {
        private final String name;
        @Setter(AccessLevel.PACKAGE) private int age;
        private double score;
        private String[] tags;
        
        @ToString(includeFieldNames=true)
        @Data(staticConstructor="of")
        public static class Exercise<T> {
          private final String name;
          private final T value;
        }
      }
    • Vanilla Java
      import java.util.Arrays;
      
      public class DataExample {
        private final String name;
        private int age;
        private double score;
        private String[] tags;
        
        public DataExample(String name) {
          this.name = name;
        }
        
        public String getName() {
          return this.name;
        }
        
        void setAge(int age) {
          this.age = age;
        }
        
        public int getAge() {
          return this.age;
        }
        
        public void setScore(double score) {
          this.score = score;
        }
        
        public double getScore() {
          return this.score;
        }
        
        public String[] getTags() {
          return this.tags;
        }
        
        public void setTags(String[] tags) {
          this.tags = tags;
        }
        
        @Override public String toString() {
          return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")";
        }
        
        protected boolean canEqual(Object other) {
          return other instanceof DataExample;
        }
        
        @Override public boolean equals(Object o) {
          if (o == this) return true;
          if (!(o instanceof DataExample)) return false;
          DataExample other = (DataExample) o;
          if (!other.canEqual((Object)this)) return false;
          if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
          if (this.getAge() != other.getAge()) return false;
          if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
          if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
          return true;
        }
        
        @Override public int hashCode() {
          final int PRIME = 59;
          int result = 1;
          final long temp1 = Double.doubleToLongBits(this.getScore());
          result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
          result = (result*PRIME) + this.getAge();
          result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
          result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
          return result;
        }
        
        public static class Exercise<T> {
          private final String name;
          private final T value;
          
          private Exercise(String name, T value) {
            this.name = name;
            this.value = value;
          }
          
          public static <T> Exercise<T> of(String name, T value) {
            return new Exercise<T>(name, value);
          }
          
          public String getName() {
            return this.name;
          }
          
          public T getValue() {
            return this.value;
          }
          
          @Override public String toString() {
            return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
          }
          
          protected boolean canEqual(Object other) {
            return other instanceof Exercise;
          }
          
          @Override public boolean equals(Object o) {
            if (o == this) return true;
            if (!(o instanceof Exercise)) return false;
            Exercise<?> other = (Exercise<?>) o;
            if (!other.canEqual((Object)this)) return false;
            if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false;
            if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
            return true;
          }
          
          @Override public int hashCode() {
            final int PRIME = 59;
            int result = 1;
            result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
            result = (result*PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode());
            return result;
          }
        }
      }
  3. @Builder
    • With Lombok
      import lombok.Builder;
      import lombok.Singular;
      import java.util.Set;
      
      @Builder
      public class BuilderExample {
        @Builder.Default private long created = System.currentTimeMillis();
        private String name;
        private int age;
        @Singular private Set<String> occupations;
      }
    • Vanilla Java
      import java.util.Set;
      
      public class BuilderExample {
        private long created;
        private String name;
        private int age;
        private Set<String> occupations;
        
        BuilderExample(String name, int age, Set<String> occupations) {
          this.name = name;
          this.age = age;
          this.occupations = occupations;
        }
        
        private static long $default$created() {
          return System.currentTimeMillis();
        }
        
        public static BuilderExampleBuilder builder() {
          return new BuilderExampleBuilder();
        }
        
        public static class BuilderExampleBuilder {
          private long created;
          private boolean created$set;
          private String name;
          private int age;
          private java.util.ArrayList<String> occupations;
          
          BuilderExampleBuilder() {
          }
          
          public BuilderExampleBuilder created(long created) {
            this.created = created;
            this.created$set = true;
            return this;
          }
          
          public BuilderExampleBuilder name(String name) {
            this.name = name;
            return this;
          }
          
          public BuilderExampleBuilder age(int age) {
            this.age = age;
            return this;
          }
          
          public BuilderExampleBuilder occupation(String occupation) {
            if (this.occupations == null) {
              this.occupations = new java.util.ArrayList<String>();
            }
            
            this.occupations.add(occupation);
            return this;
          }
          
          public BuilderExampleBuilder occupations(Collection<? extends String> occupations) {
            if (this.occupations == null) {
              this.occupations = new java.util.ArrayList<String>();
            }
      
            this.occupations.addAll(occupations);
            return this;
          }
          
          public BuilderExampleBuilder clearOccupations() {
            if (this.occupations != null) {
              this.occupations.clear();
            }
            
            return this;
          }
      
          public BuilderExample build() {
            // complicated switch statement to produce a compact properly sized immutable set omitted.
            Set<String> occupations = ...;
            return new BuilderExample(created$set ? created : BuilderExample.$default$created(), name, age, occupations);
          }
          
          @java.lang.Override
          public String toString() {
            return "BuilderExample.BuilderExampleBuilder(created = " + this.created + ", name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
          }
        }
      }
  4. @SneakyThrows
    • With Lombok
      import lombok.SneakyThrows;
      
      public class SneakyThrowsExample implements Runnable {
        @SneakyThrows(UnsupportedEncodingException.class)
        public String utf8ToString(byte[] bytes) {
          return new String(bytes, "UTF-8");
        }
        
        @SneakyThrows
        public void run() {
          throw new Throwable();
        }
      }
    • Vanilla Java
      import lombok.Lombok;
      
      public class SneakyThrowsExample implements Runnable {
        public String utf8ToString(byte[] bytes) {
          try {
            return new String(bytes, "UTF-8");
          } catch (UnsupportedEncodingException e) {
            throw Lombok.sneakyThrow(e);
          }
        }
        
        public void run() {
          try {
            throw new Throwable();
          } catch (Throwable t) {
            throw Lombok.sneakyThrow(t);
          }
        }
      }

       

Lombok之奇淫技:

   其奇就奇在是通過對注解的解析來進行編譯時增強的。說到解析我們得對比區分下:

運行時解析:

       運行時解析,我們必須使用元注解的RetentionPolicy.RUNTIME,這樣我們得程序在運行時,我們才可以通過反射的方式拿到該注解。java.lang.reflect反射包中提過了一個接口AnnotatedElment,該接口定義了獲取注解信息的幾個方法,Class、Constructor、Field、Method、Package等都實現了該接口。

boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
Annotation[] getAnnotations();
Annotation[] getDeclaredAnnotations();

編譯時解析:

      編譯時解析涉及到兩種規則(其實自己也沒搞懂,這里引用一下別人的區分)

a. Annotation Processing Tool

apt自JDK5產生,JDK7已標記為過期,不推薦使用,JDK8中已徹底刪除,自JDK6開始,可以使用Pluggable Annotation Processing API來替換它,apt被替換主要有2點原因:

  • api都在com.sun.mirror非標准包下
  • 沒有集成到javac中,需要額外運行

  apt的更多介紹可以參見這里

b. Pluggable Annotation Processing API

JSR 269,自JDK6加入,作為apt的替代方案,它解決了apt的兩個問題,javac在執行的時候會調用實現了該API的程序,這樣我們就可以對編譯器做一些增強,這時javac執行的過程如下: 
這里寫圖片描述 
  Lombok就是使用這種方式實現的,有興趣的話可以去看看其Lombok源碼,對應注解的實現都在HandleXXX中,比如@Getter注解的實現是HandleGetter.handle()。還有一些其它類庫使用這種方式實現,比如Google AutoDagger等等。

Lombok問題:

  • 無法支持多種參數構造器的重載
  • 奇淫巧技,使用會有爭議

參考文章:

  • https://blog.csdn.net/ghsau/article/details/52334762
  • https://blog.mythsman.com/2017/12/19/1/
  • https://projectlombok.org/features/experimental/all
  • https://yq.aliyun.com/articles/59972#11


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM