Java 开发工具–Lombok介绍
概述
在写POJO代码时,经常需要生成getter和setter方法,以及hashcode、tostring和equals等代码。感觉甚是麻烦,虽然有Eclipse或者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>
- 1
- 2
- 3
- 4
- 5
Gradle项目的引入方式类似:
provided group: 'org.projectlombok', name: 'lombok', version: '1.16.18'
- 1
在spring boot项目中,引入Lombok时没有必要指定<version>和<scope>
,因为 spring boot已经自动配置好。
如何安装
先说IDEA,在IDEA中安装Lombok需要先安装插件,快捷键 Ctrl + Alt + S,然后选择
plugins——> browse repositories,搜索 Lombok,install即可,安装需要重启。
其次,Lombok是在编译时期,自动生成所需方法的,所以需要开启annotation
processing;依旧是刚才的快捷键,可以搜索"annotation processing",然后选择"Enable annotation
processing".
如果是Eclipse的话,官网下载最新的Lombok.jar,双击或者java
-jar lombok.jar
在弹出的窗口,确认刚才 Lombok
扫描整个硬盘得到的Eclipse安装程序结果即可(Eclipse,MyEclipse,STS(Spring tool suite)),点击
Install/Update。
注解
不完全统计:
@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,当前类;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
实例代码
@Getter & @Setter & @ToString
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;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
如果使用Lombok提供的注解:
@ToString
class Person {
@Getter
@Setter
private boolean employed;
@Getter
@Setter(AccessLevel.PROTECTED)
private String name;
public String say() {
return getName();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
@Builder
这次先看看使用注解之前的java code:
@Builder
public class BuilderExample {
private String name;
private int age;
}
- 1
- 2
- 3
- 4
- 5
再看看其经过编译之后的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 + ")";
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
@Data
先看使用注解的类:
@Data
public class User {
private Integer id;
private String password;
}
- 1
- 2
- 3
- 4
- 5
编译之后的类长这样:
public class User {
private Integer id;
private String password;
public User() {
}
public Integer getId() {
return this.id;
}
public String getPassword() {
return this.password;
}
public void setId(Integer id) {
this.id = id;
}
public void setPassword(String password) {
this.password = password;
}
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;
}
Object this$password = this.getPassword();
Object other$password = other.getPassword();
if (this$password == null) {
if (other$password != null) {
return false;
}
} else if (!this$password.equals(other$password)) {
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());
Object $password = this.getPassword();
result = result * 59 + ($password == null ? 43 : $password.hashCode());
return result;
}
public String toString() {
return "User(id=" + this.getId() + ", password=" + this.getPassword() + ")";
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
更多注解的用法,请参考官网。
lombok.config
config官方文档
查看当前版本的lombok所支持的配置项使用命令工具configjava
-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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
要求:lombok 1.14+
原理
采用JSR269所提出的插入式注解处理(Pluggable Annotation
Processing),并结合动态代码生成技术所开发的。如下图所示:
图示为
javac 的编译过程,java文件首先通过进行解析构建出一个AST,然后执行注解处理,最后经过分析优化生成二进制的.class文件。
Annotation
Processing 是在解析和生成之间的一个步骤。
上图是
Lombok 处理流程,在Javac 解析成抽象语法树之后(AST),Lombok 根据自己的注解处理器,动态的修改
AST,增加新的节点(所谓代码),最终通过分析和生成字节码。
其他注解
Wither
val
无忧的final本地变量
NonNull
Cleanup
自动的资源管理:安全调用你的close() 方法,无需任何麻烦。
Lombok缺点
这么好的工具,那可以应用到生产环境吗?真不一定,Lombok
注解的引入一定程度上会造成源码的可读性(不是所有人都了解其使用)和完整性,而且生成环境使用,不一定还会遇到什么坑,看看其代码托管GitHub的issue就知道。
不过反过来一想,issue
开得多,不正说明其应用还是很广泛的?