摘要:Java 语言增强库,简化
POJOs实体类封装。通过为实体类添加注解、来自动生成(并代替)通用方法;减少冗余代码、提升开发效率。
目录
[TOC]
Lombok
Lombok (音lao母报ke)插件:Java 语言增强库,简化 POJOs 实体类封装。通过为实体类添加注解、来自动生成(并代替)通用方法;减少冗余代码、提升开发效率。
- 用途:一般用于简单、属性比较多的
POJOs(子)实体类、DTO数据传输对象、请求参数 ReqVO 或返回结果实体类 RespVO。 - 缺点:提高阅读源码的门槛,继承父类时
equals()可能会出错。
原理:在编译器编译时通过操作 AST(抽象语法树)改变字节码生成。即改变编写源码的方式,是编译时的特性,不像 Spring 的依赖注入一样是运行时的特性。
- 在项目的根目录有
lombok.config(opens new window)全局配置文件,开启链式调用、生成的 toString/hashcode/equals 方法需要调用父方法。
@Data、@Value
@Data:用在类上,相当于同时使用@Getter/、@Setter@ToString、@EqualsAndHashCode、这5个注解的组合,生成通常与简单@RequiredArgsConstructorPOJO关联的所有样板和bean;- 为非
final修饰的属性,添加@Setter注解的效果。 - 为
final修饰的属性,添加@RequiredArgsConstructor注解的效果。 - 因为包含
@EqualsAndHashcode()所以也有相同的问题。
- 为非
不过要注意,如果使用
@Data注解的类,继承成了其它父类的属性,最好额外添加@ToString(callSuper = true)和@EqualsAndHashCode(callSuper = true)注解。
- 因为默认情况下,
@Data注解不会处理父类的属性。所以需要我们通过callSuper = true属性,声明需要调用父类对应的方法。- 这个情况非常常见,例如说在实体类中,可能会声明一个抽象父类 AbstractEntity,而该类里有一个
id编号属性。
@Value:用在类上,和@Data类似,区别在于所有成员变量默认定义为private final修饰,且不生成setter()方法;- 与 Spring 中用于绑定
@Configuration配置的注解@Value重名,注意 import 路径;
- 与 Spring 中用于绑定
@EqualsAndHashCode
@EqualsAndHashCode(callSuper=false):用在类上,自动生成 equals() 方法和 hashCode() 方法,包括所有非静态变量和非 transient 的变量。
- 问题:默认
callSuper=false。默认仅使用子类中定义的属性、不调用父类的方法,可能会导致比较对象时出错;- 如,有多个类有相同的属性(如 id),把它们提取到父类中作为共同属性;
- 因为子类仅比较当前对象的属性、而不比较父类中的
id,所以子类对象各属性相等时其父类id可能并不相同。 - lombok 自动生成的
equals()和hashCode()方法返回 true,判定为相等,但实际上并不相等,从而导致出错。
- 修复方法:
- 在使用
@Data的同时,加上@EqualsAndHashcode(callSuper=true),覆盖@Data中的,设置让其生成的方法中调用父类方法。 - 可用
@Getter @Setter @ToString代替@Data、并自定义equals(Object other)和hashCode()方法;比如有些类只需要判断主键id是否相等即可。
- 在使用
- 另外,Java 规定,重写
equals()必须重写hashCode()方法; @EqualsAndHashCode.Include和@EqualsAndHashCode(onlyExplicitlyIncluded = true)来精确指定要使用的字段或方法。
1 | |
@Getter/@Setter、@ToString
@Getter/@Setter:用在类/属性上;- 若字段的类型为
boolean,则为isXxx(); - 用在类上时,默认
@Setter(AccessLevel.PUBLIC),指定访问级别有PUBLIC, PROTECTED, PACKAGE, PRIVATE, NONE。 @Getter(lazy=true):可替代经典的Double Check Lock样板代码。
- 若字段的类型为
@ToString:用在类上,自动覆写toString()方法,默认打印所有非静态字段;更常用@Override重写toString()方法的方式实现;- 类上用
@ToString(exclude=”id”)、字段上用@ToString.Exclude:用于排除 id 属性;@ToString(onlyExplicitlyIncluded = true),配合用@ToString.Include在字段上标记要包含的每个字段;@ToString(callSuper=true, includeFieldNames=true):调用父类的toString()方法,将父类实现的输出包含到当前输出中,包含所有属性。
@NonNull
@NonNull:添加在方法参数、类属性上,用于自动生成null参数检查。若确实是null时,抛出 NullPointerException 异常。

构造器
@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor:用在类上,为类自动生成对应参数的构造方法。
- 指定
staticName = "of"参数:生成返回类对象的静态工厂方法; @NoArgsConstructor:生成无参构造函数。- 如果字段由 final 修饰,则将导致编译器错误,除非使用
@NoArgsConstructor(force = true),否则所有 final 字段都将初始化为0 / false / null; - 对于具有约束(如
@NonNull)的字段,不会生成任何检查。
- 如果字段由 final 修饰,则将导致编译器错误,除非使用
@RequiredArgsConstructor:为每个需特殊处理的字段生成有1个参数的构造函数。- 所有未初始化的 final 字段;
- 所有未声明其位置的、未标记为
@NonNull的字段。
@AllArgsConstructor:为每个字段生成有1个参数的构造函数。标有@NonNull的字段将对这些参数进行非空检查。
@Builder
见设计模式文档。
@Builder:只能标注到类上,给该类加个构造者模式 Builder 内部类。
- 生成类当前流程的一种链式(流式)构造工厂,多用于配置类。如:
1 | |
还可以方便的实现建造者模式。
@Builder 注解:为类生成相对略微复杂的构建器 API。
- 作用于类,将其变成建造者模式
- 会生成一个全参的构造函数
- 可以以链的形式调用
- 初始化实例对象生成的对象是不可以变的,可以在创建对象的时候进行赋值
- 如果需要在原来的基础上修改可以加 set 方法,final 字段可以不需要初始化
@Builder 内部做了什么
- 创建一个名为 ThisClassBuilder 的内部静态类,并具有和实体类相同的属性(称为构建器)
- 在构建器中:对于目标类中的所有的属性和未初始化的 final 字段,都会在构建器中创建对应属性
- 在构建器中:创建一个无参的 default 构造函数
- 在构建器中:实体类中的每个参数,都会对应创建类似于 setter 的方法,方法名与该参数名相同。 并且返回值是构建器本身(便于链式调用)
- 在构建器中:会创建一个 build 方法,调用 build 方法,就会根据设置的值进行创建实体对象
- 在构建器中:会生成一个 toString 方法
- 在实体类中:会创建一个 builder 方法,目的是用来创建构建器
属性介绍
-
@Builder.Default:非 final 的字段可以有默认值 -
builderMethodName:指定创建内部静态类的方法名,默认值为 builder -
buildMethodName:指定创建实体类的方法名,默认值为 build -
builderClassName:指定内部静态的类名,默认值为 “”,默认创建的类名为 thisclassBuilder -
toBuilder:设置为 true 可以对这个对象进行拷贝生成新的对象,可以再修改,默认为 false access:设置 builderMethodName 的访问权限修饰符,默认为 public。- 共有 PUBLIC、MODULE、PROTECTED、PACKAGE、PRIVATE,其中 MODULE 是 Java 9 的新特性
setterPrefix:设置 setter 方法的前缀,默认为 “”
用法
只需要定义一个静态公共的内部类即可。
如果项目中有使用lombok的话,可以直接使用@Builder注解来实现。
1 | |
如何使用:
1 | |
没有父类的属性
问题:子类的Builder对象没有父类的属性。
- 对于父类,使用
@AllArgsConstructor注解。 - 对于子类,手动编写全参数构造器,内部调用父类全参数构造器,在子类全参数构造器上使用@Builder注解。
通过这种方式,子类Builder对象可以使用父类的所有私有属性。
但也有两个副作用:
- 因为使用
@AllArgsConstructor注解,父类构造函数字段的顺序由声明字段的顺序决定,如果子类构造函数传参的时候顺序不一致,字段类型还一样的话,出了错不好发现。 - 如果父类字段有增减,所有子类的构造器都要修改。
日志注解
见整合 ELK 统一日志框架文档
@CommonsLog、@Flogger、@Log、@JBossLog、@Log4j、@Log4j2、@Slf4j、@Slf4jX 注解,添加在类上,自动为类添加对应的日志支持。
其它注解
-
:添加在方法中的局部变量上,在作用域结束时会自动调用@Cleanup#close()方法,来释放资源。例如说,使用在 Java IO 流操作的时候。- 自动管理资源,用在局部变量前,在当前变量范围内即将执行完毕退出前自动清理资源,生成
try-finally这样的代码来关闭流;用于确保已分配的资源被释放(IO的连接关闭)。
- 自动管理资源,用在局部变量前,在当前变量范围内即将执行完毕退出前自动清理资源,生成
@SneakyThrows:添加在方法上,给该方法添加try catch代码块。自动抛出受检异常,而无需显式在方法上使用throws语句;-
@Synchronized:用在方法上,添加同步锁。-
将方法声明为同步的,并自动加锁;自动添加到同步机制,生成的代码并不是直接锁方法,而是锁代码块。
-
而锁对象是一个私有的属性
$lock或$LOCK,而synchronized关键字锁对象是this,锁在this或自己的类对象上存在副作用,不能阻止非受控代码去锁this或类对象,这可能会导致竞争条件或其它线程错误;
-
@Accessors:添加在方法或属性上,并设置chain = true,实现链式编程。用于配置lombok如何生成和查找getter和setter。- 对
getter和setter的bean命名规范;- 参数
chain=true时,类的所有属性的setter方法返回值为this,支持链式写法。
- 参数