对于这样一个简单的 Java 类,我们通常需要给每个属性写 `getXXX()` 和 `setXXX()` 方法,有时候还要重写 `equals()`、 `hashCode()`、 `toString()`等方法。在开发的过程中,重复编写这些“没有技术含量”的代码,对程序员来说是一个很枯燥无趣的事情,也增加了很大的工作负担。 针对这种情况,Lombok 提供了一种机制,基于注解的方式,帮助我们自动生成这些样板代码

什么是Lombok

一个典型的Java类

public class A {
    private int num;

    private String str;

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof A)) return false;
        A a = (A) o;
        if (getNum() != a.getNum()) return false;
        return getStr() != null ? getStr().equals(a.getStr()) : a.getStr() == null;

    }

    @Override
    public int hashCode() {
        int result = getNum();
        result = 31 * result + (getStr() != null ? getStr().hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "A{" + "num=" + num + ", str='" + str + '\'' + '}';
    }
}

对于这样一个简单的 Java 类,我们通常需要给每个属性写 getXXX()setXXX() 方法,有时候还要重写 equals()hashCode()
toString()等方法。在开发的过程中,重复编写这些“没有技术含量”的代码,对程序员来说是一个很枯燥无趣的事情,也增加了很大的工作负担。

针对这种情况,Lombok 提供了一种机制,基于注解的方式,帮助我们自动生成这些样板代码。如:

@Data
public class A {
    private int a;
    private String b;
}

@Data 就是 Lombok 的一个注解,它所做的工作包括生成了所有属性的 getXXX()setXXX() 方法,重写了equals()hashCode()
toString() 方法。

lombok.jar 拷贝到 A.java 文件所在目录进行编译:

javac -cp lombok.jar A.java && javap -public A.class

public class com.tuniu.calliper.component.view.controller.A {
   public com.tuniu.calliper.component.view.controller.A();
   public int getNum();
   public java.lang.String getStr();
   public void setNum(int);
   public void setStr(java.lang.String);
   public boolean equals(java.lang.Object);
   public int hashCode();
   public java.lang.String toString();
 }

可以看到生成的方法。反观未加入 @Data 注解的 A.java, 编译后为:

public class com.tuniu.calliper.component.view.controller.A {
   public com.tuniu.calliper.component.view.controller.A();
 }

使用

具体的安装和引入可参考 Lombok官网。需要说明的是,如果 IDE 不安装插件,同样可以通过编译,但是,在IDE中会提示报错,
并在编写代码时,不会出现方法提示,影响使用。

Lombok的运行机制

Lombok主要基于Java中的注解生成代码,以@Setter方法为例(具体源码可查看 Lombok项目源码),其注解定义如下:

@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Setter {
	lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;

	AnyAnnotation[] onMethod() default {};

	AnyAnnotation[] onParam() default {};

	@Deprecated
	@Retention(RetentionPolicy.SOURCE)
	@Target({})
	@interface AnyAnnotation {}
}

可以发现,@Setter 是源码级别的,即在编译期有效。我们知道,在 Java 中,Java 编译主要分为三大部分:

  1. 分析和输入到符号表
  2. 注解处理(可选)
  3. 语义分析和生成 class 文件

Lombok 的注解都是在编译期的第二个阶段,也就是注解处理阶段去解析注解,并生成对应的源码,然后重新编译修改后的源文件,最后在class文件中包含对应的方法。
需要注意的是,在 javac 编译器中,注解处理 机制是默认打开的,而对于 eclipse 这种自带编译器( ECJ )的 IDE 是默认不打开的,这也就是为什么 IDE 要安装插件
的原因。

Lombok注解

Lombok 目前最新版本为1.16.8,主要提供16种注解,个人觉得比较常用的有以下几种,更多可查看 Lombok官网

  • @Getter / @Setter

    作用于属性和类上,自动生成属性的 getXXX()setXXX() 方法。若在类上,则对所有属性有效。并可通过AccessLevel参数控制方法的访问级别。
  • @ToString

    作用于类,自动重写类的 ToString() 方法。常用的参数有 exclude (指定方法中不包含的属性)、callSuper (方法中是否包含父类 ToString() 方法返回的值)
  • @EqualsAndHashCode

    作用于类,自动重写类的 equals()hashCode()方法。常用的参数有 exclude (指定方法中不包含的属性)、callSuper (方法中是否包含父类 ToString() 方法返回的值)
  • @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor

    作用于类,@NoArgsConstructor 自动生成不带参数的构造方法;@RequiredArgsConstructor 自动生成带参数的构造方法,主要针对一些需要特殊处理的属性,比如未初始化的 final 属性;
    @AllArgsConstructor 自动生成包含所有属性的构造方法。
  • @Data

    @ToString@EqualsAndHashCode@Getter@Setter@RequiredArgsConstructor 注解的集合。
  • @Synchronized

    作用于方法,可锁定指定的对象,如果不指定,则默认创建创建一个对象锁定。
  • @Log

    作用于类,具体包含 @CommonsLog@Log@Log4j@Log4j2@Slf4j@XSlf4j,分别对用不同的日志系统。利用此类注解,可为类创建一个 log 属性。