LomBok

266 阅读6分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

LomBok

Lombok 是一个工具, 用来减少 Java 的冗长代码, 尤其是对于简单的 Java 实体类. 既是一个 IDE 插件, 也是一个项目要依赖的 jar 包.

  • 是 jar 包 :  编译时要用 LomBok 的注解来实现消除冗长的代码.
  • 是插件 :  LomBok 在编译器编译时, 通过操作 AST ( 抽象语法树 ) 改变字节码生成. 也就是说他可以改变 java 语法. 他不像 Spring 的依赖注入或者 hibernate 的 orm 一样是运行时的特性, 而是编译时的特性.

Lombok 依靠可插件化的 Java 自定义注解处理 API 来实现在 Javac编译阶段利用 Annotation Processor 对自定义的注解进行预处理后生成真正在 JVM 上面执行的 Class 文件.

其大致执行原理图如下:

安装使用

Eclipse 安装

使用 Lombok 需要安装这个插件, 如果不安装, IDE 则无法解析 Lombok 的注解.

  • 先在官网下载最新版本的 JAR 包. projectlombok.org/download
  • 将 lombok.jar 复制到 myeclipse.ini / eclipse.ini 所在的文件夹内.
  • 打开 eclipse.ini / myeclipse.ini 文件, 在最后面添加以下两行并保存.
-Xbootclasspath/a:lombok.jar
-javaagent:lombok.jar
  • 重启 eclipse / myeclipse.

IDEA 安装

搜索插件 Lombok, 安装. 重启即可.

引入依赖

需要注意的是, 在使用 lombok 注解的时候记得要导入 lombok 依赖到工程.

<!-- lombok -->
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.16.20</version>
  <scope>provided</scope>
</dependency>

常用注解

实体类相关

  • @Setter : 用在类/属性上, 为属性提供 setter() 方法.
  • @Getter: 用在类/属性上, 为属性提供 getter() 方法.

注意 : 基本类型的 boolean, 生成的前缀是 is, 包装类型的 Boolean, 生成的前缀是 get.

  • @Data    使用在类上, 提供 getter() , setter() , equals() , canEqual() , hashCode() , toString() 方法.

@Data 注解提供的 toString() 方法在打印输出时, 如果该类继承自父类, 默认不会输出父类的属性值,
可以通过添加 @ToString(callSuper = true) 注解来实现输出父类中的属性值

  • @NoArgsConstructor    使用在类上, 为类提供一个无参的构造方法.
  • @AllArgsConstructor     使用在类上, 为类提供一个全参的构造方法.

坑:实体类中没有任何属性的话,@NoArgsConstructor 和 @AllArgsConstructor 只能用一个,否则编写时不报错,运行时报错构造器重复.

  • @RequiredArgsConstructor    使用在类上, 为类中所有被 @NonNull 注解的或者被 final 修饰的成员变量生成对应的构造方法.
  • @ToString()    使用在类上, 提供 toString() 方法.
  • @ToString(callSuper = true) 使用 lombok 后, 子类不输出父类的属性, 需要添加该注解
  • @EqualsAndHashCode 使用在类上, 提供 equals() , hashCode() 方法.

日志相关

LOG 相关的注解使用在类上. 用来生成各种 log 对象.  默认名称是使用该注解的类的类名. 也可以通过 topic 参数来指定. Log 相关的注解, 推荐使用 @Slf4j

如果这里使用 @SLF4J 注解,发现 log 对象没有 info 等方法,不能使用时,可能是以下原因:

  • 只导入 Lombok 只是导入了日志的门面,还需要导入日志的实现
  • SpringBoot 的项目依赖冲突,可能是由于日志实现在 springboot 的 starter 中已经有了,但是又导入了 logback-classic,排除冲突
  • 可能是 idea 没有安装 lombook 插件导致的.

判空

注解@NonNull使用在属性上时, 用于属性的非空检查, 默认会生成一个无参构造方法. 当放在 @Setter 注解的字段上, 或者在方法或构造函数的参数上使用, 将生成一个空检查, 如果为空, 则抛出 NullPointerException.

比如,如下代码:

public class User implements Serializable {
    @NonNull
    @Setter
    @Getter
    private Integer id,
    
    private String userName,
}

实际生成代码如下:

public class User implements Serializable {
    @NonNull
    private Integer id,
    private String userName,

    public User() {    }

    public void setId(@NonNull Integer id) {
        if (id == null) {
            throw new NullPointerException("id"),
        } else {
            this.id = id,
        }
    }

    @NonNull
    public Integer getId() {
        return this.id,
    }
}

不可变对象

@Value注解在类上, 可以生成不可变对象, 全参的构造方法, getter 方法, equals(), hashCode(), toString() 方法, 但没有 setter.

类会被 final 修饰, 属性也会被 final 修饰.

比如如下代码:

@Value
public class User implements Serializable {
    private Integer id,
    private String userName,
}

最后生成的代码:

public final class User implements Serializable {
    private final Integer id,
    private final String userName,
}

builder 模式 链式调用

@Builder 注解用在类上, 可以实现属性赋值时的链式调用.

比如如下代码:

@Builder(toBuilder = true)
public class User implements Serializable {
    private Integer id,
    private String userName,
}

最后生成的代码:

public void testUser(){
    User user = User.builder().id(101).userName("aaa").build(),
    System.out.println(user),
}

LomBok 的坑

SetGet方法的坑

有个实体类如下:

@Data
public class NMetaVerify{
    private Long id;
    private NMetaType nMetaType;
    ...
}

在一次使用 Mybatis 插入数据时, 其他属性都能正常的插入,但是就是nMetaType属性在数据库一直是 null.

① 原因如下:

Lombok 对于第一个字母小写,第二个字母大写的属性生成的 get-set 方法和 Mybatis 以及 idea 以及Java官方认可的get-set方法的结果不一样.

Lombok 生成的 Get-Set 方法 :

@Data
public class NMetaVerify {
    private Long id;
    private NMetaType nMetaType;
    private Date createTime;
    
    public void test(){
        NMetaVerify nMetaVerify = new NMetaVerify();
        nMetaVerify.setNMetaType(null); //注意:nMetaType的set方法为setNMetaType,第一个n字母大写了,
        nMetaVerify.getNMetaType();     //getxxxx方法也是大写
    }
}

idea,Mybatis,Java官方默认的是:

public class NMetaVerify {
    private Long id;
    private NMetaType nMetaType;

    public NMetaType getnMetaType() {//注意:nMetaType属性的第一个字母小写
        return nMetaType;
    }

    public void setnMetaType(NMetaType nMetaType) {//注意:nMetaType属性的第一个字母小写
        this.nMetaType = nMetaType;
    }
}

② 解决方案

  • 修改属性名字,让第二个字母小写,或者说是规定所有的属性的前两个字母必须小写.
  • 如果数据库已经设计好,并且前后端接口对接好了,不想修改,那就专门为这种特殊的属性使用 idea 生成 get-set 方法.

③ 教训

  • 不要有奇怪的属性名. 建议在编写属性名时,当第一个字母小写时, 第二个字母不要大写.

与 EasyExcel 的坑

① 遇到的问题

在使用 easyexcel 导出的时候,发现实体类上都增加了@Accessor(chain = true)注解后,导出功能不正常了。

② 原因

easyexcel 底层使用的是 cglib 来做反射工具包的. cglib 在获取 setXXX 方法时,只获取了返回值是 void 类型的 setxXxx 方法,  因为使用了链式调用, 所以该实体类的 setXXX 方法的返回值不是 void,所以所有的值都无法设置上.

③ 解决方案

  • 去掉 @Accessor 注解,不使用链式调用.
  • 等待 easyexcel 的作者替换掉底层的 cglib 或者修复这个问题吧.

未知错误

没有属性的实体类,SpringBoot 项目中作为返回结果,运行报错如下:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class xxx.entity.DetailReturnVO and no properties discovered to create BeanSerializer

不知道什么原因,要么去掉 lombok 注解,要么就添加任意属性就可以解决.