文章目录
简介
在写实体类时,通常要为实体类写getter、setter、toString方法。在使用map将对象作为key时,还要重写对象的equals和hashcode方法。
这些代码格式类似,借助IDEA工具我们还是可以比较方便的生成。不过当对象变更时,如增删字段、更改字段类型,所有的方法仍需要重新生成一遍,容易遗漏和忽略。
而使用lombok,就可以将这些工作放在源码编译阶段,由lombok在编译时帮我们自动生成class,开发只需要定义实体类的属性即可。
安装&配置
引入lombok依赖用于编译时生成class,IDEA安装lombok插件用于代码提示和检查。
依赖
在项目的根pom下引入lombok依赖。使用springboot框架不用指定版本。optional=true只在本项目生效,不要强迫其他项目引入我们sdk的小伙伴~
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
插件
IDEA默认已安装,启用插件即可。主要提供lombok的注解检查和代码提示,比如可以在源码中未写getter时使用pojo.getXxx()。
使用
熟悉lombok常用注解及食用姿势~不过约定大于配置,简单就是最好的!常用食用姿势有哪些呢?
- 使用@Data为实体类一键生成getter、setter、toString、equals、hashcode
- 使用@NoArgsConstructor、@AllArgsConstructor为实体类添加默认的无参构造器、全参构造器
- 使用@SuperBuilder、@Builder为实体类添加构造器模式,方便链式构建复杂对象
- 使用@Slf4j为类添加一个静态常量slf4j的日志logger
- 使用@SneakyThrows为方法添加try-catch处理异常
- 全局配置lombok.config
其他的特性就不太需要了,容易消化不良…
@Data
使用继承时,子类的toString、equals、hashcode方法默认不会包含父类属性。
需要使用全局配置callSuper=true或使用@ToString、@EqualsAndHashCode指定callSuper=true
默认生成无参构造器、getter/setter、equals、hashCode、toString。
适应场景:各种实体类。居家旅行必备无脑使用。
import lombok.Data;
@Data
public class Demo {
Long id;
Instant createTime;
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
public class Demo {
Long id;
Instant createTime;
public Demo() {
}
public Long getId() {
return this.id;
}
public Instant getCreateTime() {
return this.createTime;
}
public void setId(final Long id) {
this.id = id;
}
public void setCreateTime(final Instant createTime) {
this.createTime = createTime;
}
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Demo)) {
return false;
} else {
Demo other = (Demo)o;
if (!other.canEqual(this)) {
return false;
} else {
Object this$id = this.getId();
Object other$id = other.getId();
if (this$id == null) {
if (other$id != null) {
return false;
}
} else if (!this$id.equals(other$id)) {
return false;
}
Object this$createTime = this.getCreateTime();
Object other$createTime = other.getCreateTime();
if (this$createTime == null) {
if (other$createTime != null) {
return false;
}
} else if (!this$createTime.equals(other$createTime)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(final Object other) {
return other instanceof Demo;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $id = this.getId();
result = result * 59 + ($id == null ? 43 : $id.hashCode());
Object $createTime = this.getCreateTime();
result = result * 59 + ($createTime == null ? 43 : $createTime.hashCode());
return result;
}
public String toString() {
return "Demo(id=" + this.getId() + ", createTime=" + this.getCreateTime() + ")";
}
}
@NoArgsConstructor、@AllArgsConstructor
约定大于配置:1. 无自定义构造器时,默认生成无参构造器 2. 有自定义构造器时,不会默认生成无参构造器
对于2情况,在使用@Builder、@SuperBuilder构造器时,会默认生成全参构造器。如果又同时需要无参构造器,这2个注解都需要标上。
默认生成无参构造器、全参构造器。
适应场景:简单对象使用@AllArgsConstructor创建,但同时有Json序列化/反序列化需要用@NoArgsConstructor额外添加无参构造器。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
public class Demo {
Long id;
Instant createTime;
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import java.time.Instant;
public class Demo {
Long id;
Instant createTime;
public Demo() {
}
public Demo(final Long id, final Instant createTime) {
this.id = id;
this.createTime = createTime;
}
}
@SuperBuilder
使用@Builder继承时,子类的builder方法默认不会包含父类属性。使用@SuperBuilder替代。
在使用@SuperBuilder构造器时,会默认生成全参构造器。如果又同时需要无参构造器,这2个注解都需要标上。
生成对象构造器。链式构造复杂对象。
适用场景:链式灵活构造复杂对象,省略一堆setter。不过也可以被链式setter替换。
import lombok.experimental.SuperBuilder;
@SuperBuilder
public class Demo {
Long id;
Instant createTime;
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
public class Demo {
Long id;
Instant createTime;
protected Demo(final DemoBuilder<?, ?> b) {
this.id = b.id;
this.createTime = b.createTime;
}
public static DemoBuilder<?, ?> builder() {
return new DemoBuilderImpl();
}
private static final class DemoBuilderImpl extends DemoBuilder<Demo, DemoBuilderImpl> {
private DemoBuilderImpl() {
}
protected DemoBuilderImpl self() {
return this;
}
public Demo build() {
return new Demo(this);
}
}
public abstract static class DemoBuilder<C extends Demo, B extends DemoBuilder<C, B>> {
private Long id;
private Instant createTime;
public DemoBuilder() {
}
protected abstract B self();
public abstract C build();
public B id(final Long id) {
this.id = id;
return this.self();
}
public B createTime(final Instant createTime) {
this.createTime = createTime;
return this.self();
}
public String toString() {
return "Demo.DemoBuilder(id=" + this.id + ", createTime=" + this.createTime + ")";
}
}
}
@Slf4j
没什么坑,按需使用即可。它的api可能不如手动的slf4j丰富。
为当前类生成一个slf4j的logger。字段名为log(应该是大写遵循约定吧…)
适用场景:日志打印。按需无脑使用。
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Demo {
Long id;
Instant createTime;
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Demo {
private static final Logger log = LoggerFactory.getLogger(Demo.class);
Long id;
Instant createTime;
public Demo() {
}
}
@SneakyThrows
慎用,这个对代码的改造不易产生认知。异常最好仍是显示声明和处理。
不做示例了,最好手动处理异常。但是也尽量不要满屏的try-catch…结合异常处理在需要的地方去处理。
lombok.config
上面有提到使用继承时,@Data使用不友好,不会包含父类属性。那其实除了手动使用@ToString、@EqualsAndHashCode重写外,也可以通过全局配置来指定。
lombok使用lombok.config作为全局配置,因为是根据源码生成class所以放置在src/main/java源码路径下。lombok.config文件所在的位置会覆盖其所在包下所有lombok代码生成的默认行为,所以可以通过lombok.config的包目录控制影响代码生成默认行为的范围。
建议简单一点,放在源码包根目录就行了。
具体的配置项可参考ConfigurationKeys.class。
列取几个比较好玩有用的配置项:
# toString、equalsAndHashCode默认callSuper
lombok.equalsAndHashCode.callSuper=call
lombok.toString.callSuper=call
# 指定loggerName。这个要悠着点要么全局配置,不然影响源码里的字段使用
lombok.log.fieldName=log
# 链式setter,了解下吧,还是用@SuperBuilder直观点。但是builder要注意同时注解无参构造器
lombok.accessors.chain=false
# 禁止不建议的lombok用法,比如@Builder、@SneakyThrows、@Synchronized
lombok.builder.flagUsage=ERROR
lombok.sneakyThrows.flagUsage=ERROR
lombok.synchronized.flagUsage=ERROR
是银弹吗?
lombok有人推荐,有人排斥。就我个人使用经验来说,还是推荐使用,原因:
- 消除重复劳动 -> 问就是懒,CRUD也可以甜甜的
- 减少变更引起的重复劳动和改动遗漏
- 代码清爽简洁 -> 一个POJO几十上百行,┭┮﹏┭┮,把字段混在getter/setter中间根本找不到!
有什么坑吗?
- 源码跟反编译class对不上 -> 很显然是对不上了,比如排查日志发现堆栈的行号跟源码行号不一样,一脸懵逼
- 需要稍稍了解下lombok,避免因为版本问题产生未知错误 -> Maven版本、lombok版本、jdk版本
其实就是看使用姿势吧,咱总不至于在POJO对象里写bug吧?还是尽量遵循约定大于配置,简单有效就是最好的!