在互联网行业中,基于Java开发的业务类系统,不管是服务端还是客户端,业务逻辑代码的更新往往是非常频繁的,这源于功能的快速迭代特性。在一般公司内部,特别是使用 Java web 技术构建的平台中,不管是基于模块化还是服务化的,业务逻辑都会相对复杂。 这些系统之间、系统内部往往存在大量的API接口,这些接口一般都需要对入参(输入参数的简称)做校验,以保证: 1) 核心业务逻辑能够顺利按照预期执行。 2) 数据能够正常存取。 3) 数据安全性。包括符合约束以及限制,有访问权限控制以及不出现SQL注入等问题。 开发人员在维护核心业务逻辑的同时,还需要为输入做严格的校验。当输入不合法时,能够给caller一个明确的反馈,最常见的反馈就是返回封装了result的对象或者抛出exception。 一些常见的验证代码片段如下所示:
public Response execute(Request request) {
if (request == null) {
throw BizException();
}
List cars = request.getCars();
if (CollectionUtils.isEmpty(cars)) {
throw BizException();
}
for (Car car : cars) {
if (car.getSeatCount() < 2) {
throw BizException();
}
}
// do core business logic
}我们可以发现,它不够优雅而且违反一些范式: 1)违反单一职责原则(Single responsibility)。核心业务逻辑( core business logic)和验证逻辑(validation logic)耦合在一个类中。 2)开闭原则(Open/closed)。我们应该对扩展开放,对修改封闭,验证逻辑不好扩展,而且一旦需要修改需要动整体这个类。 3)DRY原则(Don’t repeat yourself)。代码冗余,相同逻辑可能散落多处,长此以往不好收殓。
FluentValidato是一个适用于以Java语言开发的程序,让开发人员回归focus到业务逻辑上,使用流式(Fluent Interface)调用风格让验证跑起来很优雅,同时验证器(Validator)可以做到开闭原则,实现最大程度的复用的工具库。
1) 验证逻辑与业务逻辑不再耦合 摒弃原来不规范的验证逻辑散落的现象。 2) 校验器各司其职,好维护,可复用,可扩展 一个校验器(Validator)只负责某个属性或者对象的校验,可以做到职责单一,易于维护,并且可复用。 3) 流式风格(Fluent Interface)调用 4) 使用注解方式验证 可以装饰在属性上,减少硬编码量。 5) 支持JSR 303 – Bean Validation标准 或许你已经使用了Hibernate Validator,不用抛弃它,FluentValidator可以站在巨人的肩膀上。 6) Spring良好集成 校验器可以由Spring IoC容器托管。校验入参可以直接使用注解,配置好拦截器,核心业务逻辑完全没有验证逻辑的影子,干净利落。 7) 回调给予你充分的自由度 验证过程中发生的错误、异常,验证结果的返回,开发人员都可以定制。
<dependency>
<groupId>com.baidu.unbiz</groupId>
<artifactId>fluent-validator</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
<version>1.0.5</version>
</dependency>从广义角度来说DTO( Data Transfer Object)、VO(Value Object)、BO(Business Object)、POJO等都可以看做是业务表达模型。 创建一个学生类,包含 name(姓名)、age(年龄)、schoolName(学校名称)、(area)地区
package com.example.fluentvalidator;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @author :jianyul
* @date : 2022/5/16 18:00
*/
@Data
@AllArgsConstructor
public class StudentDto {
private String name;
private Integer age;
private String schoolName;
private String area;
}
针对schoolName(学校名称)创建一个Validator,代码如下:
public class SchoolNameValidator extends ValidatorHandler<String> implements Validator<String> {
@Override
public boolean validate(ValidatorContext context, String schoolName) {
if (!"无锡中学".equals(schoolName)) {
context.addErrorMsg("学校名称不正确");
return false;
}
return true;
}
}很简单,实现Validator接口,泛型T规范这个校验器待验证的对象的类型,继承ValidatorHandler可以避免实现一些默认的方法,validate()方法第一个参数是整个校验过程的上下文,第二个参数是待验证对象,也就是学校名称。 验证逻辑:假设学校名称必须是无锡中学,否则通过context放入错误消息并且返回false,成功返回true。
StudentDto studentDto = new StudentDto("张三", 18, "苏州中学", "无锡");
Result result =
FluentValidator.checkAll()
.on(studentDto.getSchoolName(), new SchoolNameValidator())
.doValidate()
.result(toSimple());
System.out.println(result);
//打印结果:Result{isSuccess=false, errors=[学校名称不正确]}首先我们通过FluentValidator.checkAll()获取了一个FluentValidator实例,紧接着调用了failFast()表示有错了立即返回,它的反义词是failOver,然后,、on()操作表示在指定属性上使用对应校验器进行校验,截止到此,真正的校验还并没有做,这就是所谓的“惰性求值(Lazy valuation)”,有点像Java8 Stream API中的filter()、map()方法,直到doValidate()验证才真正执行了,最后我们需要收殓出来一个结果供caller获
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删