[Lombok] 优雅编程 之 Lombok

优雅编程 之 Lombok

准备工作

  • IntelliJ IDEA 中搜索并安装Lombook插件
  • maven 引用

  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.8</version>
  </dependency>
  • 开启自动编译配置

Lombook使用介绍

常用注解

  • @Getter and @Setter 可以针对类的属性字段自动生成Get/Set方法

public class OrderCreateDemoReq{

    @Getter
    @Setter
    private String customerId;

    @Setter
    @Getter
    private String poolId;

    //其他代码……
}

//上面请求Req类的代码相当于如下:

public class OrderCreateDemoReq{
    private String customerId;    
    private String poolId;

    public String getCustomerId(){
         return customerId;
    }

    public String getPoolId(){
         return poolId;
    }

    public void setCustomerId(String customerId){
         this.customerId = customerId;
    }

    public void setPoolId(String poolId){
         this.pool = pool;
    }

}
  • @NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor,这几个注解分别为类自动生成了无参构造器、指定参数的构造器和包含所有参数的构造器

@RequiredArgsConstructor(staticName = "of") 
@AllArgsConstructor(access = AccessLevel.PROTECTED) 
public class ConstructorExample<T> { 

  private int x, y; 
  @NonNull private T description; 

  @NoArgsConstructor 
  public static class NoArgsExample { 
    @NonNull private String field; 
  } 

}

//上面代码相当于如下:
public class ConstructorExample<T> { 
  private int x, y; 
  @NonNull private T description; 

  private ConstructorExample(T description) { 
    if (description == null) throw new NullPointerException("description"); 
    this.description = description; 
  } 

  public static <T> ConstructorExample<T> of(T description) { 
    return new ConstructorExample<T>(description); 
  } 

  @java.beans.ConstructorProperties({"x", "y", "description"}) 
  protected ConstructorExample(int x, int y, T description) { 
    if (description == null) throw new NullPointerException("description"); 
    this.x = x; 
    this.y = y; 
    this.description = description; 
  } 

  public static class NoArgsExample { 
    @NonNull private String field;

    public NoArgsExample() { 
    } 
  } 
}
  • @NonNull 能够为方法或构造函数的参数提供非空检查

    public void notNullExample(@NonNull String string) {
        //方法内的代码
    }

    //=>上面代码相当于如下:

    public void notNullExample(String string) {
        if (string != null) {
            //方法内的代码相当于如下:
        } else {
            throw new NullPointerException("null");
        }
    }
  • @ToString 为使用该注解的类生成一个toString方法,默认的toString格式为:ClassName(fieldName= fieleValue ,fieldName1=fieleValue)

    用在类的属性字段上,不需要自己手写setter、getter方法。且能指定访问数组


@ToString(callSuper=true,exclude="someExcludedField")
public   class Demo extends Bar {

    private boolean someBoolean = true;
    private String someStringField;
    private float someExcludedField;

}

//上面代码相当于如下:

public   class Demo extends Bar {

    private boolean someBoolean = true;
    private String someStringField;
    private float someExcludedField;

    @ Override
    public String toString() {
        return "Foo(super=" +   super.toString() +
            ", someBoolean=" +   someBoolean +
            ", someStringField=" +   someStringField + ")";
    }
}
  • @EqualsAndHashCode 为使用该注解的类自动生成equals和hashCode方法。

@EqualsAndHashCode(exclude = {"id"}, callSuper =true)
public class LombokDemo extends Demo{
    private int id;
    private String name;
    private String gender;
}

//上面代码相当于如下:

public class LombokDemo extends Demo{

    private int id;
    private String name;
    private String gender;

    @Override
    public boolean equals(final Object o) {
        if (o == this) return true;
        if (o == null) return false;
        if (o.getClass() != this.getClass()) return false;
        if (!super.equals(o)) return false;
        final LombokDemo other = (LombokDemo)o;
        if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;
        if (this.gender == null ? other.gender != null : !this.gender.equals(other.gender)) return false;
        return true;
    }

    @Override
    public int hashCode() {
        final int PRIME = 31;
        int result = 1;
        result = result * PRIME + super.hashCode();
        result = result * PRIME + (this.name == null ? 0 : this.name.hashCode());
        result = result * PRIME + (this.gender == null ? 0 : this.gender.hashCode());
        return result;
    }

}
  • @Data = @ToString + @EqualsAndHashCode + @Getter + [非final字段]@Setter +@RequiredArgsConstructor

    其包含注解的集合@ToString,@EqualsAndHashCode,所有字段的@Getter和所有非final字段的@Setter,@RequiredArgsConstructor。其中@RequiredArgsConstructor使用了类中的带有@NonNull注解的或者final修饰的成员变量,它可以使用@Data(staticConstructor=”methodName”)来生成一个静态方法,返回一个调用相应的构造方法产生的对象

    注意:

    • @NoArgsConstructor会覆盖@Data中的@RequiredArgsConstructor的相关方法,两者是互相冲突的
    • @Data(staticConstructor=”yoz”) 等效于 @RequiredArgsConstructor(staticName = “yoz”)

@Data(staticConstructor="yoz")
public class DataExample {

    private String x;
    @NonNull
    private int y;
    @NonNull
    private boolean z;

}

//等效于 留意yoz方法

public class DataExample {

    private String x;
    @NonNull
    private int y;
    @NonNull
    private boolean z;

    @java.beans.ConstructorProperties({"y", "z"})
    private DataExample(int y, boolean z) {
        this.y = y;
        this.z = z;
    }

    public static DataExample yoz(int y, boolean z) {
        return new DataExample(y, z);
    }

    public String getX() {
        return this.x;
    }

    @NonNull
    public int getY() {
        return this.y;
    }

    @NonNull
    public boolean isZ() {
        return this.z;
    }

    public void setX(String x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    public void setZ(boolean z) {
        this.z = z;
    }

    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof DataExample)) return false;
        final DataExample other = (DataExample) o;
        if (!other.canEqual((Object) this)) return false;
        final Object this$x = this.x;
        final Object other$x = other.x;
        if (this$x == null ? other$x != null : !this$x.equals(other$x)) return false;
        if (this.y != other.y) return false;
        if (this.z != other.z) return false;
        return true;
    }

    public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        final Object $x = this.x;
        result = result * PRIME + ($x == null ? 0 : $x.hashCode());
        result = result * PRIME + this.y;
        result = ((result * PRIME) + (this.z ? 79 : 97));
        return result;
    }

    public boolean canEqual(Object other) {
        return other instanceof DataExample;
    }

    public String toString() {
        return "com.example.chapter1.lombok.DataExample(x=" + this.x + ", y=" + this.y + ", z=" + this.z + ")";
    }
}
  • @Builder注解提供了一种比较推崇的构建值对象的方式

@Builder 
public class BuilderExample { 

  private String name; 
  private int age; 

  @Singular private Set<String> occupations; 

}

//上面代码相当于如下:

public class BuilderExample { 

  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; 
  } 

  public static BuilderExampleBuilder builder() { 
    return new BuilderExampleBuilder(); 
  } 

  public static class BuilderExampleBuilder { 

    private String name; 
    private int age; 
    private java.util.ArrayList<String> occupations;    

    BuilderExampleBuilder() { 
    } 

    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() {  
      Set<String> occupations = new HashSet<>(); 
      return new BuilderExample(name, age, occupations); 
    } 

    @verride 
    public String toString() { 
      return "BuilderExample.BuilderExampleBuilder(name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")"; 
    } 
  } 
}
  • @Cleanup 能够自动释放资源

public   void jedisExample(String[] args) {
       try {
           @Cleanup Jedis jedis =   redisService.getJedis();
       } catch (Exception ex) {
           logger.error(“Jedis异常:”,ex)
       }

       //=>上面代码相当于如下:
       Jedis jedis= null;
       try {
           jedis = redisService.getJedis();
       } catch (Exception e) {
           logger.error(“Jedis异常:”,ex)
       } finally {
           if (jedis != null) {
               try {
                   jedis.close();
               } catch (Exception e) {
                   e.printStackTrace();
               }
           }
       }
   }
  • @Synchronized

    类似Java中的Synchronized 关键字,但是可以隐藏同步锁
    注解用在类方法或者实例方法上,效果和synchronized关键字相同,区别在于锁对象不同,对于类方法和实例方法,synchronized关键字的锁对象分别是类的class对象和this对象,而@Synchronized得锁对象分别是私有静态final对象LOCK和私有final对象lock,当然,也可以自己指定锁对象


public class SynchronizedExample { 

 private final Object readLock = new   Object(); 

 @Synchronized 
 public static void hello() { 
     System.out.println("world");   
 } 

 @Synchronized("readLock") 
 public void foo() { 
   System.out.println("bar"); 
 } 

//上面代码相当于如下:

 public class SynchronizedExample { 

  private static final Object $LOCK = new   Object[0]; 
  private final Object readLock = new   Object(); 

  public static void hello() { 
    synchronized($LOCK) { 
      System.out.println("world"); 
    } 
  }   

  public void foo() { 
    synchronized(readLock) { 
        System.out.println("bar");   
    } 
  } 

}
  • @SneakyThrows

    注解用在方法上,可以将方法中的代码用try-catch语句包裹起来,捕获异常并在catch中用Lombok.sneakyThrow(e)把异常抛出,可以使用@SneakyThrows(Exception.class)的形式指定抛出哪种异常,很简单的注解


@SneakyThrows
public void testSneakyThrows() {
    throw new IllegalAccessException();
}

public void testSneakyThrows() {
    try {
        throw new IllegalAccessException();
    } catch (java.lang.Throwable $ex) {
        throw lombok.Lombok.sneakyThrow($ex);
    }
}
  • @val 声明变量类型为final

public   static void main(String[] args) {

    val setVar = new HashSet<String>();
    val listsVar = new   ArrayList<String>();
    val mapVar = new HashMap<String,   String>();

    //=>上面代码相当于如下:
    final Set<String> setVar2 = new   HashSet<>();
    final List<String> listsVar2 = new   ArrayList<>();
    final Map<String, String> maps2 =   new HashMap<>();

}
  • @Log

    这个注解用在类上,可以省去从日志工厂生成日志对象这一步,直接进行日志记录,具体注解根据日志工具的不同而不同,同时,可以在注解中使用topic来指定生成log对象时的类名。不同的日志注解总结如下(上面是注解,下面是实际作用)


@CommonsLog
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
@JBossLog
private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
@Log
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
@Log4j
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
@Log4j2
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
@Slf4j
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
@XSlf4j
private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

Constructor专题

@RequiredArgsConstructor

会生成一个包含常量,和`标识了NotNull的变量` 的构造方法。生成的构造方法是private,如果想要对外提供使用可以使用staticName选项
生成一个static方法

@RequiredArgsConstructor(staticName = "sunsfan")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor
public class Shape {
    private int x;
    @NonNull
    private double y;
    @NonNull
    private String name;
}

//等效于

public class Shape {
    private int x;
    private double y;
    private String name;

    public Shape(){
    }

    protected Shape(int x,double y,String name){
        this.x = x;
        this.y = y;
        this.name = name;
    }

    public Shape(double y,String name){
        this.y = y;
        this.name = name;
    }

    public static Shape sunsfan(double y,String name){
        return new Shape(y,name);
    }
}

@AllArgsContructor

会生成一个包含所有变量,同时如果变量使用了NotNull annotation , 会进行是否为空的校验

@NoArgsConstructor

生成一个无参数的构造方法,这个annotation在与其他的annotation配合起来使用的时候更加能凸显出他的重要性,例如在使用hibernate这
种框架的时候,如果有一个有参数的构造方法的时候,NoArgsConstructor会展示出他的作用

自定义注解原理

Lombok不是通过字节码改写来实现的。它主要是用编译器内支持的annotation processing,直接操纵抽象语法树(AST),根据需要添加新节点。依靠可插件化的Java自定义注解处理API(JSR 269: Pluggable Annotation Processing API)来实现在Javac编译阶段利用“Annotation Processor”对自定义的注解进行预处理后生成真正在JVM上面执行的“Class文件”。其大致执行原理图如下:

输入图片说明

从上面的这个原理图上可以看出Annotation Processing是编译器在解析Java源代码和生成Class文件之间的一个步骤。其中Lombok插件具体的执行流程如下:

输入图片说明

从上面的Lombok执行的流程图中可以看出,在Javac 解析成AST抽象语法树之后, Lombok 根据自己编写的注解处理器,动态地修改 AST,增加新的节点(即Lombok自定义注解所需要生成的代码),最终通过分析生成JVM可执行的字节码Class文件。使用Annotation Processing自定义注解是在编译阶段进行修改,而JDK的反射技术是在运行时动态修改,两者相比,反射虽然更加灵活一些但是带来的性能损耗更加大。

输入图片说明

从熟悉JSR 269: Pluggable Annotation Processing API的同学可以从工程类结构图中发现AnnotationProcessor这个类是Lombok自定义注解处理的入口。该类有两个比较重要的方法:一个是init方法,另外一个是process方法。在init方法中,先用来做参数的初始化,将AnnotationProcessor类中定义的内部类(JavacDescriptor、EcjDescriptor)先注册到ProcessorDescriptor类型定义的列表中。其中,内部静态类—JavacDescriptor在其加载的时候就将lombok.javac.apt.LombokProcessor这个类进行对象实例化并注册。

在LombokProcessor处理器中,其中的process方法会根据优先级来分别运行相应的handler处理类。Lombok中的多个自定义注解都分别有对应的handler处理类,如下图所示:

输入图片说明

可以看出,在Lombok中对于其自定义注解进行实际的替换、修改和处理的正是这些handler类。对于其实现的细节可以具体参考其中的代码。

如果想自己动手写个Getter方法,可以参考下文

Lombok原理分析与功能实现

REFRENCES

  1. 官方使用文档
  2. Java开发神器Lombok的使用与原理
  3. Lombook注释说明文档

微信公众号



扫码关注或搜索架构探险之道获取最新文章,坚持每周一更,坚持技术分享的我和你们一起成长 ^_^ !

猜你喜欢

转载自blog.csdn.net/sinat_28690417/article/details/80750007