Joh*_*pit 13 java findbugs builder lombok null-check
我有很多@NonNull使用Lombok构建器的字段.
@Builder
class SomeObject {
@NonNull String mandatoryField1;
@NonNull String mandatoryField2;
Integer optionalField;
...
}
Run Code Online (Sandbox Code Playgroud)
但是,这为调用者提供了在不设置a的情况下创建对象的选项mandatoryField,在使用时会导致运行时失败.
SomeObject.builder()
.mandatoryField1("...")
// Not setting mandatoryField2
.build();
Run Code Online (Sandbox Code Playgroud)
我正在寻找在构建时捕获这些错误的方法.
有一些非Lombok方式,如StepBuilders,甚至是构造函数,以确保始终设置必填字段,但我对使用Lombok构建器实现此目的的方式感兴趣.
另外,我理解@AllArgsConstructor为了进行编译时检查而设计类(比如一个步骤构建器或者一个)会产生很多笨拙的代码 - 这就是为什么我有动力构建一个后编译的FindBugs步骤来检测这些.
现在,当我明确地将@NonNull字段设置为时,FindBugs确实失败了null:
FindBugs检测到此失败,
new SomeObject().setMandatoryField1(null);
Run Code Online (Sandbox Code Playgroud)
但它没有检测到这个:
SomeObject.builder()
.mandatoryField1(null)
.build();
Run Code Online (Sandbox Code Playgroud)
它也没有发现这个:
SomeObject.builder()
.mandatoryField1("...")
//.mandatoryField2("...") Not setting it at all.
.build();
Run Code Online (Sandbox Code Playgroud)
这似乎正在发生,因为Delomboked构建器看起来像,
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);
}
}
Run Code Online (Sandbox Code Playgroud)
我观察到:
@NonNull向其内部字段添加任何内容,也不会向非空字段添加任何空值检查.SomeObject.set*方法,因为FindBugs可以捕获这些故障.我有以下问题:
@NonNull设置了属性?Lombok @NonNull在生成时会考虑这些注释@AllArgsConstructor.这也适用于生成的构造函数@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;
}
Run Code Online (Sandbox Code Playgroud)
因此,FindBugs理论上可以找到问题,因为null检查存在于构造函数中,稍后null在您的示例中使用值调用.但是,FindBugs可能不够强大(但是?),我不知道任何能够做到这一点的自定义探测器.
问题仍然是为什么lombok不会将这些检查添加到构建器的setter方法(这将使FindBugs更容易发现问题).这是因为使用仍@NonNull设置了字段的构建器实例是完全合法的null.考虑以下用例:
例如,您可以使用该toBuilder()方法从实例创建新构建器,然后通过调用删除其中一个必需字段mandatoryField1(null)(可能是因为您希望避免泄漏实例值).然后你可以将它传递给其他方法让它重新填充必填字段.因此,lombok 不会也不应该将这些空检查添加到生成的构建器的不同setter方法中.(当然,lombok可以扩展,以便用户可以"选择加入"生成更多的空检查;请参阅GitHub上的讨论.但是,这个决定取决于lombok维护者.)
TLDR:问题可以从理论上找到,但FindBugs还不够强大.另一方面,lombok不应该添加进一步的空检查,因为它会破坏合法的用例.
发生在编译时,这在本次讨论中非常重要。
Bean 验证发生在运行时,因此需要在代码中显式调用,或者托管环境通过创建和调用验证器隐式执行此操作(如Spring或JavaEE)。
FindBugs是一个静态字节码分析器,因此发生在编译后。它使用巧妙的启发式方法,但它不执行代码,因此不是 100% 无懈可击。在您的情况下,它仅在浅层情况下遵循可空性检查,并且错过了构建器。
另请注意,通过手动创建构建器并添加必要的@NotNull注释,如果您没有分配任何值, FindBugs将不会启动,这与分配null. 另一个差距是反射和反序列化。
@NotNull我了解您希望尽快验证验证注释(如 )中表达的合同。
有一种方法可以做到这一点SomeClassBuilder.build()(仍然是运行时!),但它有点复杂并且需要创建自定义构建器:
也许它可以变得通用以适应许多类 - somoeone 请编辑!
@Builder
class SomeObject {
@NonNull String mandatoryField1;
@NonNull String mandatoryField2;
Integer optionalField;
...
public static SomeObjectBuilder builder() { //class name convention by Lombok
return new CustomBuilder();
}
public static class CustomBuilder extends SomeObjectBuilder {
private static ValidationFactory vf = Validation.buildDefaultValidationFactory();
private Validator validator = vf.getValidator();
@Overrride
public SomeObject build() {
SomeObject result = super.build();
validateObject(result);
return result;
}
private void validateObject(Object object) {
//if object is null throw new IllegalArgException or ValidationException
Set<ConstraintVioletion<Object>> violations = validator.validate(object);
if (violations.size() > 0) {
//iterate through violations and each one has getMessage(), getPropertyPath()
// - to build up detailed exception message listing all violations
[...]
throw new ValidationException(messageWithAllViolations) }
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
693 次 |
| 最近记录: |