前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Lombok有啥牛皮的?SpringBoot和IDEA官方都要支持它!

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

作者头像
沉默王二
发布2022-04-14 15:20:10
5120
发布2022-04-14 15:20:10
举报
文章被收录于专栏:沉默王二

大家好,我是二哥呀!由于感冒嗓子哑了快一周时间,不能说话的感觉实在是太糟糕了!

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,这会让代码看起来不够简练,就像老太婆的裹脚布一样,又臭又长。

代码语言:javascript
复制
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 等等的。

代码语言:javascript
复制
@Getter
@Setter
class CmowerLombok {
 private int age;
 private String name;
 private BigDecimal money;
}

瞧一瞧,源码看起来苗条多了,对不对?

二、集成 Lombok

如果项目使用 Maven 构建的话,添加Lombok 的依赖就变得轻而易举了。

代码语言:javascript
复制
<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 插件,否则 Javabeangetter / setter 就无法自动编译,也就不能被调用。不过,新版的 Intellij IDEA 也已经内置好了,不需要再安装。

三、常用的 Lombok 注解

1)**@Getter / @Setter**

@Getter / @Setter 用起来很灵活,比如说像下面这样:

代码语言:javascript
复制
class CmowerLombok {
 @Getter @Setter private int age;
 @Getter private String name;
 @Setter private BigDecimal money;
}

字节码文件反编译后的内容是:

代码语言:javascript
复制
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**

打印日志的好帮手哦。

代码语言:javascript
复制
@ToString
class CmowerLombok {
 private int age;
 private String name;
 private BigDecimal money;
}

字节码文件反编译后的内容是:

代码语言:javascript
复制
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 也提供了一个类似的。

代码语言:javascript
复制
class CmowerLombok {
 public void test() {
  val names = new ArrayList<String>();
  names.add("沉默王二");
  val name = names.get(0);
  System.out.println(name);
 }
}

字节码文件反编译后的内容是:

代码语言:javascript
复制
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 / setterequalshashCode,以及 toString,是个总和的选项。

代码语言:javascript
复制
@Data
class CmowerLombok {
 private int age;
 private String name;
 private BigDecimal money;
}

字节码文件反编译后的内容是:

代码语言:javascript
复制
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等。

代码语言:javascript
复制
@Slf4j
public class Log4jDemo {
    public static void main(String[] args) {
        log.info("level:{}","info");
        log.warn("level:{}","warn");
        log.error("level:{}", "error");
    }
}

字节码文件反编译后的内容是:

代码语言:javascript
复制
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 注解可以用来通过建造者模式来创建对象,这样就可以通过链式调用的方式进行对象赋值,非常的方便。

代码语言:javascript
复制
@Builder
@ToString
public 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);
    }
}

字节码文件反编译后的内容是:

代码语言:javascript
复制
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的应用、责任链设计模式的应用、高并发性能优化、滑动窗口的实现、匀速限流与冷启动算法、信号量隔离的目的与实现等。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 沉默王二 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Lombok 的自我介绍
  • 二、集成 Lombok
  • 三、常用的 Lombok 注解
  • 四、Lombok 的处理流程
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档