Lombok有啥牛皮的?SpringBoot和IDEA官方都要支持它!


大家好,我是二哥呀!由于感冒嗓子哑了快一周时间,不能说话的感觉实在是太糟糕了!今天高兴,文末送大家五本新书《实战 Alibaba Sentinel》,迫不及待的小伙伴直接拉到文末获取抽奖方式。

Spring Boot 早在 2.1.x 版本后就在 starter 中内置了 Lombok 依赖,Intellij IDEA 也早在 IDEA 2020.3 版本的时候内置了 Lombok 插件。为什么它们都要支持 Lombok 呢?Lombok 到底有啥牛皮的?今天我们就来补上这一课。
一、Lombok 的自我介绍
Lombok 在官网是这样作自我介绍的:
Project Lombok makes java a spicier language by adding 'handlers' that know how to build and compile simple, boilerplate-free, not-quite-java code.
大致的意思就是:Lombok 是个好类库,可以为 Java 代码添加一些“处理程序”,让其变得更简洁、更优雅。在我看来,Lombok 最大的好处就在于通过注解的形式来简化 Java 代码,简化到什么程度呢?
作为一名 Java 程序员,我相信你一定写过不少的 getter / setter,尽管可以借助 IDE 来自动生成,可一旦 Javabean 的属性很多,就免不了要产生大量的 getter / setter,这会让代码看起来不够简练,就像老太婆的裹脚布一样,又臭又长。
class Cmower { private int age; private String name; private BigDecimal money; public int getAge() {  return age; } public void setAge(int age) {  this.age = age; } public String getName() {  return name; } public void setName(String name) {  this.name = name; } public BigDecimal getMoney() {  return money; } public void setMoney(BigDecimal money) {  this.money = money; }}
Lombok 可以通过注解的方式,在编译的时候自动为 Javabean 的属性生成 getter / setter,不仅如此,还可以生成构造方法、equals方法、hashCode方法,以及 toString方法。注意是在编译的时候哦,源码当中是没有 getter / setter 等等的。
@Getter@Setterclass CmowerLombok { private int age; private String name; private BigDecimal money;}
瞧一瞧,源码看起来苗条多了,对不对?
二、集成 Lombok
如果项目使用 Maven 构建的话,添加Lombok 的依赖就变得轻而易举了。
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.6</version> <scope>provided</scope></dependency>
其中 scope=provided,就说明 Lombok 只在编译阶段生效。也就是说,Lombok 会在编译期静悄悄地将带 Lombok 注解的源码文件正确编译为完整的 class 文件。
SpringBoot 2.1.x 版本后不需要再显式地添加 Lombok 依赖了。之后,还需要为 Intellij IDEA 安装 Lombok 插件,否则 Javabean 的 getter / setter 就无法自动编译,也就不能被调用。不过,新版的 Intellij IDEA 也已经内置好了,不需要再安装。

三、常用的 Lombok 注解
1)**@Getter / @Setter**
@Getter / @Setter 用起来很灵活,比如说像下面这样:
class CmowerLombok { @Getter @Setter private int age; @Getter private String name; @Setter private BigDecimal money;}
字节码文件反编译后的内容是:
class CmowerLombok { private int age; private String name; private BigDecimal money; public int getAge() {  return this.age; } public void setAge(int age) {  this.age = age; } public String getName() {  return this.name; } public void setMoney(BigDecimal money) {  this.money = money; }}
2)**@ToString**
打印日志的好帮手哦。
@ToStringclass CmowerLombok { private int age; private String name; private BigDecimal money;}
字节码文件反编译后的内容是:
class CmowerLombok { private int age; private String name; private BigDecimal money; public String toString() {  return "CmowerLombok(age=" + this.age + ", name=" + this.name + ", money=" + this.money + ")"; }}
3)val
在编写 JavaScript 代码时,我一直觉得 var 这个变量声明类型用起来特别方便。Lombok 也提供了一个类似的。
class CmowerLombok { public void test() {  val names = new ArrayList<String>();  names.add("沉默王二");  val name = names.get(0);  System.out.println(name); }}
字节码文件反编译后的内容是:
class CmowerLombok { public void test() {  ArrayList<String> names = new ArrayList();  names.add("沉默王二");  String name = (String) names.get(0);  System.out.println(name); }}
4)**@Data**
@Data 注解可以生成 getter / setter、equals、hashCode,以及 toString,是个总和的选项。
@Dataclass CmowerLombok { private int age; private String name; private BigDecimal money;}
字节码文件反编译后的内容是:
class CmowerLombok { private int age; private String name; private BigDecimal money; public int getAge() {  return this.age; } public String getName() {  return this.name; } public BigDecimal getMoney() {  return this.money; } public void setAge(int age) {  this.age = age; } public void setName(String name) {  this.name = name; } public void setMoney(BigDecimal money) {  this.money = money; } public boolean equals(Object o) {  if (o == this) {   return true;  } else if (!(o instanceof CmowerLombok)) {   return false;  } else {   CmowerLombok other = (CmowerLombok) o;   if (!other.canEqual(this)) {    return false;   } else if (this.getAge() != other.getAge()) {    return false;   } else {    Object this$name = this.getName();    Object other$name = other.getName();    if (this$name == null) {     if (other$name != null) {      return false;     }    } else if (!this$name.equals(other$name)) {     return false;    }    Object this$money = this.getMoney();    Object other$money = other.getMoney();    if (this$money == null) {     if (other$money != null) {      return false;     }    } else if (!this$money.equals(other$money)) {     return false;    }    return true;   }  } } protected boolean canEqual(Object other) {  return other instanceof CmowerLombok; } public int hashCode() {  int PRIME = true;  int result = 1;  int result = result * 59 + this.getAge();  Object $name = this.getName();  result = result * 59 + ($name == null ? 43 : $name.hashCode());  Object $money = this.getMoney();  result = result * 59 + ($money == null ? 43 : $money.hashCode());  return result; } public String toString() {  return "CmowerLombok(age=" + this.getAge() + ", name=" + this.getName() + ", money=" + this.getMoney() + ")"; }}
5)@Slf4j
@Slf4j 可以用来生成注解对象,你可以根据自己的日志实现方式来选用不同的注解,比如说:@Log、@Log4j、@Log4j2、@Slf4j等。
@Slf4jpublic class Log4jDemo {    public static void main(String[] args) {        log.info("level:{}","info");        log.warn("level:{}","warn");        log.error("level:{}", "error");    }}
字节码文件反编译后的内容是:
public class Log4jDemo {    private static final Logger log = LoggerFactory.getLogger(Log4jDemo.class);    public Log4jDemo() {    }    public static void main(String[] args) {        log.info("level:{}", "info");        log.warn("level:{}", "warn");        log.error("level:{}", "error");    }}
6)@Builder
@Builder 注解可以用来通过建造者模式来创建对象,这样就可以通过链式调用的方式进行对象赋值,非常的方便。
@Builder@ToStringpublic class BuilderDemo {    private Long id;    private String name;    private Integer age;    public static void main(String[] args) {        BuilderDemo demo = BuilderDemo.builder().age(18).name("沉默王二").build();        System.out.println(demo);    }}
字节码文件反编译后的内容是:
public class BuilderDemo {    private Long id;    private String name;    private Integer age;    public static void main(String[] args) {        BuilderDemo demo = builder().age(18).name("沉默王二").build();        System.out.println(demo);    }    BuilderDemo(final Long id, final String name, final Integer age) {        this.id = id;        this.name = name;        this.age = age;    }    public static BuilderDemo.BuilderDemoBuilder builder() {        return new BuilderDemo.BuilderDemoBuilder();    }    public String toString() {        return "BuilderDemo(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")";    }    public static class BuilderDemoBuilder {        private Long id;        private String name;        private Integer age;        BuilderDemoBuilder() {        }        public BuilderDemo.BuilderDemoBuilder id(final Long id) {            this.id = id;            return this;        }        public BuilderDemo.BuilderDemoBuilder name(final String name) {            this.name = name;            return this;        }        public BuilderDemo.BuilderDemoBuilder age(final Integer age) {            this.age = age;            return this;        }        public BuilderDemo build() {            return new BuilderDemo(this.id, this.name, this.age);        }        public String toString() {            return "BuilderDemo.BuilderDemoBuilder(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")";        }    }}
除了我上面提到的这些,Lombok 还提供了同步注解 @Synchronized、自动抛出异常注解 @SneakyThrows、不可变对象 @Value、自动生成 hashCode 和 equals 方法的注解 @EqualsAndHashCode 等等,大家可以去尝试一下,顺带看一下反编译后的字节码,体验一下 Lombok 的工作原理。
四、Lombok 的处理流程
一图胜千言,直接上图。

javac 对源代码进行分析,生成一棵抽象语法树(AST)
javac 编译过程中调用实现了JSR 269 的 Lombok 程序
Lombok 对 AST 进行处理,找到 Lombok 注解所在类对应的语法树(AST),然后修改该语法树,增加 Lombok 注解定义的相应树节点(所谓代码)
javac 使用修改后的抽象语法树生成字节码文件
Lombok 用起来虽然爽,但需要团队内部达成一致,就是要用大家都用,否则有些用了有些没用就会乱成一锅粥,很影响代码的整体风格。另外,假如有团队成员还在用 Eclipse,那么也得要求他安装 Lombok 插件,否则打开一个使用 Lombok 注解的项目就会无法通过编译。
如果一类使用了 Lombok 注解,通过类结构是可以查看到对应的方法的,比如说下图中的 toString 和 builder 方法。

打开 target 目录下的 .class 文件,就可以看到 Lombok 生成的反编译后的字节码文件,也可以验证 Lombok 是在编译阶段实现 Java 代码增强功能的。

相信看完技术文章的小伙伴手气一定会更旺,简单介绍一下这本书《实战 Alibaba Sentinel——深度解析微服务高并发流量治理》:
本书分为14章,涵盖的知识主要包括:限流与熔断等基础概念、Sentinel的特性与性能压测,Sentinel概念、核心类与数据结构,Sentinel整体工作流程,资源指标数据统计,限流与流量效果控制,熔断降级与熔断器,授权与系统自适应功能,扩展Sentinel实现开关降级,Sentinel动态数据源,Sentinel适配主流框架的实现原理,热点参数限流,集群限流,异步调用链的支持,资源指标数据的收集与持久化。本书内容丰富,概念通俗易懂,让读者不仅能够深入理解Sentinel的实现原理,还能够从Sentinel中学习到一些技术,如Java SPI的应用、责任链设计模式的应用、高并发性能优化、滑动窗口的实现、匀速限流与冷启动算法、信号量隔离的目的与实现等。
抽奖方式
这次一共送五本,点击二哥的公众号名片,后台回复「抽奖」即可参与,也不搞什么抽奖助手了,谁经常留言、转发、常读的我一看头像就知道了,逮 5 个幸运读者随机送,这次就别怪自己手气差了,解释权归二哥所有,你懂的,熟客真香。
下次记得给二哥的公众号「沉默王二」加个星标,这样一定不会错过了。
到顶部