Lombok —— 简介

Lombok —— 简介

在这里插入图片描述

官网地址:Project Lombok

1、什么是 Lombok

官网介绍:

Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.

Project Lombok 是一个 java 库,可自动插入您的编辑器和构建工具,为您的 java 增添趣味。
永远不要再编写另一个 getter 或 equals 方法,使用一个注释,您的类就有一个功能齐全的构建器、自动化您的日志记录变量等等。

Lombok 是一种 JavaTM 实用工具,可用来帮助开发人员消除 Java 中的冗长代码,尤其是对于简单的 Java 对象(POJO)/ JavaBean,它通过注解实现这一目的

简而言之:Lombok能以简单的注解形式来简化java代码,提高开发人员的开发效率

使用前后对比

  • 使用前

    package cn.edu.hziee.pojo;
    
    public class User {
        private String userName;
        private String password;
        private Integer age;
        private String phone;
        private String[] interest;
        private Boolean isMale;
    
        public User() {
        }
    
        public User(String userName, String password, Integer age, String phone, String[] interest, Boolean isMale) {
            this.userName = userName;
            this.password = password;
            this.age = age;
            this.phone = phone;
            this.interest = interest;
            this.isMale = isMale;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public String getPhone() {
            return phone;
        }
    
        public void setPhone(String phone) {
            this.phone = phone;
        }
    
        public String[] getInterest() {
            return interest;
        }
    
        public void setInterest(String[] interest) {
            this.interest = interest;
        }
    
        public Boolean getMale() {
            return isMale;
        }
    
        public void setMale(Boolean male) {
            isMale = male;
        }
    }
    
  • 使用 Lombok 后

    package cn.edu.hziee.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
        private String userName;
        private String password;
        private Integer age;
        private String phone;
        private String[] interest;
        private Boolean isMale;
    }
    

    在这里插入图片描述

2、Lombok 原理

在这里插入图片描述

Lombok本质上就是一个实现了“JSR 269 API”的程序。在使用javac的过程中,它产生作用的具体流程如下:

  1. javac 对源代码进行分析,生成了一棵抽象语法树(AST)
  2. 运行过程中调用实现了“JSR 269 API”的Lombok程序
  3. 此时 ombok 就对第一步骤得到的AST进行处理,找到 @Data 注解所在类对应的语法树(AST),然后修改该语法树(AST),增加 getter 和 setter 方法定义的相应树节点
  4. javac 使用修改后的抽象语法树(AST)生成字节码文件,即给 class 增加新的节点(代码块)

通过读Lombok源码,发现对应注解的实现都在HandleXXX中,比如@Getter注解的实现在HandleGetter.handle()。还有一些其它类库使用这种方式实现,比如Google Auto、Dagger等等

JSR 269:插件化注解处理API(Pluggable Annotation Processing API)

官网地址:The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 269 (jcp.org)

Lombok 的核心之处就是对于注解的解析上。JDK5引入了注解的同时,也提供了两种解析方式

  1. 编译阶段处理

    @Retention(RetentionPolicy.SOURCE)
    

    编译时解析有两种机制,分别简单描述下:

    • Annotation Processing Tool

      apt 自 JDK5 产生,JDK7 已标记为过期,不推荐使用,JDK8 中已彻底删除,自 JDK6 开始,可以使用 Pluggable Annotation Processing API 来替换它,apt 被替换主要有2点原因:

      • api都在com.sun.mirror非标准包下
      • 没有集成到javac中,需要额外运行
    • Pluggable Annotation Processing API

      在这里插入图片描述

      JSR 269 自 JDK6 加入,作为 apt 的替代方案,它解决了 apt 的两个问题,javac 在执行的时候会调用实现了该 API 的程序,这样我们就可以对编译器做一些增强,javac 执行的过程如下:

      在这里插入图片描述

  2. 运行阶段处理

    @Retention(RetentionPolicy.RUNTIME)
    

    运行时能够解析的注解,必须将 @Retention 设置为 RUNTIME ,这样就可以通过反射拿到该注解。 java.lang.reflect 反射包中提供了一个接口 AnnotatedElement ,该接口定义了获取注解信息的几个方法,Class、Constructor、Field、Method、Package 等都实现了该接口,对反射熟悉的朋友应该都会很熟悉这种解析方式Lombok 注解在编译阶段就会生效,在编译生成的 .class 文件中生成相应的方法

例如:

  • 加有 Lombok 注解的 .java 文件

    package cn.edu.hziee.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
        private String userName;
        private String password;
        private Integer age;
        private String phone;
        private String[] interest;
        private Boolean isMale;
    }
    
  • 编译生成的 .class 文件

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by FernFlower decompiler)
    //
    
    package cn.edu.hziee.pojo;
    
    import java.util.Arrays;
    
    public class User {
        private String userName;
        private String password;
        private Integer age;
        private String phone;
        private String[] interest;
        private Boolean isMale;
    
        public String getUserName() {
            return this.userName;
        }
    
        public String getPassword() {
            return this.password;
        }
    
        public Integer getAge() {
            return this.age;
        }
    
        public String getPhone() {
            return this.phone;
        }
    
        public String[] getInterest() {
            return this.interest;
        }
    
        public Boolean getIsMale() {
            return this.isMale;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public void setPhone(String phone) {
            this.phone = phone;
        }
    
        public void setInterest(String[] interest) {
            this.interest = interest;
        }
    
        public void setIsMale(Boolean isMale) {
            this.isMale = isMale;
        }
    
        public boolean equals(Object o) {
            if (o == this) {
                return true;
            } else if (!(o instanceof User)) {
                return false;
            } else {
                User other = (User)o;
                if (!other.canEqual(this)) {
                    return false;
                } else {
                    label75: {
                        Object this$age = this.getAge();
                        Object other$age = other.getAge();
                        if (this$age == null) {
                            if (other$age == null) {
                                break label75;
                            }
                        } else if (this$age.equals(other$age)) {
                            break label75;
                        }
    
                        return false;
                    }
    
                    Object this$isMale = this.getIsMale();
                    Object other$isMale = other.getIsMale();
                    if (this$isMale == null) {
                        if (other$isMale != null) {
                            return false;
                        }
                    } else if (!this$isMale.equals(other$isMale)) {
                        return false;
                    }
    
                    Object this$userName = this.getUserName();
                    Object other$userName = other.getUserName();
                    if (this$userName == null) {
                        if (other$userName != null) {
                            return false;
                        }
                    } else if (!this$userName.equals(other$userName)) {
                        return false;
                    }
    
                    label54: {
                        Object this$password = this.getPassword();
                        Object other$password = other.getPassword();
                        if (this$password == null) {
                            if (other$password == null) {
                                break label54;
                            }
                        } else if (this$password.equals(other$password)) {
                            break label54;
                        }
    
                        return false;
                    }
    
                    label47: {
                        Object this$phone = this.getPhone();
                        Object other$phone = other.getPhone();
                        if (this$phone == null) {
                            if (other$phone == null) {
                                break label47;
                            }
                        } else if (this$phone.equals(other$phone)) {
                            break label47;
                        }
    
                        return false;
                    }
    
                    if (!Arrays.deepEquals(this.getInterest(), other.getInterest())) {
                        return false;
                    } else {
                        return true;
                    }
                }
            }
        }
    
        protected boolean canEqual(Object other) {
            return other instanceof User;
        }
    
        public int hashCode() {
            int PRIME = true;
            int result = 1;
            Object $age = this.getAge();
            int result = result * 59 + ($age == null ? 43 : $age.hashCode());
            Object $isMale = this.getIsMale();
            result = result * 59 + ($isMale == null ? 43 : $isMale.hashCode());
            Object $userName = this.getUserName();
            result = result * 59 + ($userName == null ? 43 : $userName.hashCode());
            Object $password = this.getPassword();
            result = result * 59 + ($password == null ? 43 : $password.hashCode());
            Object $phone = this.getPhone();
            result = result * 59 + ($phone == null ? 43 : $phone.hashCode());
            result = result * 59 + Arrays.deepHashCode(this.getInterest());
            return result;
        }
    
        public String toString() {
            return "User(userName=" + this.getUserName() + ", password=" + this.getPassword() + ", age=" + this.getAge() + ", phone=" + this.getPhone() + ", interest=" + Arrays.deepToString(this.getInterest()) + ", isMale=" + this.getIsMale() + ")";
        }
    
        public User() {
        }
    
        public User(String userName, String password, Integer age, String phone, String[] interest, Boolean isMale) {
            this.userName = userName;
            this.password = password;
            this.age = age;
            this.phone = phone;
            this.interest = interest;
            this.isMale = isMale;
        }
    }
    

3、Lombok 安裝

添加 maven 依賴

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version>
    <scope>provided</scope>
</dependency>

官网说明:

The Jetbrains IntelliJ IDEA editor is compatible with lombok without a plugin as of version 2020.3.

For versions prior to 2020.3, you can add the Lombok IntelliJ plugin to add lombok support for IntelliJ:

  • Go to File > Settings > Plugins
  • Click on Browse repositories...
  • Search for Lombok Plugin
  • Click on Install plugin
  • Restart IntelliJ IDEA

在这里插入图片描述

4、Lombok的优缺点

Lombok 注解可以自动生成代码,大大减少了代码量,使代码非常简洁。

但是并不意味着Lombok的使用没有任何问题,在使用 Lombok 的过程中,还可能存在对队友不友好、对代码不友好、对调试不友好、对升级不友好等问题。

虽然,使用 Lombok 还会导致破坏封装性的问题,但是我更认为 Lombok 的操作是遵循了Bean 的使用初衷。

Bean 尤其数据库和Java类的映射 Bean,Java 对 Bean 的定义和使用就是无参数的构造方法和 set 和 get 方法,而不应该在 bean 中处理任何和业务有任何关系的逻辑

4.1、Lombok 优点

使用 @Data 注解大大减少了代码量,使代码非常简洁,这也是很多开发者热衷于使用 Lombok 的主要原因。

不仅如此,Lombok其它的优势:

  • 减少模板代码:Lombok 处理 get,set,toString,hash,equal 等方法,大量的模板代码进行封装,减少重复代码,当增加新属性的时候,以上方法都不需要再重新编写
  • 增强代码可读性:专注于类的属性定义,不需要再去为排版浪费时间
  • 减少代码维护:新增属性的时候,会减少非常多的代码维护工作

4.2、Lombok 缺点

  1. 强迫队友

    Lombok 插件的使用,要求开发者一定要在 IDE 中安装对应的插件。不仅自己要安装,任何和你协同开发的人都要安装。

    如果有谁未安装插件的话,使用IDE打开一个基于 Lombok 的项目的话会提示找不到方法等错误,导致项目编译失败。

    更重要的是,如果我们定义的一个jar包中使用了 Lombok ,那么就要求所有依赖这个 jar 包的所有应用都必须安装插件,这种侵入性是很高的。

    只次一点,我就已经决定不在自己的代码中使用 Lombok 注解了,但是,为了项目编译我依然会使用 Lombok 插件

  2. 代码可调试性降低

    Lombok 确实可以帮忙减少很多代码,因为 Lombok 会帮忙自动生成很多代码。

    但是,这些代码是要在编译阶段才会生成的,所以在开发的过程中,其实很多代码其实是缺失的。

    这就给代码调试带来一定的问题,我们想要知道某个类中的某个属性的getter方法都被哪些类引用的话,就没那么简单了。

  3. 影响版本升级

    Lombok 对于代码有很强的侵入性,就可能带来一个比较大的问题,那就是会影响我们对 JDK 的升级。

    按照如今 JDK 的升级频率,每半年都会推出一个新的版本,但是 Lombok 作为一个第三方工具,并且是由开源团队维护的,那么他的迭代速度是无法保证的

    所以,如果我们需要升级到某个新版本的 JDK 的时候,若其中的特性在 Lombok 中不支持的话就会受到影响。

    还有一个可能带来的问题,就是 Lombok 自身的升级也会受到限制。

    因为一个应用可能依赖了多个 jar 包,而每个jar包可能又要依赖不同版本的 Lombok,这就导致在应用中需要做版本仲裁,而我们知道,jar 包版本仲裁是没那么容易的,而且发生问题的概率也很高

  4. 更容易产生错误

    在使用 Lombok 过程中,如果对于各种注解的底层原理不理解的话,很容易产生意想不到的结果。

    举一个简单的例子:

    我们知道,当我们使用 @Data 定义一个类的时候,会自动帮我们生成 equals() 方法 。

    但是如果只使用了 @Data,而不使用 @EqualsAndHashCode(callSuper=true) 的话,会默认是 @EqualsAndHashCode(callSuper=false),这时候生成的 equals() 方法只会比较子类的属性,不会考虑从父类继承的属性,无论父类属性访问权限是否开放,这就可能得到意想不到的结果。

  5. 可能会破坏封装性

    如果说上面的4点问题都可以人为避免,那么,关于封装性的问题就是Lombok的短板了。

    举个简单的例子,我们定义一个购物车类:

    @Data
    
    public class ShoppingCart { 
    
        //商品数目
        private int itemsCount; 
    
        //总价格
        private double totalPrice; 
    
        //商品明细
        private List items = new ArrayList<>();
        
    }
    

    我们知道,购物车中商品数目、商品明细以及总价格三者之前其实是有关联关系的,如果需要修改的话是要一起修改的

    但是,我们使用了 Lombok 的 @Data 注解,对于 itemsCount 和 totalPrice 这两个属性,虽然我们将它们定义成 private 类型,但是提供了 public 的 getter、setter 方法

    外部可以通过 setter 方法随意地修改这两个属性的值,我们可以随意调用 setter 方法,来重新设置 itemsCount、totalPrice 属性的值,这也会导致其跟 items 属性的值不一致

    而面向对象封装的定义是:通过访问权限控制,隐藏内部数据,外部仅能通过类提供的有限的接口访问、修改内部数据。所以,暴露不应该暴露的 setter 方法,明显违反了面向对象的封装特性

    好的做法应该是不提供 getter/setter,而是只提供一个 public 的 addItem 方法,同时取修改 itemsCount、totalPrice 以及 items 三个属性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

bit-apk-code

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

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

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

打赏作者

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

抵扣说明:

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

余额充值