前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Lombok,简化代码的神器,你值得拥有

Lombok,简化代码的神器,你值得拥有

作者头像
孟君
发布2019-08-26 17:02:13
1.5K0
发布2019-08-26 17:02:13
举报
文章被收录于专栏:孟君的编程札记

本文给大家介绍一个Java代码简化的神器 -- Lombok。主要从如下几个方面进行展开:

  1. Lombok介绍
  2. Lombok安装
  3. Lombok注解使用
  4. Lombok原理解析
  5. 小结

1、Lombok介绍

根据Lombok官网https://projectlombok.org/的介绍如下:

代码语言:javascript
复制
Project Lombok is a java library that automatically plugs into 
your editor and build tools, spicing up your java.
Never write another getter or equals method again, 
with one annotation your class has a fully featured builder, 
Automate your logging variables, and much more.

根据Lombok官网的描述可以看出:

代码语言:javascript
复制
Project Lombok是一个java库,其可以自动插入到你的编辑器和构建工具中,
使java代码更加生动。
使用Lombok提供的注解将会带来诸多改变,如:
 -- 不需要再写getter、equals等方法
 -- 一个注解将为类提供功能齐全的Builder,后续我们将会演示@Builder注解
 -- 自动插入日志变量等等

看着还挺吸引人吧,那接下来就来安装一下,然后来小试一下其提供的神奇注解。

2、Lombok安装

访问Lombok官网(https://projectlombok.org/download)下载Lombok.jar。使用java -jar lombok.jar运行, 选择指定IDE路径,本文以Eclipse为示例,其它IDE的安装类似。选择Eclipse.exe。

点击Install / update按钮即可完成安装。

3、Lombok注解使用

引入Lombok依賴包:

代码语言:javascript
复制
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
    <scope>provided</scope>
</dependency>

Lombok提供的注解可以在lombok的package找到,如下图所示:

本文将给出如下注解的使用示例,包括:

  1. @Getter @Setter
  2. @NoArgsConstructor AllArgsConstructor
  3. @EqualsAndHashCode
  4. @ToString
  5. @Data
  6. @NonNull
  7. @Builder
  8. @Singular
  9. @CleanUp
  10. @Synchronized
  11. @Slf4j

3.1 @Getter / @Setter

代码语言:javascript
复制
package com.wangmengjun.tutorial.lombok;

import java.io.Serializable;
import java.util.List;

import lombok.Getter;
import lombok.Setter;

@Getter 
@Setter
public class Book implements Serializable  {

  private static final long serialVersionUID = -8212889116567064704L;
  
  /**书名*/
  private String name;
  
  /**ISBN*/
  private  String isbn;
  
  /**作者*/
  private List<String> authors;
  
}

代码自动为各个属性增加了getter、setter方法,如下图所示:

利用反编译工具查看,其效果等价于如下代码:

代码语言:javascript
复制
package com.wangmengjun.tutorial.lombok;

import java.io.Serializable;
import java.util.List;

public class Book
  implements Serializable
{

  private static final long serialVersionUID = 0x8e05f4dcbb592f80L;
  private String name;
  private String isbn;
  private List authors;

  public Book()
{
  }

  public String getName()
{
    return name;
  }

  public String getIsbn()
{
    return isbn;
  }

  public List getAuthors()
{
    return authors;
  }

  public void setName(String name)
{
    this.name = name;
  }

  public void setIsbn(String isbn)
{
    this.isbn = isbn;
  }

  public void setAuthors(List authors)
{
    this.authors = authors;
  }
}

3.2 @NoArgsConstructor @AllsConstructor

从注解的名字上就可以看出,其作用是产生无参以及包含全部参数的构造函数,如下图高亮部分所示:

如果,Book类需要增加一个出版社press的属性,那么,全部参数的构造函数需要重新添加一个属性。在这个时候,使用@AllArgsConstructor注解的BookLombok 类将不用再修改任何代码。

3.3 @EqualsAndHashCode

该注解将为类产生equals,hasCode,其中还产生了一个用于比较的canEquals方法,如下图高亮部分所示:

反编译后增加的代码如所示:

代码语言:javascript
复制

  public boolean equals(Object o)
  {
    if (o == this)
      return true;
    if (!(o instanceof Book))
      return false;
    Book other = (Book)o;
    if (!other.canEqual(this))
      return false;
    Object this$name = getName();
    Object other$name = other.getName();
    if (this$name != null ? !this$name.equals(other$name) : other$name != null)
      return false;
    Object this$isbn = getIsbn();
    Object other$isbn = other.getIsbn();
    if (this$isbn != null ? !this$isbn.equals(other$isbn) : other$isbn != null)
      return false;
    Object this$authors = getAuthors();
    Object other$authors = other.getAuthors();
    return this$authors != null ? this$authors.equals(other$authors) : other$authors == null;
  }

  protected boolean canEqual(Object other)
  {
    return other instanceof Book;
  }

  public int hashCode()
  {
    int PRIME = 59;
    int result = 1;
    Object $name = getName();
    result = result * 59 + ($name != null ? $name.hashCode() : 43);
    Object $isbn = getIsbn();
    result = result * 59 + ($isbn != null ? $isbn.hashCode() : 43);
    Object $authors = getAuthors();
    result = result * 59 + ($authors != null ? $authors.hashCode() : 43);
    return result;
  }

3.4 @ToString

该注解将为类产生toString方法,如下图高亮部分所示:

代码语言:javascript
复制
  public String toString()
  {
    return (new StringBuilder("Book(name=")).append(getName()).append(", isbn=").append(getIsbn()).append(", authors=").append(getAuthors()).append(")").toString();
  }

3.5 @Data

使用@Data注解,其效果等价于:

代码语言:javascript
复制
All together now: A shortcut for
@ToString, @EqualsAndHashCode, 
@Getter on all fields, and @Setter on all non-final fields, 
and @RequiredArgsConstructor!
  • 添加@ToString, @EqualsAndHashCode 和 @RequiredArgsConstructor
  • 所有变量增加@Getter
  • 所有非final类型的变量增加@Setter

只有一个@Data注解的类如下:

代码语言:javascript
复制
package com.wangmengjun.tutorial.lombok;

import java.io.Serializable;
import java.util.List;

import lombok.Data;

@Data 
public class Book implements Serializable  {

  private static final long serialVersionUID = -8212889116567064704L;
  
  /**书名*/
  private String name;
  
  /**ISBN*/
  private  String isbn;
  
  /**作者*/
  private List<String> authors;
  
}

反编译查看一下,

代码语言:javascript
复制
package com.wangmengjun.tutorial.lombok;

import java.io.Serializable;
import java.util.List;

public class Book
  implements Serializable
{

  private static final long serialVersionUID = 0x8e05f4dcbb592f80L;
  private String name;
  private String isbn;
  private List authors;

  public String getName()
{
    return name;
  }

  public String getIsbn()
{
    return isbn;
  }

  public List getAuthors()
{
    return authors;
  }

  public void setName(String name)
{
    this.name = name;
  }

  public void setIsbn(String isbn)
{
    this.isbn = isbn;
  }

  public void setAuthors(List authors)
{
    this.authors = authors;
  }

  public boolean equals(Object o)
{
    if (o == this)
      return true;
    if (!(o instanceof Book))
      return false;
    Book other = (Book)o;
    if (!other.canEqual(this))
      return false;
    Object this$name = getName();
    Object other$name = other.getName();
    if (this$name != null ? !this$name.equals(other$name) : other$name != null)
      return false;
    Object this$isbn = getIsbn();
    Object other$isbn = other.getIsbn();
    if (this$isbn != null ? !this$isbn.equals(other$isbn) : other$isbn != null)
      return false;
    Object this$authors = getAuthors();
    Object other$authors = other.getAuthors();
    return this$authors != null ? this$authors.equals(other$authors) : other$authors == null;
  }

  protected boolean canEqual(Object other)
{
    return other instanceof Book;
  }

  public int hashCode()
{
    int PRIME = 59;
    int result = 1;
    Object $name = getName();
    result = result * 59 + ($name != null ? $name.hashCode() : 43);
    Object $isbn = getIsbn();
    result = result * 59 + ($isbn != null ? $isbn.hashCode() : 43);
    Object $authors = getAuthors();
    result = result * 59 + ($authors != null ? $authors.hashCode() : 43);
    return result;
  }

  public String toString()
{
    return (new StringBuilder("Book(name=")).append(getName()).append(", isbn=").append(getIsbn()).append(", authors=").append(getAuthors()).append(")").toString();
  }

  public Book()
{
  }
}

可以看到所有属性的Getter、Setter、默认构造函数、toString方法、equas和hasCode方法都具备了。

3.6 @NonNull

此注解用于标注属性不能为null,如果setter方法传值为null,则抛出NullpointerException。

代码语言:javascript
复制
package com.wangmengjun.tutorial.lombok;

import java.io.Serializable;
import java.util.List;

import lombok.Data;
import lombok.NonNull;

@Data 
public class Book implements Serializable  {

  private static final long serialVersionUID = -8212889116567064704L;
  
  /**书名*/
  private @NonNull String name;
  
  /**ISBN*/
  private  String isbn;
  
  /**作者*/
  private List<String> authors;
  
}

上述示例对name增加了@NonNull, 反编译后,

代码语言:javascript
复制
  public void setName(String name)
  {
    if (name == null)
    {
      throw new NullPointerException("name is marked non-null but is null");
    } else
    {
      this.name = name;
      return;
    }
  }

3.7 @Builder

此注解将产生Builder类,可以采用构建者模式完成赋值,如:

代码语言:javascript
复制
@Getter
@Setter
@Builder
public class Book implements Serializable  {

  private static final long serialVersionUID = -8212889116567064704L;
  
  /**书名*/
  private String name;
  
  /**ISBN*/
  private  String isbn;
  
  /**作者*/
  private List<String> authors;
  
}

可以看到使用@Builder之后,自动生成了BookBuilder类和build方法, 如下图高亮部分所示:

反编译后,其效果如下:

代码语言:javascript
复制
package com.wangmengjun.tutorial.lombok;

import java.io.Serializable;
import java.util.List;

public class Book
  implements Serializable
{
  public static class BookBuilder
{

    private String name;
    private String isbn;
    private List authors;

    public BookBuilder name(String name)
{
      this.name = name;
      return this;
    }

    public BookBuilder isbn(String isbn)
{
      this.isbn = isbn;
      return this;
    }

    public BookBuilder authors(List authors)
{
      this.authors = authors;
      return this;
    }

    public Book build()
{
      return new Book(name, isbn, authors);
    }

    public String toString()
{
      return (new StringBuilder("Book.BookBuilder(name=")).append(name).append(", isbn=").append(isbn).append(", authors=").append(authors).append(")").toString();
    }

    BookBuilder()
    {
    }
  }


  private static final long serialVersionUID = 0x8e05f4dcbb592f80L;
  private String name;
  private String isbn;
  private List authors;

  Book(String name, String isbn, List authors)
  {
    this.name = name;
    this.isbn = isbn;
    this.authors = authors;
  }

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

  public String getName()
{
    return name;
  }

  public String getIsbn()
{
    return isbn;
  }

  public List getAuthors()
{
    return authors;
  }

  public void setName(String name)
{
    this.name = name;
  }

  public void setIsbn(String isbn)
{
    this.isbn = isbn;
  }

  public void setAuthors(List authors)
{
    this.authors = authors;
  }
}

Buider创建对象示例如下:

代码语言:javascript
复制

    Book book = Book.builder().name("Eric").isbn("1234567890")
                              .authors(Arrays.asList("Eric","Joan")).build();
   

3.8 @Singular

此注解与@Builder一起使用,增加单个赋值的方法,如对作者Authors变量设置此变量:

代码语言:javascript
复制
@Getter
@Setter
@Builder
public class Book implements Serializable  {

  private static final long serialVersionUID = -8212889116567064704L;
  
  /**书名*/
  private String name;
  
  /**ISBN*/
  private  String isbn;
  
  /**作者*/
  private @Singular List<String> authors;
  
}

Builder支持单个author的赋值,增加一个方法

代码语言:javascript
复制
   public BookBuilder author(String author)
    {
      if (authors == null)
        authors = new ArrayList();
      authors.add(author);
      return this;
    }

这样时候,Authors列表的赋值,支持一个一个地增加,如:

代码语言:javascript
复制
Book book = Book.builder().name("Eric").isbn("1234567890asdfghjkl")
                          .author("Eirc")
                          .author("Joan")
                          .build();

3.9 @CleanUp

代码语言:javascript
复制
使用@CleanUp可以完成自动资源管理,如添加close()方法关闭资源。
代码语言:javascript
复制
import lombok.Cleanup;
import java.io.*;

public class CleanupExample {
  public static void main(String[] args) throws IOException {
    @Cleanup InputStream in = new FileInputStream(args[0]);
    @Cleanup OutputStream out = new FileOutputStream(args[1]);
    byte[] b = new byte[10000];
    while (true) {
      int r = in.read(b);
      if (r == -1) break;
      out.write(b, 0, r);
    }
  }
}

等价于

代码语言:javascript
复制
import java.io.*;

public class CleanupExample {
  public static void main(String[] args) throws IOException {
    InputStream in = new FileInputStream(args[0]);
    try {
      OutputStream out = new FileOutputStream(args[1]);
      try {
        byte[] b = new byte[10000];
        while (true) {
          int r = in.read(b);
          if (r == -1) break;
          out.write(b, 0, r);
        }
      } finally {
        if (out != null) {
          out.close();
        }
      }
    } finally {
      if (in != null) {
        in.close();
      }
    }
  }
}

3.10 @Synchronized

代码语言:javascript
复制
 For non-static methods, a field named {@code $lock} is used, 
 and for static methods, {@code $LOCK} is used. 

从@Synchronized注解描述可以看出,其对非静态方法会使用变量$lock,而对静态方法使用的是$LOCK。如:

代码语言:javascript
复制
public class SynchronizedExample {

  private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");
   
  @Synchronized
  public String synchronizedFormat(Date date) {
      return format.format(date);
  }
  
  @Synchronized
  public static  String synchronizedStaticMethod() {
    
    String message = "hello synchronized";
      return  message;
  }
}

从下图右侧的高亮部分可以看到包含了对象$lock 和 $LOCK。

反编译查看代码,

代码语言:javascript
复制
package com.wangmengjun.tutorial.lombok;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SynchronizedExample
{

  private final Object $lock = new Object[0];
  private static final Object $LOCK = new Object[0];
  private DateFormat format;

  public SynchronizedExample()
  {
    format = new SimpleDateFormat("MM-dd-YYYY");
  }

  public String synchronizedFormat(Date date)
  {
    Object obj = $lock;
    JVM INSTR monitorenter ;
    return format.format(date);
    obj;
    JVM INSTR monitorexit ;
    throw ;
  }

  public static String synchronizedStaticMethod()
  {
    Object obj = $LOCK;
    JVM INSTR monitorenter ;
    String message = "hello synchronized";
    return message;
    obj;
    JVM INSTR monitorexit ;
    throw ;
  }

}

上述的moniterenter和moniterexit其实就是synchronized的实现,所以代码也就等价于:

代码语言:javascript
复制
package com.wangmengjun.tutorial.lombok;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SynchronizedExample
{

  private final Object $lock = new Object[0];
  private static final Object $LOCK = new Object[0];
  private DateFormat format;

  public SynchronizedExample()
  {
    format = new SimpleDateFormat("MM-dd-YYYY");
  }

  public String synchronizedFormat(Date date)
  {
     synchronized($lock) {
        return format.format(date);
     }
  }

  public static String synchronizedStaticMethod()
  {
    synchronized($LOCK) {
      String message = "hello synchronized";
      return message;
    }
  }

}

3.11@Slf4j

使用@Slf4j,其会在代码增加一个log对象,我们直接可以使用log进行日志记录。

代码语言:javascript
复制
package com.wangmengjun.tutorial.lombok;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class LogExample {

    public void greeting(String name) {
        System.out.println("Hello," +name);
        log.info("Method greeting, name is {}", name);
    }
    
    public static void main(String[] args) {
      LogExample example = new LogExample();
      example.greeting("mengjun");
      
  }

}

其等价于:

代码语言:javascript
复制
package com.wangmengjun.tutorial.lombok;

import java.io.PrintStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogExample
{

  private static final Logger log = LoggerFactory.getLogger(com/wangmengjun/tutorial/lombok/LogExample);

  public LogExample()
{
  }

  public void greeting(String name)
{
    System.out.println((new StringBuilder("Hello,")).append(name).toString());
    log.info("Method greeting, name is {}", name);
  }

  public static void main(String args[])
{
    LogExample example = new LogExample();
    example.greeting("mengjun");
  }

}

运行LogEample类,可以直接可以使用log日志记录,可以说非常方便。

代码语言:javascript
复制
Hello,mengjun
10:49:33.765 [main] INFO com.wangmengjun.tutorial.lombok.LogExample - Method greeting, name is mengjun

注意,使用相关的日志注解,需要导入相关的日志,本示例中采用的SLF4j,需要引入如下依赖包:

代码语言:javascript
复制
 <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
 
        <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

当然,Lombok支持其它日志的记录,如Apache Common, JBOSS、Log4j等,只要使用对应的注解即可。

@CommonsLog

创建

代码语言:javascript
复制
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);

@JBossLog

创建

代码语言:javascript
复制
private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);

@Log

创建

代码语言:javascript
复制
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());

@Log4j

创建

代码语言:javascript
复制
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);

@Log4j2

创建

代码语言:javascript
复制
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class

Lombok还提供了很多其它的注解,在本篇文章中就不一一列举出来了。

那么问题来了,既然Lombok有这么好用的功能,其实现的原理又是什么呢?

4、Lombok原理分析

4.1 Java源码编译

在弄清Lombok是如何工作的之前,我们先来看一下OpenJDK上对Java源码编译过程的一个说明:

http://openjdk.java.net/groups/compiler/doc/compilation-overview/index.html

Java 源码编译一般可以分成三个不同的阶段:

  • 解析和输入
  • 注解处理
  • 语义分析和生成class文件
代码语言:javascript
复制
在解析和输入阶段,编译器会解析源文件到一个抽象语法树( Abstract Syntax Tree, 简称AST)。如果语法不合法将会抛出错误。

在注解处理阶段,自定义的注解处理器将会被调用,这可以被认为是预编译阶段。注解处理器可以做一些譬如验证类正确性、产生新的资源包含源文件等操作。

如果新的源码文件是注解处理的结果,那么编译循环回到解析和输入阶段,重复这个过程,直到没有新的源文件生产为止。

在最后一个阶段,即对抽象语法树(AST) 进行语义分析,编译器根据产生的抽象语法树生成class文件(字节码文件)。

大致了解了Java源码编译的过程之后,我们再来看一下Lombok是如何做的?

4.2 Lombok基本原理

Lombok的魔法就在于其修改了AST,分析和生成class阶段使用了修改后的AST,也就最终改变了生成的字节码文件。如,添加一个方法节点 ( Method Node )到AST,那么产生的class文件时将会包含新的方法。

通过修改AST,Lombok可以产生新的方法(如getter、setter等),或者注入代码到已存在的方法中去,比如 ( Lombok 提供的@Cleanup注解 -- 这个可以本文示例中找到 )。

代码语言:javascript
复制
Project Lombok使用了JSR 269 Pluggable Annotation Processing API ,
lombok.jar 包含一个名字为 
/META-INF/services/javax.annotation.processing.Processor的文件。
当 javac 看到编译路径上的这个文件时,
会在编译期间使用定义在这个文件中的注解处理器。

https://www.jcp.org/en/jsr/detail?id=269

定义的注解处理器主要有两个AnnotationProcessor以及ClaimingProcessor。

AnnotationProcessor以及ClaimingProcessor在Lombok中的源代码如下:

代码语言:javascript
复制
package lombok.launch;

import java.lang.reflect.Field;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Completion;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;

import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor;

import sun.misc.Unsafe;

class AnnotationProcessorHider {

    //省略其它
    public static class AnnotationProcessor extends AbstractProcessor {
    private final AbstractProcessor instance = createWrappedInstance();
    
    @Override public Set<String> getSupportedOptions() {
      return instance.getSupportedOptions();
    }
    
    @Override public Set<String> getSupportedAnnotationTypes() {
      return instance.getSupportedAnnotationTypes();
    }
    
    @Override public SourceVersion getSupportedSourceVersion() {
      return instance.getSupportedSourceVersion();
    }
    
    @Override public void init(ProcessingEnvironment processingEnv) {
      disableJava9SillyWarning();
      AstModificationNotifierData.lombokInvoked = true;
      instance.init(processingEnv);
      super.init(processingEnv);
    }
    
    // sunapi suppresses javac's warning about using Unsafe; 'all' suppresses eclipse's warning about the unspecified 'sunapi' key. Leave them both.
    // Yes, javac's definition of the word 'all' is quite contrary to what the dictionary says it means. 'all' does NOT include 'sunapi' according to javac.
    @SuppressWarnings({"sunapi", "all"})
    private void disableJava9SillyWarning() {
      // JVM9 complains about using reflection to access packages from a module that aren't exported. This makes no sense; the whole point of reflection
      // is to get past such issues. The only comment from the jigsaw team lead on this was some unspecified mumbling about security which makes no sense,
      // as the SecurityManager is invoked to check such things. Therefore this warning is a bug, so we shall patch java to fix it.
      
      try {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        Unsafe u = (Unsafe) theUnsafe.get(null);
        
        Class<?> cls = Class.forName("jdk.internal.module.IllegalAccessLogger");
        Field logger = cls.getDeclaredField("logger");
        u.putObjectVolatile(cls, u.staticFieldOffset(logger), null);
      } catch (Throwable t) {
        // We shall ignore it; the effect of this code failing is that the user gets to see a warning they remove with various --add-opens magic.
      }
    }
    
    @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
      return instance.process(annotations, roundEnv);
    }
    
    @Override public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
      return instance.getCompletions(element, annotation, member, userText);
    }
    
    private static AbstractProcessor createWrappedInstance() {
      ClassLoader cl = Main.getShadowClassLoader();
      try {
        Class<?> mc = cl.loadClass("lombok.core.AnnotationProcessor");
        return (AbstractProcessor) mc.getDeclaredConstructor().newInstance();
      } catch (Throwable t) {
        if (t instanceof Error) throw (Error) t;
        if (t instanceof RuntimeException) throw (RuntimeException) t;
        throw new RuntimeException(t);
      }
    }
  }
  
  @SupportedAnnotationTypes("lombok.*")
  public static class ClaimingProcessor extends AbstractProcessor {
    @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
      return true;
    }
    
    @Override public SourceVersion getSupportedSourceVersion() {
      return SourceVersion.latest();
    }
  }
}

Project Lombok充当了一个注解处理器的角色。注解处理器扮演了一个分发器,其委托Lombok注解处理器来处理,Lombok注解处理器声明了具体要处理的注解。当委托给一个处理器时,Lombok注解处理器会通过注入新的节点(如,方法、表达式等)的方式去修改抽象语法树 (AST)。在注解处理阶段之后,编译器会根据修改后的AST,生成字节码。

Lombok在源码编译中,大致处理的过程如下图所示:

图片来源:http://notatube.blogspot.jp/2010/12/project-lombok-creating-custom.html

5、小结

本文先对Lombok进行了介绍,大致理解其是做什么用的。然后给出安装步骤,结合示例对多个注解进行简单说明,相信经过这些例子的说明,读者已经有了一个更好的体感,建议亲手操作一下,效果更佳;最后,对Lombok的原理进行了简单分析,可以更好地理解Lombok。

任何事物都具有两面性。尽管,Lombok能带给我们诸多方便,可以减少诸如Getter、 Setter代码,其中@Builder可以采用构建者模式的方式对变量进行赋值,直观方便;@CleanUp可以防止程序忘关闭流等等。但是,在某些场景,其也存在使用的限制,也存在一些缺点,请访问如下链接查看https://code.google.com/p/projectlombok/issues/list。

Lombok也可以在诸多互联网公司的开源代码中看到,大家可以根据自身的需要进行选择。

另外,友情提示一下:如果你的项目组是需要统计代码量,建议还是不要用了 你懂得

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 孟君的编程札记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档