本文已参与「新人创作礼」活动,一起开启掘金创作之路。
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 注解,要么就添加任意属性就可以解决.