认识Java项目开发效率工具 Lombok

作者:幻好

来源:恒生LIGHT云社区

引言

在通常的Java项目中,充斥着太多不友好的代码:POJO的getter/setter/toString;异常处理;I/O流的关闭操作等等,这些样板代码既没有技术含量,又影响着代码的美观,Lombok应运而生。

而 IDEA 2020 版本中,已经内置了Lombok插件,SpringBoot 2.1.x之后的版本也在Starter中内置了Lombok依赖。今天来讲讲Lombok的使用,看看它有何神奇之处!

Lombok的安装配置

使用 Lombok 之前我们先要在所使用的 IDE 中进行集成安装,这里以 IDEA 为例,安装步骤十分简单:

  • 前往 File -> Settings -> Plugin -> Marketplace ,搜索 Lombok,并安装

    image-20210926224151642.png

  • 选择搜索结果 Lombok ,点击 Install 安装。

  • 安装完成后重启即可。

在 IDE 安装了 Lombok 插件后,我们就可以在 pom.xml 文件中添加 Lombok 的依赖进行使用了。

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
复制代码

Lombok的常用注解

1. @Data

@Data 是一个方便使用的组合注解,是 @ToString@EqualsAndHashCode@Getter@Setter@RequiredArgsConstructor 的组合体。

@Data
public class DemoUser {
    private String userId;
    private String userName;
    private String userAge;
}
复制代码

编译后Lombok会生成如下代码:

public class DemoUser {
    private String userId;
    private String userName;
    private String userAge;

    public DemoUser() {
    }

    public String getUserId() {
        return this.userId;
    }

    public String getUserName() {
        return this.userName;
    }

    public String getUserAge() {
        return this.userAge;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void setUserAge(String userAge) {
        this.userAge = userAge;
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof DemoUser)) {
            return false;
        } else {
            DemoUser other = (DemoUser)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                label47: {
                    Object this$userId = this.getUserId();
                    Object other$userId = other.getUserId();
                    if (this$userId == null) {
                        if (other$userId == null) {
                            break label47;
                        }
                    } else if (this$userId.equals(other$userId)) {
                        break label47;
                    }

                    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;
                }

                Object this$userAge = this.getUserAge();
                Object other$userAge = other.getUserAge();
                if (this$userAge == null) {
                    if (other$userAge != null) {
                        return false;
                    }
                } else if (!this$userAge.equals(other$userAge)) {
                    return false;
                }

                return true;
            }
        }
    }

    protected boolean canEqual(Object other) {
        return other instanceof DemoUser;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $userId = this.getUserId();
        int result = result * 59 + ($userId == null ? 43 : $userId.hashCode());
        Object $userName = this.getUserName();
        result = result * 59 + ($userName == null ? 43 : $userName.hashCode());
        Object $userAge = this.getUserAge();
        result = result * 59 + ($userAge == null ? 43 : $userAge.hashCode());
        return result;
    }

    public String toString() {
        return "DemoUser(userId=" + this.getUserId() + ", userName=" + this.getUserName() + ", userAge=" + this.getUserAge() + ")";
    }
}
复制代码

2.@Value

使用 @Value 注解可以把类声明为不可变的,声明后此类相当于 final 类,无法被继承,其属性也会变成 final 属性。

@Value
public class DemoUser {
    private final String userId;
    private final String userName;
    private final String userAge;
}
复制代码

编译后Lombok会生成如下代码:

public final class DemoUser {
    private final String userId;
    private final String userName;
    private final String userAge;

    public DemoUser(String userId, String userName, String userAge) {
        this.userId = userId;
        this.userName = userName;
        this.userAge = userAge;
    }

    public String getUserId() {
        return this.userId;
    }

    public String getUserName() {
        return this.userName;
    }

    public String getUserAge() {
        return this.userAge;
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof DemoUser)) {
            return false;
        } else {
            DemoUser other;
            label44: {
                other = (DemoUser)o;
                Object this$userId = this.getUserId();
                Object other$userId = other.getUserId();
                if (this$userId == null) {
                    if (other$userId == null) {
                        break label44;
                    }
                } else if (this$userId.equals(other$userId)) {
                    break label44;
                }

                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;
            }

            Object this$userAge = this.getUserAge();
            Object other$userAge = other.getUserAge();
            if (this$userAge == null) {
                if (other$userAge != null) {
                    return false;
                }
            } else if (!this$userAge.equals(other$userAge)) {
                return false;
            }

            return true;
        }
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $userId = this.getUserId();
        int result = result * 59 + ($userId == null ? 43 : $userId.hashCode());
        Object $userName = this.getUserName();
        result = result * 59 + ($userName == null ? 43 : $userName.hashCode());
        Object $userAge = this.getUserAge();
        result = result * 59 + ($userAge == null ? 43 : $userAge.hashCode());
        return result;
    }

    public String toString() {
        return "DemoUser(userId=" + this.getUserId() + ", userName=" + this.getUserName() + ", userAge=" + this.getUserAge() + ")";
    }
}
复制代码

3.@Slf4j

使用 Lombok 生成日志对象时,根据使用日志实现的不同,有多种注解可以使用。比如 @Log@Log4j@Log4j2@Slf4j等。

@Value
public class DemoUser {
    private final String userId;
    private final String userName;
    private final String userAge;
}
复制代码

编译后Lombok会生成如下代码:

public class DemoUser {
    private static final Logger log = LoggerFactory.getLogger(DemoUser.class);
    private String userId;
    private String userName;
    private String userAge;

    public DemoUser() {
    }
}
复制代码

4.@Builder

使用 @Builder 注解可以通过建造者模式来创建对象,建造者模式加链式调用,创建对象非常方便。

@Builder
public class DemoUser {
    private final String userId;
    private final String userName;
    private final String userAge;
}
复制代码

编译后Lombok会生成如下代码:

public class DemoUser {
    private final String userId;
    private final String userName;
    private final String userAge;

    DemoUser(String userId, String userName, String userAge) {
        this.userId = userId;
        this.userName = userName;
        this.userAge = userAge;
    }

    public static DemoUser.DemoUserBuilder builder() {
        return new DemoUser.DemoUserBuilder();
    }

    public static class DemoUserBuilder {
        private String userId;
        private String userName;
        private String userAge;

        DemoUserBuilder() {
        }

        public DemoUser.DemoUserBuilder userId(String userId) {
            this.userId = userId;
            return this;
        }

        public DemoUser.DemoUserBuilder userName(String userName) {
            this.userName = userName;
            return this;
        }

        public DemoUser.DemoUserBuilder userAge(String userAge) {
            this.userAge = userAge;
            return this;
        }

        public DemoUser build() {
            return new DemoUser(this.userId, this.userName, this.userAge);
        }

        public String toString() {
            return "DemoUser.DemoUserBuilder(userId=" + this.userId + ", userName=" + this.userName + ", userAge=" + this.userAge + ")";
        }
    }
}
复制代码

Lombok的原理

如果IDEA不安装Lombok插件的话,我们打开使用Lombok的项目是无法通过编译的。装了以后IDEA才会提示我们Lombok为我们生成的方法和属性。

使用了@Data注解以后,查看类结构可以发现getter、setter、toString等方法。

自Java 6起,javac开始支持JSR 269 Pluggable Annotation Processing API规范,只要程序实现了该API,就能在java源码编译时调用定义的注解。举例来讲,如今有一个实现了"JSR 269 API"的程序A,那么使用javac编译源码的时候具体流程以下:

  1. javac对源代码进行分析,生成一棵抽象语法树(AST);插件

  2. 运行过程当中调用实现了"JSR 269 API"的A程序;

  3. 此时A程序就能够完成它本身的逻辑,包括修改第一步骤获得的抽象语法树(AST);

  4. javac使用修改后的抽象语法树(AST)生成字节码文件;

    详细的流程图以下:

    53a402ceac1a471d889423831bfcb4bf-2.png

    从上面的Lombok执行的流程图中能够看出,在Javac 解析成AST抽象语法树以后, Lombok 根据本身编写的注解处理器,动态地修改 AST,增长新的节点(即Lombok自定义注解所须要生成的代码),最终经过分析生成JVM可执行的字节码Class文件。使用Annotation Processing自定义注解是在编译阶段进行修改,而jdk的反射技术是在运行时动态修改,二者相比,反射虽然更加灵活一些可是带来的性能损耗更加大。

Lombok的缺点

  • 在开发工具中,使用Lombok注解省略的方法在被调用时,会报找不到定义的错误,此种状况下,须要作些特殊处理;
  • 使用Lombok虽然可以省去手动建立settergetter等方法的麻烦,可是却大大下降了源码的可读性和完整性,下降了阅读源代码的温馨度。

猜你喜欢

转载自juejin.im/post/7016975945722494983