首页 » PHP教程 » fluentphp技巧_Java 的营业逻辑验证框架 之fluentvalidator

fluentphp技巧_Java 的营业逻辑验证框架 之fluentvalidator

duote123 2024-11-19 0

扫一扫用手机浏览

文章目录 [+]

一些常见的验证代码片段如下所示:

<pre class="prettyprint hljs php" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">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}</pre>

我们不能说这是反模式(anti-pattern),但是从中我们可以创造,它不足优雅而且违反一些范式:

fluentphp技巧_Java 的营业逻辑验证框架 之fluentvalidator

违反单一职责原则(Single responsibility)。
核心业务逻辑(core business logic)和验证逻辑(validation logic)耦合在一个类中。
-开闭原则(Open/closed)。
我们该当对扩展开放,对修正封闭,验证逻辑不好扩展,而且一旦须要修正须要动整体这个类。
DRY原则(Don’t repeat yourself)。
代码冗余,相同逻辑可能散落多处,长此以往不好收殓。
为何要利用 FluentValidator

缘故原由很大略,第一为了优雅,出色的程序员都有点洁癖,都希望让验证看起来很舒畅;第二,为了尽最大可能符合这些精良的原则,做 clean code。
FluentValidator 便是这么一个工具类库,适用于以Java措辞开拓的程序,让开发职员回归 focus 到业务逻辑上,利用流式(Fluent Interface)调用风格让验证跑起来很优雅,同时验证器(Validator)可以做到开闭原则,实现最大程度的复用。

fluentphp技巧_Java 的营业逻辑验证框架 之fluentvalidator
(图片来自网络侵删)
FluentValidator 特点

这里算是Quick learn理解下,也当且看做打广告吧,看了这些特点,希望能给你往下连续阅读的兴趣:)

验证逻辑与业务逻辑不再耦合 摒弃原来不规范的验证逻辑散落的征象。
验器各司其职,好掩护,可复用,可扩展。
一个校验器(Validator)只卖力某个属性或者工具的校验,可以做到职责单一,易于掩护,并且可复用。
流式风格(Fluent Interface)调用 借助 [Martin 大神提倡的流式 API 风格利用“惰性求值(Lazy evaluation)”式的链式调用,类似 guava、Java8 stream API 的利用体验。
利用表明办法验证 可以装饰在属性上,减少硬编码量。
支持 JSR 303 – Bean Validation标准 或许你已经利用了 Hibernate Validator,不用抛弃它,FluentValidator 可以站在巨人的肩膀上。
Spring 良好集成 校验器可以由 Spring IoC 容器托管。
校验入参可以直策应用表明,配置好拦截器,核心业务逻辑完备没有验证逻辑的影子,干净利落。
回调给予你充分的自由度 验证过程中发生的缺点、非常,验证结果的返回,开拓职员都可以定制。
哪里可以获取到 FluentValidator

项目托管在github上,解释文档全英完成,i18n 化,同时利用 Apache2 License开源。

最新发布的 Jar 包可以在 maven 中心仓库找到,

maven 引入依赖

添加如下依赖到 maven 的 pom.xml 文件中:

<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><dependency> <groupId>com.baidu.unbiz</groupId> <artifactId>fluent-validator</artifactId> <version>1.0.5</version></dependency></pre>

注:最新 release 请及时参考 github。

上面这个 FluentValidator 是个根本核心包,只依赖于 slf4j 和 log4j,如果你利用 logback,想去掉 log4j,打消掉的方法如下所示:

<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><dependency> <groupId>com.baidu.unbiz</groupId> <artifactId>fluent-validator</artifactId> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> </exclusions></dependency></pre>开拓业务领域模型

从广义角度来说 DTO(Data Transfer Object)、VO(Value Object)、BO(Business Object)、POJO 等都可以看做是业务表达模型。

我们这里创建一个汽车类(Car)的POJO,里面定义了牌照(license plate)、座椅数(seat count)、生产商(manufacturer)。

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class Car { private String manufacturer; private String licensePlate; private int seatCount; // getter and setter...}</pre>开拓一个专职的 Validator

实际这里须要开拓三个 Validator,分别对 Car 的3个属性进行校验,这里以座椅数为例展示如何开拓一个 Validator,其他两个省略。

<pre class="prettyprint hljs scala" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class CarSeatCountValidator extends ValidatorHandler<Integer> implements Validator<Integer> { @Override public boolean validate(ValidatorContext context, Integer t) { if (t < 2) { context.addErrorMsg(String.format("Seat count is not valid, invalid value=%s", t)); return false; } return true; }}</pre>

很大略,实现 Validator 接口,泛型T规范这个校验器待验证的工具的类型,继续 ValidatorHandler 可以避免实现一些默认的方法,例如 accept() ,后面会提到, validate() 方法第一个参数是全体校验过程的高下文,第二个参数是待验证工具,也便是座椅数。

验证逻辑很大略,座椅数必须大于1,否则通过 context 放入缺点并且返回 false,成功返回 true。

开始验证吧

二话不说,直接上代码:

<pre class="prettyprint hljs cs" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Car car = getCar();Result ret = FluentValidator.checkAll() .on(car.getLicensePlate(), new CarLicensePlateValidator()) .on(car.getManufacturer(), new CarManufacturerValidator()) .on(car.getSeatCount(), new CarSeatCountValidator()) .doValidate() .result(toSimple());System.out.println(ret);</pre>

我想不用多说,如果你会英文,你就能知道这段代码是如何事情的。
这便是流式风格(Fluent Interface)调用的优雅之处,让代码更可读、更好理解。

还是轻微解释下,首先我们通过 FluentValidator.checkAll() 获取了一个 FluentValidator 实例,紧接着调用了failFast()表示有错了立即返回,它的反义词是 failOver,然后,持续串 on() 操作表示在 Car 的3个属性上依次利用3个校验器进行校验(这个过程叫做 applying constraints),截止到此,真正的校验还并没有做,这便是所谓的“惰性求值(Lazy valuation)”,有点像 Java8 Stream API中的 filter() 、 map() 方法,直到 doValidate() 验证才真正实行了,末了我们须要收殓出来一个结果供 caller 获取打印,直策应用默认供应的静态方法 toSimple() 来做一个回调函数传入 result() 方法,终极返回 Result 类,如果座椅数不合法,那么掌握台打印结果如下:

<pre class="prettyprint hljs vbscript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Result{ isSuccess=false, errors=[Seat count is not valid, invalid value=99]}</pre>深入实践Validator详解

Validator 接口定义如下:

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public interface Validator<T> { / 判断在该工具上是否接管或者须要验证 <p/> 如果返回true,那么则调用{@link #validate(ValidatorContext, Object)},否则跳过该验证器 @param context 验证高下文 @param t 待验证工具 @return 是否接管验证 / boolean accept(ValidatorContext context, T t); / 实行验证 <p/> 如果发生缺点内部须要调用{@link ValidatorContext#addErrorMsg(String)}方法,也即<code>context.addErrorMsg(String) </code>来添加缺点,该缺点会被添加到结果存根{@link Result}的缺点列表中。
@param context 验证高下文 @param t 待验证工具 @return 是否验证通过 / boolean validate(ValidatorContext context, T t); / 非常回调 <p/> 当实行{@link #accept(ValidatorContext, Object)}或者{@link #validate(ValidatorContext, Object)}发生非常时的如何处理 @param e 非常 @param context 验证高下文 @param t 待验证工具 / void onException(Exception e, ValidatorContext context, T t);}</pre>

ValidatorHandler 是实现 Validator 接口的一个模板类,如果你自己实现的 Validator 不想覆盖上面3个方法,可以继续这个 ValidatorHandler。

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class ValidatorHandler<T> implements Validator<T> { @Override public boolean accept(ValidatorContext context, T t) { return true; } @Override public boolean validate(ValidatorContext context, T t) { return true; } @Override public void onException(Exception e, ValidatorContext context, T t) { }}</pre>

内部校验逻辑发生缺点时候,有两个处理办法,

第一,大略处理,直接放入缺点。

<pre class="prettyprint hljs kotlin" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">context.addErrorMsg("Something is wrong about the car seat count!");return false;</pre>

第二,须要详细的信息,包括缺点,缺点属性/字段,缺点值,缺点码,都可以自己定义,放入缺点的方法如下, create() 方法传入(必填), setErrorCode() 方法设置缺点码(选填), setField() 设置缺点字段(选填), setInvalidValue() 设置缺点值(选填)。
当然这些信息须要 result(toComplex()) 才可以获取到,详见6.7小节。

<pre class="prettyprint hljs kotlin" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">context.addError(ValidationError.create("Something is wrong about the car seat count!").setErrorCode(100).setField("seatCount").setInvalidValue(t));return false;</pre>ValidatorChain

on() 的持续串调用实际便是构建调用链,因此天经地义可以传入一个调用链。

<pre class="prettyprint hljs cs" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">ValidatorChain chain = new ValidatorChain();List<Validator> validators = new ArrayList<Validator>();validators.add(new CarValidator());chain.setValidators(validators);Result ret = FluentValidator.checkAll().on(car, chain).doValidate().result(toSimple());</pre>

如果要验证的是一个凑集(Collection)或者数组,那么可以利用 onEach,FluentValidator 会自动为你遍历,依次 apply constraints。

<pre class="prettyprint hljs css" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">FluentValidator.checkAll() .onEach(Lists.newArrayList(new Car(), new Car()), new CarValidator());FluentValidator.checkAll() .onEach(new Car[]{ }, new CarValidator());</pre>fail fast or fail over

当涌现校验失落败时,也便是 Validator 的 validate() 方法返回了 false,那么是连续还是直接退出呢?默认为利用 failFast() 方法,直接退出,如果你想连续完成所有校验,利用 failOver() 来 skip 掉。

<pre class="prettyprint hljs css" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">FluentValidator.checkAll().failFast().on(car.getManufacturer(), new CarManufacturerValidator());FluentValidator.checkAll().failOver().on(car.getManufacturer(), new CarManufacturerValidator());</pre>

on() 后面可以紧跟一个 when() ,当 when 知足 expression 表达式 on 才启用验证,否则 skip 调用。

<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">FluentValidator.checkAll().on(car.getManufacturer(), new CarManufacturerValidator()).when(a == b)</pre>验证回调 callback

doValidate() 方法接管一个 ValidateCallback 接口,接口定义如下:

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public interface ValidateCallback { / 所有验证完成并且成功后 @param validatorElementList 验证器list / void onSuccess(ValidatorElementList validatorElementList); / 所有验证步骤结束,创造验证存在失落败后 @param validatorElementList 验证器list @param errors 验证过程中发生的缺点 / void onFail(ValidatorElementList validatorElementList, List<ValidationError> errors); / 实行验证过程中发生了非常后 @param validator 验证器 @param e 非常 @param target 正在验证的工具 @throws Exception / void onUncaughtException(Validator validator, Exception e, Object target) throws Exception;}</pre>

默认的,利用不含参数的 doValidate() 方法,FluentValidator 利用 DefaultValidateCallback,实在现如下,可以看出出错了,成功了什么也不做,有不可控非常的时候直接抛出。

<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class DefaultValidateCallback implements ValidateCallback { @Override public void onSuccess(ValidatorElementList validatorElementList) { } @Override public void onFail(ValidatorElementList validatorElementList, List<ValidationError> errors) { } @Override public void onUncaughtException(Validator validator, Exception e, Object target) throws Exception { throw e; }}</pre>

如果你不满意这种办法,例如成功的时候打印一些,一种实现办法如下:

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Result ret = FluentValidator.checkAll() .on(car.getSeatCount(), new CarSeatCountValidator()) .doValidate(new DefaulValidateCallback() { @Override public void onSuccess(ValidatorElementList validatorElementList) { LOG.info("all ok!"); } }).result(toSimple());</pre>获取结果

result()接管一个ResultCollector接口,如上面所示,toSimple()实际是个静态方法,这种办法在Java8 Stream API中很常见,默认可以利用FluentValidator自带的大略结果Result,如果须要可以利用繁芜ComplexResult,内含缺点,缺点属性/字段,缺点值,缺点码,如下所示:

ComplexResult ret = FluentValidator.checkAll().failOver()

.on(company, new CompanyCustomValidator())

.doValidate().result(toComplex());

当然,如果你想自己实现一个结果类型,完备可以定制,实现ResultCollector接口即可。

public interface ResultCollector {

/

转换为对外结果@param result 框架内部验证结果@return 对外验证结果工具/T toResult(ValidationResult result);}toSimple() 和 toComplex()方法是通过如下办法static import进来的:

import static com.baidu.unbiz.fluentvalidator.ResultCollectors.toSimple;

import static com.baidu.unbiz.fluentvalidator.ResultCollectors. toComplex;

6.8 关于RuntimeValidateException

如果验证中发生了一些不可控非常,例如数据库调用失落败,RPC连接失落效等,会抛出一些非常,如果Validator没有try-catch处理,FluentValidator会将这些非常封装在RuntimeValidateException,然后再re-throw出去,这个情形你该当知晓并作出你认为最精确的处理。

6.9 context高下文共享

通过putAttribute2Context()方法,可以往FluentValidator注入一些键值对,在所有Validator中共享,有时候这相称有用。

例如下面展示了在caller添加一个ignoreManufacturer属性,然后再Validator中拿到这个值的过程。

FluentValidator.checkAll()

.putAttribute2Context(“ignoreManufacturer”, true)

.on(car.getManufacturer(), new CarManufacturerValidator())

.doValidate().result(toSimple());

public class CarManufacturerValidator extends ValidatorHandler implements Validator {

<pre class="prettyprint hljs kotlin" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@Overridepublic boolean validate(ValidatorContext context, String t) { Boolean ignoreManufacturer = context.getAttribute("ignoreManufacturer", Boolean.class); if (ignoreManufacturer != null && ignoreManufacturer) { return true; } // ...}</pre>

6.10 闭包

通过putClosure2Context()方法,可以往FluentValidator注入一个闭包,这个闭包的浸染是在Validator内部可以调用,并且缓存结果到Closure中,这样caller在上层可以获取这个结果。

范例的运用处景是,当须要频繁调用一个RPC的时候,每每该实行线程内部一次调用就够了,多次调用会影响性能,我们就可以缓存住这个结果,在所有Validator间和caller中共享。

下面展示了在caller处存在一个manufacturerService,它如果须要RPC才能获取所有生产商,显然是很耗时的,可以在validator中调用,然后validator内部共享的同时,caller可以利用闭包拿到结果,用于后续的业务逻辑。

Closure<List> closure = new ClosureHandler<List>() {

<pre class="prettyprint hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">private List<String> allManufacturers;@Overridepublic List<String> getResult() { return allManufacturers;}@Overridepublic void doExecute(Object... input) { allManufacturers = manufacturerService.getAllManufacturers();}</pre>

};

Result ret = FluentValidator.checkAll()

.putClosure2Context(“manufacturerClosure”, closure)

.on(car, validator)

.doValidate().result(toSimple());

public class CarValidator extends ValidatorHandler implements Validator {

<pre class="prettyprint hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@Overridepublic boolean validate(ValidatorContext context, Car car) { Closure<List<String>> closure = context.getClosure("manufacturerClosure"); List<String> manufacturers = closure.executeAndGetResult(); if (!manufacturers.contains(car.getManufacturer())) { context.addErrorMsg(String.format(CarError.MANUFACTURER_ERROR.msg(), car.getManufacturer())); return false; } return true;}</pre>高等玩法与JSR303规范最佳实现Hibernate Validator集成

Hibernate Validator是JSR 303 – Bean Validation规范的一个最佳的实现类库,他仅仅是jboss家族的一员,和大名鼎鼎的Hibernate ORM是系出同门,属于远房亲戚关系。
很多框架都会天然集成这个精良类库,例如Spring MVC的@Valid表明可以为Controller方法上的参数做校验。

FluentValidator当然不会重复早轮子,这么好的类库,一定要利用站在巨人肩膀上的策略,将它集成进来。

想要理解更多Hibernate Validator用法,参考这个链接。

下面的例子展示了 @NotEmpty, @Pattern, @NotNull, @Size, @Length 和 @Valid 这些表明装饰一个公司类(Company)以及其成员变量(Department)。

<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class Company { @NotEmpty @Pattern(regexp = "[0-9a-zA-Z\4e00-\u9fa5]+") private String name; @NotNull(message = "The establishTime must not be null") private Date establishTime; @NotNull @Size(min = 0, max = 10) @Valid private List<Department> departmentList; // getter and setter...}public class Department { @NotNull private Integer id; @Length(max = 30) private String name; // getter and setter...}</pre>

我们要在这个 Company 类上做校验,首先引入如下的jar包到pom.xml中。

<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><dependency> <groupId>com.baidu.unbiz</groupId> <artifactId>fluent-validator-jsr303</artifactId> <version>1.0.5</version></dependency></pre>

默认依赖于 Hibernate Validator 5.2.1.Final 版本。

然后我们利用 HibernateSupportedValidator 这个验证器来在 company 上做校验,它和普通的 Validator 没任何差异。

<pre class="prettyprint hljs cs" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Result ret = FluentValidator.checkAll() .on(company, new HibernateSupportedValidator<Company>().setValidator(validator)) .on(company, new CompanyCustomValidator()) .doValidate().result(toSimple());System.out.println(ret);</pre>

把稳这里的HibernateSupportedValidator依赖于javax.validation.Validator的实现,也便是Hibernate Validator,否则它将不会正常事情。

下面是Hibernate Validator官方供应的初始化javax.validation.Validator实现的方法,供参考利用。

<pre class="prettyprint hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Locale.setDefault(Locale.ENGLISH); // speicify languageValidatorFactory factory = Validation.buildDefaultValidatorFactory();javax.validation.Validator validator = factory.getValidator();</pre>表明验证

一贯以来先容的办法都是通过显示的API调用来进行验证,FluentValidator同样供应简洁的基于表明配置的办法来达到同样的效果。

@FluentValidate 可以装饰在属性上,内部吸收一个 Class[] 数组参数,这些个classes必须是Validator的子类,这叫表明在某个属性上依次用这些Validator做验证。
举例如下,我们改造下Car这个类:

<pre class="prettyprint hljs groovy" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class Car { @FluentValidate({ CarManufacturerValidator.class}) private String manufacturer; @FluentValidate({ CarLicensePlateValidator.class}) private String licensePlate; @FluentValidate({ CarSeatCountValidator.class}) private int seatCount; // getter and setter ... }</pre>

然后还是利用on()或者onEach()方法来校验,这里只不过不用传入Validator或者ValidatorChain了。

<pre class="prettyprint hljs nginx" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Car car = getCar();Result ret = FluentValidator.checkAll().configure(new SimpleRegistry()) .on(car) .doValidate() .result(toSimple());</pre>

那么问题来了?不要问我挖掘机(Validator)哪家强,先来说挖掘(Validator)从哪来。

默认的,FluentValidator利用SimpleRegistry,它会考试测验从当前的class loader中调用 Class.newInstance() 方法来新建一个Validator,详细利用示例如下,默认的你不须要利用 configure(new SimpleRegistry() )。

一样平常情形下,SimpleRegistry都够用了。
除非你的验证器须要Spring IoC容器管理的bean注入,那么你干脆可以把Validator也用Spring托管,利用@Service或者@Component表明在Validator类上就可以做到,如下所示:

<pre class="prettyprint hljs scala" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@Componentpublic class CarSeatCountValidator extends ValidatorHandler<Integer> implements Validator<Integer> { // ...}</pre>

这时候,须要利用SpringApplicationContextRegistry,它的浸染便是去Spring的容器中探求Validator。
想要利用Spring增强功能,将下面的maven依赖加入到pom.xml中。

<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><dependency> <groupId>com.baidu.unbiz</groupId> <artifactId>fluent-validator-spring</artifactId> <version>1.0.5</version></dependency></pre>

依赖树如下,请自觉打消或者改换不想要的版本。

<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">[INFO] +- org.springframework:spring-context-support:jar:4.1.6.RELEASE:compile[INFO] | +- org.springframework:spring-beans:jar:4.1.6.RELEASE:compile[INFO] | +- org.springframework:spring-context:jar:4.1.6.RELEASE:compile[INFO] | | +- org.springframework:spring-aop:jar:4.1.6.RELEASE:compile[INFO] | | | \- aopalliance:aopalliance:jar:1.0:compile[INFO] | | \- org.springframework:spring-expression:jar:4.1.6.RELEASE:compile[INFO] | \- org.springframework:spring-core:jar:4.1.6.RELEASE:compile[INFO] +- org.slf4j:slf4j-api:jar:1.7.7:compile[INFO] +- org.slf4j:slf4j-log4j12:jar:1.7.7:compile[INFO] | \- log4j:log4j:jar:1.2.17:compile</pre>

让Spring容器去管理SpringApplicationContextRegistry,由于这个类须要实现ApplicationContextAware接口,以是Spring会帮助我们把context注入到里面,我们也就可以随意找bean了。

调用的例子省略,原来怎么校验现在还怎么来。

当利用表明验证时候,会碰着这样的情形,某些时候例如添加操作,我们会验证A/B/C三个属性,而修正操作,我们须要验证B/C/D/E 4个属性,显示调用FluentValidator API的情形下,我们可以做到“想验证什么,就验证什么”。

@FluentValidate 表明其余一个接管的参数是groups,里面也是 Class[] 数组,只不过这个Class可以是开拓职员随意写的一个大略的类,不含有任何属性方法都可以,例如:

<pre class="prettyprint hljs groovy" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class Add { }public class Car { @FluentValidate(value = { CarManufacturerValidator.class}, groups = { Add.class}) private String manufacturer;}</pre>

那么验证的时候,只须要在 checkAll() 方法中传入想要验证的 group,就只会做选择性的分组验证,例如下面例子,只有生产商会被验证。

<pre class="prettyprint hljs vbscript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Result ret = FluentValidator.checkAll(new Class<?>[] { Add.class}) .on(car) .doValidate() .result(toSimple());</pre>

这种groups的方法同样适用于hibernate validator,想理解更多请参考官方文档。

级联工具图

级联验证(cascade validation),也叫做工具图(object graphs),指一个类嵌套其余一个类的时候做的验证。

如下例所示,我们在车库(Garage)类中含有一个汽车列表(carList),可以在这个汽车列表属性上利用@FluentValid表明,表示须要级联到内部Car做onEach验证。

<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class Garage { @FluentValidate({ CarNotExceedLimitValidator.class}) @FluentValid private List<Car> carList;}</pre>

把稳, @FluentValid 和 @FluentValidate 两个表明不相互冲突,如下所示,调用链会先验证 carList 上的 CarNotExceedLimitValidator,然后再遍历 carList,对每个 car 做内部的生产商、座椅数、牌照验证。

Spring AOP集成

读到这里,你会创造,只先容了优雅,说好的业务逻辑和验证逻辑解耦合呢?这须要借助Spring AOP技能实现,如下示例,addCar()方法内部上来便是真正的核心业务逻辑,把稳参数上的car前面装饰有@FluentValid表明,这表示须要Spring利用切面拦截方法,对参数利用FluentValidator做校验。

<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@Servicepublic class CarServiceImpl implements CarService { @Override public Car addCar(@FluentValid Car car) { System.out.println("Come on! " + car); return car; }}</pre>

Spring XML配置如下,首先定义了ValidateCallback表示该如何处理成功,失落败,抛出不可控非常三种情形,因此,纵然你拿不到Result结果,也可以做自己的操作,例如下面例子,如果发生缺点,抛出CarException,并且是第一条缺点信息,如果成功则打印"Everything works fine",如果失落败,把实际的非常e放入运行时的CarException抛出。

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class ValidateCarCallback extends DefaulValidateCallback implements ValidateCallback { @Override public void onFail(ValidatorElementList validatorElementList, List<ValidationError> errors) { throw new CarException(errors.get(0).getErrorMsg()); } @Override public void onSuccess(ValidatorElementList validatorElementList) { System.out.println("Everything works fine!"); } @Override public void onUncaughtException(Validator validator, Exception e, Object target) throws Exception { throw new CarException(e); }}</pre>

将ValidateCarCallback注入到FluentValidateInterceptor中。
然后利用BeanNameAutoProxyCreator在ServiceImpl上用fluentValidateInterceptor拦截做验证,这有点类似Spring事务处理拦截器的利用办法。
更多AOP的用法请参考Spring官方文档。

<pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><bean id="validateCarCallback" class="com.baidu.unbiz.fluentvalidator.interceptor.ValidateCarCallback"/><bean id="fluentValidateInterceptor" class="com.baidu.unbiz.fluentvalidator.interceptor.FluentValidateInterceptor"> <property name="callback" ref="validateCarCallback"/> <property name="locale" value="zh_CN"/> <property name="hibernateDefaultErrorCode" value="10000"/></bean><bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <value>ServiceImpl</value> </list> </property> <property name="interceptorNames"> <list> <value>fluentValidateInterceptor</value> </list> </property></bean></pre>

测试代码如下:

<pre class="prettyprint hljs kotlin" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@Testpublic void testAddCarNegative() { try { Car car = getValidCar(); car.setLicensePlate("BEIJING123"); carService.addCar(car); } catch (CarException e) { assertThat(e.getMessage(), Matchers.is("License is not valid, invalid value=BEIJING123")); return; } fail();}</pre>国际化支持

也便是常说的i18n,利用Spring供应的MessageSource来支持,只须要再Spring的XML配置中加入如下配置:

<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>error-message</value> </list> </property></bean></pre>

同时编辑两个国际化文件,路径放在classpath中即可,常日放到resource下面,文件名分别叫做error-message.properties和error-message_zh_CN.properties表示默认(英文)和中文-简体中文。

<pre class="prettyprint hljs vim" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">car.size.exceed=Car number exceeds limit, max available num is { 0}car.size.exceed=\u6c7d\u8f66\u6570\u91cf\u8d85\u8fc7\u9650\u5236\uff0c\u6700\u591a\u5141\u8bb8{ 0}\u4e2a</pre>

把稳,个中 {0} 代表和运行时添补的参数。

在Validator利用时,示例如下,只用MessageSupport.getText(…)来获取国际化信息。

<pre class="prettyprint hljs scala" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class GarageCarNotExceedLimitValidator extends ValidatorHandler<Garage> implements Validator<Garage> { public static final int MAX_CAR_NUM = 50; @Override public boolean validate(ValidatorContext context, Garage t) { if (!CollectionUtil.isEmpty(t.getCarList())) { if (t.getCarList().size() > MAX_CAR_NUM) { context.addError( ValidationError.create(MessageSupport.getText(GarageError.CAR_NUM_EXCEED_LIMIT.msg(), MAX_CAR_NUM)) .setErrorCode(GarageError.CAR_NUM_EXCEED_LIMIT.code()) .setField("garage.cars") .setInvalidValue(t.getCarList().size())); return false; } } return true; }}</pre>

这里须要把稳下,如果利用 HibernateValidator 打的表明,例如 @NotNull , @Length 等,须要将缺点信息放到 ValidationMessages.properties和ValidationMessages_zh_CN.properties 中,否则找不到哦。

上面对 FluentValidator 做了一个全面的先容,从显示的流式风格(Fluent Interface)API 调用,以及各种丰富多样的链操作方法,再到对 JSR303 – Bean Validation 规范的集成,末了先容了高等点的表明办法验证、支持级联工具图和 Spring AOP 的集成,我想这完成了对 FluentValidator 任务的诠释,更好的帮助开拓职员做业务逻辑验证,希望能给你的项目一点帮助,哪怕不是用这个框架,而只是汲取一些思想,然后产生自己的思考,让我们的 clean code 更加“环保”。

标签:

相关文章

介绍白点控制之路,从原理到方法

白点,作为生活中常见的现象,无处不在。对于如何控制白点,许多人却感到困惑。本文将从原理出发,探讨白点的控制方法,并结合实际案例,为...

PHP教程 2025-01-03 阅读0 评论0

介绍直播王者,如何开启你的电竞直播之旅

随着电竞产业的蓬勃发展,越来越多的年轻人投身于电竞直播行业。王者荣耀作为一款备受欢迎的MOBA手游,吸引了大量玩家和观众。如何开启...

PHP教程 2025-01-03 阅读0 评论0