Java开发工具–Lombok深入实战

概述

在写POJO代码时,经常需要生成getter和setter,hashcode()、tostring()和equals()方法等代码。感觉甚是麻烦,虽然有intellij IDEA快捷键能够一键自动生成些代码。但是,代码里面充斥着这种没有意义的片段,看着都觉得烦;另一方面,单元测试时,若是考察行覆盖率,这种POJO代码势必会增加分母的大小,降低代码覆盖率。
偶然得知Lombok Project,通过使用注解,可以消除冗余的java代码。官网介绍:

Project Lombok makes java a spicier language by adding ‘handlers’ that know how to build and compile simple, boilerplate-free, not-quite-java code.

使用

引入依赖即可:

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

Gradle项目的引入方式类似:

provided group: 'org.projectlombok', name: 'lombok', version: '1.16.18'

在spring boot项目中,引入Lombok时无需指定<version>和<scope>,spring boot已经自动适配好。

安装

在IDEA中使用Lombok需要先安装插件,快捷键 Ctrl + Alt + S,然后选择 plugins——> browse repositories,搜索 Lombok,install即可,安装需要重启。
其次,Lombok是在编译时期,自动生成所需方法的,所以需要开启annotation processing;依旧是刚才的快捷键,可以搜索"annotation processing",然后选择"Enable annotation processing"。

注解

不完全统计:

@NonNull:标识对象是否为空,为空则抛出异常
@Getter/@Setter:自动生成Getter/Setter方法
@ToString:覆盖tostring方法,可以添加限制条件
@EqualsAndHashCode:覆盖equal和hashCode方法,生成方法时只会使用类中的非静态和非transient成员变量
@Data:@Getter/@Setter, @ToString, @EqualAndHashCode,@NoArgsConstructor等组合
@Cleanup:用在变量前面,可以保证此变量代表的资源会被自动关闭,默认是调用资源的close()方法,如果该资源有其它关闭方法,可使用@Cleanup("methodName")来指定要调用的方法
@NoArgsConstructor/@RequiredArgsConstructor /@AllArgsConstructor:第二个注解使用类中所有带有@NonNull注解的或带有final修饰的非静态成员变量生成对应的构造方法,
@Log:类注解,可以省去从日志工厂生成日志对象这一步,直接进行日志记录,具体注解根据日志工具的不同而不同,同时,可以在注解中使用topic来指定生成log对象时的类名
@Builder:关于build模式,可以参考《Effective  java》这本书
@Value
@SneakyThrows
@Synchronized
@Accessors(chain = true),在生成 setter 方法时不返回 void,而是返回 this,当前类;

实战

@Getter & @Setter & @ToString

如果没有lombok这个工具,之前写一个实体类需要写这么多行代码:

public class Person {
    private boolean employed;
    private String name;

    Person() {
    }

    public String say() {
        return this.getName();
    }

    public String toString() {
        return "Person(employed=" + this.isEmployed() + ", name=" + this.getName() + ")";
    }

    public boolean isEmployed() {
        return this.employed;
    }

    public void setEmployed(boolean employed) {
        this.employed = employed;
    }

    public String getName() {
        return this.name;
    }

    protected void setName(String name) {
        this.name = name;
    }
}

引入Lombok后的代码量:

@ToString
 class Person {
    @Getter
    @Setter
    private boolean employed;
    @Getter
    @Setter(AccessLevel.PROTECTED)
    private String name;

    public String say() {
        return getName();
    }
}

甚至可以一个@Data注解搞定。

@Builder

23种设计模式其一,构建器模式,建造者模式。这次先看看使用注解之前的java code:

@Builder
public class BuilderExample {
    private String name;
    private int age;
}

再看看其经过编译之后的java code:

import java.beans.ConstructorProperties;

public class BuilderExample {
    private String name;
    private int age;

    @ConstructorProperties({"name", "age"})
    BuilderExample(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static BuilderExample.BuilderExampleBuilder builder() {
        return new BuilderExample.BuilderExampleBuilder();
    }

    public static class BuilderExampleBuilder {
        private String name;
        private int age;

        BuilderExampleBuilder() {
        }

        public BuilderExample.BuilderExampleBuilder name(String name) {
            this.name = name;
            return this;
        }

        public BuilderExample.BuilderExampleBuilder age(int age) {
            this.age = age;
            return this;
        }

        public BuilderExample build() {
            return new BuilderExample(this.name, this.age);
        }

        public String toString() {
            return "BuilderExample.BuilderExampleBuilder(name=" + this.name + ", age=" + this.age + ")";
        }
    }
}

然后可以使用,代码风格简洁易懂:

UserVO user = UserVO.builder().id(3).username("chifanshuijiao").build();

注意:需要额外增加两个注解:

@NoArgsConstructor
@AllArgsConstructor

@Data

先看使用注解的类:

@Data
public class User {
    private Integer id;
}

编译之后的类长这样:

public class User {
    private Integer id;

    public User() {
    }

    public Integer getId() {
        return this.id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    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 {
                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;
                }
                return true;
            }
        }
    }

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

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $id = this.getId();
        int result = result * 59 + ($id == null ? 43 : $id.hashCode());
        return result;
    }

    public String toString() {
        return "User(id=" + this.getId() + ")";
    }
}

更多注解的用法,请参考官网

自定义注解

TODO

lombok.config

config官方文档
查看当前版本的lombok所支持的配置项使用命令工具config
java -jar lombok.jar config -g --verbose
cofing工具查看某个配置文件所有的配置项
java -jar lombok.jar config lombok.config

示例:

# Tell the configuration system it should stop looking for other configuration files (default: false).
# 指明lombok的根目录为当前配置文件所在目录
# 配置文件是分层的,原则是接近源文件的配置设置优先。在根目录的子目录中可以创建lombok.config配置文件,来覆盖根目录的配置文件(只覆盖配置项相同的配置,其他继承根目录的配置,若没有显式配置则使用默认值)
config.stopBubbling = true
# Generate setters that return 'this' instead of 'void' (default: false).
lombok.accessors.chain = true
# 使用不带前缀的简单方式生成setter和getter,即使用原始的字段名称省略get和set前缀
lombok.accessors.fluent=true
# 跳过添加一个@java.bean.ConstructorProperties生成的构造器
lombok.anyConstructor.suppressConstructorProperties=true
# Don't call the getters but use the fields directly in the generated toString method (default = false).
lombok.toString.doNotUseGetters = true
# When generating toString for classes that extend something (other than Object), either automatically take into account superclass implementation (call), or don't (skip), or warn and don't (warn). (default = warn).
lombok.toString.callSuper = CALL

要求:lombok 1.14+。

原理

采用JSR269所提出的插入式注解处理(Pluggable Annotation Processing),并结合动态代码生成技术所开发的。如下图所示:
这里写图片描述
图示为 javac 的编译过程,java文件首先通过进行解析构建出一个AST,然后执行注解处理,最后经过分析优化生成二进制的.class文件。
Annotation Processing 是在解析和生成之间的一个步骤。
这里写图片描述
上图是 Lombok 处理流程,在Javac 解析成抽象语法树之后(AST),Lombok 根据自己的注解处理器,动态的修改 AST,增加新的节点(所谓代码),最终通过分析和生成字节码。

其他注解

@Accessors

为getter和setter设计,lombok 1.18.22版本位于包lombok.experimental下面,即实验性。源码如下:

public @interface Accessors {
	boolean fluent() default false;
	boolean chain() default false;
	String[] prefix() default {};

可用于类和属性。因此使用@Accessors时有3个选择:

  1. fluent,流畅风格,布尔值。为真,pepper的getter就是 pepper(),setter方法就是pepper(T newValue)。
  2. chain,链式风格,布尔值。如果为真,产生的setter返回的this而不是void。set方法返回的是对象的实例,因此可以直接再使用set方法或者直接调用函数
  3. prefix,为get方法添加前缀

链式风格

@Wither

在这里插入图片描述

@val,@var

通过Lombok,Java也能够像JS一样使用弱类型定义变量。@val变量申明是final类型,@var变量是非final类型

@NonNull

在方法或构造函数的参数上使用@NonNull,lombok将生成一个空值检查语句。

@Cleanup

资源自动管理:安全调用你的close()方法,无需任何麻烦。

@Synchronized

类似于Synchronized关键字,但是可以隐藏同步锁。

@SneakyThrows

把checked异常转化为unchecked异常,好处是不用再往上层方法抛出。

@Getter(lazy=true)

如果getter方法计算值需要大量CPU,或计算结果占用大量内存,第一次调用getter方法时,它将一次计算一个值,然后缓存下来。

Lombok缺点

这么好的工具,那可以应用到生产环境吗?真不一定,Lombok 注解的引入一定程度上会造成源码的可读性(不是所有人都了解其使用)和完整性,而且生成环境使用,不一定还会遇到什么坑,看看其代码托管GitHub的issue就知道。
不过反过来一想,issue 开得多,不正说明其应用还是很广泛的?

参考

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

johnny233

晚饭能不能加鸡腿就靠你了

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

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

打赏作者

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

抵扣说明:

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

余额充值