【工具使用】01 lombok

简介

在写实体类时,通常要为实体类写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常用注解及食用姿势~不过约定大于配置,简单就是最好的!常用食用姿势有哪些呢?

  1. 使用@Data为实体类一键生成getter、setter、toString、equals、hashcode
  2. 使用@NoArgsConstructor、@AllArgsConstructor为实体类添加默认的无参构造器、全参构造器
  3. 使用@SuperBuilder、@Builder为实体类添加构造器模式,方便链式构建复杂对象
  4. 使用@Slf4j为类添加一个静态常量slf4j的日志logger
  5. 使用@SneakyThrows为方法添加try-catch处理异常
  6. 全局配置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有人推荐,有人排斥。就我个人使用经验来说,还是推荐使用,原因:

  1. 消除重复劳动 -> 问就是懒,CRUD也可以甜甜的
  2. 减少变更引起的重复劳动和改动遗漏
  3. 代码清爽简洁 -> 一个POJO几十上百行,┭┮﹏┭┮,把字段混在getter/setter中间根本找不到!

有什么坑吗?

  1. 源码跟反编译class对不上 -> 很显然是对不上了,比如排查日志发现堆栈的行号跟源码行号不一样,一脸懵逼
  2. 需要稍稍了解下lombok,避免因为版本问题产生未知错误 -> Maven版本、lombok版本、jdk版本

其实就是看使用姿势吧,咱总不至于在POJO对象里写bug吧?还是尽量遵循约定大于配置,简单有效就是最好的!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

theskyzero

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值