Java 非空Lombok builder属性的FindBugs检测器
我有很多使用Lombok构建器的类,这些类具有Java 非空Lombok builder属性的FindBugs检测器,java,builder,findbugs,lombok,null-check,Java,Builder,Findbugs,Lombok,Null Check,我有很多使用Lombok构建器的类,这些类具有@NonNull字段 @Builder class SomeObject { @NonNull String mandatoryField1; @NonNull String mandatoryField2; Integer optionalField; ... } 但是,这使调用者可以选择在不设置mandatoryField的情况下创建对象,这将导致运行时失败 SomeObject.builder()
@NonNull
字段
@Builder
class SomeObject {
@NonNull String mandatoryField1;
@NonNull String mandatoryField2;
Integer optionalField;
...
}
但是,这使调用者可以选择在不设置mandatoryField
的情况下创建对象,这将导致运行时失败
SomeObject.builder()
.mandatoryField1("...")
// Not setting mandatoryField2
.build();
我正在寻找在构建时捕获这些错误的方法
有一些非Lombok的方法,比如StepBuilder,甚至是构造函数,可以确保始终设置必填字段,但我对使用Lombok builder实现这一点感兴趣
此外,我知道为了进行编译时检查而设计类(如步骤生成器或@allargsconstuctor
)会产生大量笨拙的代码——这就是为什么我有动机构建一个编译后FindBugs步骤来检测这些
现在,当我显式地将@NonNull
字段设置为null
时,FindBugs确实失败了:
FindBugs检测到此故障
new SomeObject().setMandatoryField1(null);
但它没有检测到这一点:
SomeObject.builder()
.mandatoryField1(null)
.build();
SomeObject.builder()
.mandatoryField1("...")
//.mandatoryField2("...") Not setting it at all.
.build();
它也没有检测到这一点:
SomeObject.builder()
.mandatoryField1(null)
.build();
SomeObject.builder()
.mandatoryField1("...")
//.mandatoryField2("...") Not setting it at all.
.build();
这似乎是因为戴着假面具的建筑工人看起来像
public static class SomeObjectBuilder {
private String mandatoryField1;
private String mandatoryField2;
private Integer optionalField;
SomeObjectBuilder() {}
public SomeObjectBuilder mandatoryField1(final String mandatoryField1) {
this.mandatoryField1 = mandatoryField1;
return this;
}
// ... other chained setters.
public SomeObject build() {
return new SomeObject(mandatoryField1, mandatoryField2, optionalField);
}
}
我认为:
- Lombok不会向其内部字段添加任何
,也不会向非null字段添加任何null检查@NonNull
- 它不调用任何
方法,以便FindBugs捕捉这些失败SomeObject.set*
- 如果设置了
属性,是否有任何方法使用Lombok构建器时会导致构建时失败(在运行FindBugs时或其他情况下)@NonNull
- 是否有任何自定义FindBugs检测器检测这些故障
- Findbugs
- Bean验证(JSR303)
- Bean验证2.0(JSR380)
- 这似乎是个吹毛求疵的问题。。。。。。但请记住,这些都不是:
@NotNull
注释,FindBugs将不会生效,如果您没有分配任何值,这与分配null
相反。另一个差距是反思和反序列化
我理解您希望尽快验证以验证注释(如@NotNull
)表示的合同
有一种方法可以在SomeClassBuilder.build()
(仍然是运行时!)上执行此操作,但它有点复杂,需要创建自定义生成器:
也许它可以成为通用的,以适应许多类-somoeone请编辑
@Builder
类对象{
@非空字符串mandatoryField1;
@非空字符串mandatoryField2;
整数域;
...
公共静态SomeObjectBuilder(){//Lombok的类名约定
返回新的CustomBuilder();
}
公共静态类CustomBuilder扩展了SomeObjectBuilder{
私有静态ValidationFactory vf=Validation.buildDefaultValidationFactory();
私有验证器Validator=vf.getValidator();
@过度
公共对象生成(){
SomeObject result=super.build();
验证对象(结果);
返回结果;
}
私有void validateObject(对象对象){
//如果对象为null,则抛出新的IllegalArgException或ValidationException
设置冲突=validator.validate(对象);
如果(冲突.size()>0){
//遍历冲突,每个冲突都有getMessage(),getPropertyPath()
//-建立详细的异常消息,列出所有违规行为
[...]
抛出新的ValidationException(messageWithAllViolations)}
}
}
Lombok在生成@allargsconstuctor
时会考虑这些@NonNull
注释。这也适用于由@Builder
生成的构造函数。在您的示例中,这是构造函数的delomboked代码:
SomeObject(@NonNull final String mandatoryField1, @NonNull final String mandatoryField2, final Integer optionalField) {
if (mandatoryField1 == null) {
throw new java.lang.NullPointerException("mandatoryField1 is marked @NonNull but is null");
}
if (mandatoryField2 == null) {
throw new java.lang.NullPointerException("mandatoryField2 is marked @NonNull but is null");
}
this.mandatoryField1 = mandatoryField1;
this.mandatoryField2 = mandatoryField2;
this.optionalField = optionalField;
}
因此,FindBugs在理论上可以找到问题,因为构造函数中存在空检查,在您的示例中,稍后将使用null
值调用它。然而,FindBugs可能还没有强大到可以这样做(现在还?),我不知道有任何自定义检测器能够做到这一点
问题在于龙目岛为什么不将这些检查添加到生成器的SETTER方法中(这会使Funbbug更容易发现问题)。这是因为使用一个生成器实例仍然是完全合法的,它仍然有<代码> @非null < /COD>字段设置为<代码> null < /C>。考虑以下用例:
例如,您可以使用toBuilder()
方法从实例创建一个新的生成器,然后通过调用mandatoryField1(null)
删除其中一个必填字段(可能是因为您希望避免泄漏实例值)。然后您可以将其传递给其他方法,让其重新填充必填字段。因此,lombok不会也不应该将这些空检查添加到生成的生成器的不同setter方法中。(当然,可以扩展lombok,以便用户可以“选择加入”要生成更多的空检查,请参阅。但是,这取决于lombok维护人员。)
TLDR:这个问题可以从理论上找到,但FindBugs不够强大。在oth上