Fluent-Validator 業(yè)務(wù)校驗器
背景
在互聯(lián)網(wǎng)行業(yè)中嫡意,基于Java開發(fā)的業(yè)務(wù)類系統(tǒng),不管是服務(wù)端還是客戶端烹看,業(yè)務(wù)邏輯代碼的更新往往是非常頻繁的,這源于功能的快速迭代特性固耘。在一般公司內(nèi)部,特別是使用Java web技術(shù)構(gòu)建的平臺中,不管是基于模塊化還是服務(wù)化的纵诞,業(yè)務(wù)邏輯都會相對復(fù)雜。
這些系統(tǒng)之間器予、系統(tǒng)內(nèi)部往往存在大量的API接口浪藻,這些接口一般都需要對入?yún)ⅲㄝ斎雲(yún)?shù)的簡稱)做校驗,以保證:
1) 核心業(yè)務(wù)邏輯能夠順利按照預(yù)期執(zhí)行乾翔。
2) 數(shù)據(jù)能夠正常存取爱葵。
3) 數(shù)據(jù)安全性。包括符合約束以及限制反浓,有訪問權(quán)限控制以及不出現(xiàn)SQL注入等問題萌丈。
開發(fā)人員在維護(hù)核心業(yè)務(wù)邏輯的同時,還需要為輸入做嚴(yán)格的校驗雷则。當(dāng)輸入不合法時辆雾,能夠給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
}
我們可以發(fā)現(xiàn)度迂,它不夠優(yōu)雅而且違反一些范式:
1)違反單一職責(zé)原則(Single responsibility)。核心業(yè)務(wù)邏輯(core business logic)和驗證邏輯(validation logic)耦合在一個類中猜揪。
2)開閉原則(Open/closed)惭墓。我們應(yīng)該對擴展開放,對修改封閉而姐,驗證邏輯不好擴展腊凶,而且一旦需要修改需要動整體這個類。
3)DRY原則(Don’t repeat yourself)拴念。代碼冗余钧萍,相同邏輯可能散落多處,長此以往不好收殮丈莺。
1.簡介
FluentValidato是一個適用于以Java語言開發(fā)的程序划煮,讓開發(fā)人員回歸focus到業(yè)務(wù)邏輯上,使用流式(Fluent Interface)調(diào)用風(fēng)格讓驗證跑起來很優(yōu)雅缔俄,同時驗證器(Validator)可以做到開閉原則弛秋,實現(xiàn)最大程度的復(fù)用的工具庫器躏。
2.特點
- 驗證邏輯與業(yè)務(wù)邏輯不再耦合
摒棄原來不規(guī)范的驗證邏輯散落的現(xiàn)象。 - 校驗器各司其職蟹略,好維護(hù)登失,可復(fù)用,可擴展
一個校驗器(Validator)只負(fù)責(zé)某個屬性或者對象的校驗挖炬,可以做到職責(zé)單一揽浙,易于維護(hù),并且可復(fù)用意敛。 - 流式風(fēng)格(Fluent Interface)調(diào)用
- 使用注解方式驗證
可以裝飾在屬性上馅巷,減少硬編碼量。 - 支持JSR 303 – Bean Validation標(biāo)準(zhǔn)
或許你已經(jīng)使用了Hibernate Validator草姻,不用拋棄它钓猬,F(xiàn)luentValidator可以站在巨人的肩膀上。 - Spring良好集成
校驗器可以由Spring IoC容器托管撩独。校驗入?yún)⒖梢灾苯邮褂米⒔獬ú埽渲煤脭r截器,核心業(yè)務(wù)邏輯完全沒有驗證邏輯的影子综膀,干凈利落澳迫。 - 回調(diào)給予你充分的自由度
驗證過程中發(fā)生的錯誤、異常剧劝,驗證結(jié)果的返回橄登,開發(fā)人員都可以定制。
3.上手
3.1引入maven依賴:
<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>
3.2 業(yè)務(wù)領(lǐng)域模型
從廣義角度來說DTO(Data Transfer Object)担平、VO(Value Object)示绊、BO(Business Object)、POJO等都可以看做是業(yè)務(wù)表達(dá)模型暂论。
創(chuàng)建一個學(xué)生類面褐,包含 name(姓名)、age(年齡)取胎、schoolName(學(xué)校名稱)展哭、(area)地區(qū)
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;
}
3.3 Validator樣例
針對schoolName(學(xué)校名稱)創(chuàng)建一個Validator,代碼如下:
public class SchoolNameValidator extends ValidatorHandler<String> implements Validator<String> {
@Override
public boolean validate(ValidatorContext context, String schoolName) {
if (!"無錫中學(xué)".equals(schoolName)) {
context.addErrorMsg("學(xué)校名稱不正確");
return false;
}
return true;
}
}
很簡單闻蛀,實現(xiàn)Validator接口匪傍,泛型T規(guī)范這個校驗器待驗證的對象的類型,繼承ValidatorHandler可以避免實現(xiàn)一些默認(rèn)的方法觉痛,validate()方法第一個參數(shù)是整個校驗過程的上下文役衡,第二個參數(shù)是待驗證對象,也就是學(xué)校名稱薪棒。
驗證邏輯:假設(shè)學(xué)校名稱必須是無錫中學(xué)手蝎,否則通過context放入錯誤消息并且返回false榕莺,成功返回true。
3.4 驗證
StudentDto studentDto = new StudentDto("張三", 18, "蘇州中學(xué)", "無錫");
Result result =
FluentValidator.checkAll()
.on(studentDto.getSchoolName(), new SchoolNameValidator())
.doValidate()
.result(toSimple());
System.out.println(result);
//打印結(jié)果:Result{isSuccess=false, errors=[學(xué)校名稱不正確]}
首先我們通過FluentValidator.checkAll()獲取了一個FluentValidator實例棵介,緊接著調(diào)用了failFast()表示有錯了立即返回钉鸯,它的反義詞是failOver,然后邮辽,唠雕、on()操作表示在指定屬性上使用對應(yīng)校驗器進(jìn)行校驗,截止到此吨述,真正的校驗還并沒有做岩睁,這就是所謂的“惰性求值(Lazy valuation)”,有點像Java8 Stream API中的filter()揣云、map()方法笙僚,直到doValidate()驗證才真正執(zhí)行了,最后我們需要收殮出來一個結(jié)果供caller獲取打印灵再,直接使用默認(rèn)提供的靜態(tài)方法toSimple()來做一個回調(diào)函數(shù)傳入result()方法,最終返回Result類亿笤。
4.深入了解
4.1 Validator詳解
Validator接口代碼如下:
public interface Validator<T> {
/**
* 判斷在該對象上是否接受或者需要驗證
* <p/>
* 如果返回true翎迁,那么則調(diào)用{@link #validate(ValidatorContext, Object)},否則跳過該驗證器
*
* @param context 驗證上下文
* @param t 待驗證對象
*
* @return 是否接受驗證
*/
boolean accept(ValidatorContext context, T t);
/**
* 執(zhí)行驗證
* <p/>
* 如果發(fā)生錯誤內(nèi)部需要調(diào)用{@link ValidatorContext#addErrorMsg(String)}方法净薛,也即<code>context.addErrorMsg(String)
* </code>來添加錯誤汪榔,該錯誤會被添加到結(jié)果存根{@link Result}的錯誤消息列表中。
*
* @param context 驗證上下文
* @param t 待驗證對象
*
* @return 是否驗證通過
*/
boolean validate(ValidatorContext context, T t);
/**
* 異乘喟荩回調(diào)
* <p/>
* 當(dāng)執(zhí)行{@link #accept(ValidatorContext, Object)}或者{@link #validate(ValidatorContext, Object)}發(fā)生異常時的如何處理
*
* @param e 異常
* @param context 驗證上下文
* @param t 待驗證對象
*/
void onException(Exception e, ValidatorContext context, T t);
}
ValidatorHandler是實現(xiàn)Validator接口的一個模板類痴腌,如果你自己實現(xiàn)的Validator不想覆蓋上面3個方法,可以繼承這個ValidatorHandler燃领。
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) {
}
}
內(nèi)部校驗邏輯發(fā)生錯誤時候士聪,有兩個處理辦法,
第一猛蔽,簡單處理剥悟,如上述3.3中代碼所示:
context.addErrorMsg("學(xué)校名稱不正確");
第二,需要詳細(xì)的信息曼库,包括錯誤消息区岗,錯誤屬性/字段,錯誤值毁枯,錯誤碼慈缔,都可以自己定義,放入錯誤的方法如下种玛,create()方法傳入消息(必填)藐鹤,setErrorCode()方法設(shè)置錯誤碼(選填)瓤檐,setField()設(shè)置錯誤字段(選填),setInvalidValue()設(shè)置錯誤值(選填)教藻。當(dāng)然這些信息需要result(toComplex())才可以獲取到距帅。
public class AreaValidator extends ValidatorHandler<String> implements Validator<String> {
// 實現(xiàn)Validator接口,泛型T規(guī)范這個校驗器待驗證的對象的類型
@Override
public boolean validate(ValidatorContext context, String area) {
if (!"無錫".equals(area)) {
context.addError(
ValidationError.create("地址不正確")
.setErrorCode(5000)
.setField("area")
.setInvalidValue(area));
// context.addErrorMsg("地址不正確");
return false;
}
return true;
}
}
如果需要可以使用復(fù)雜ComplexResult括堤,內(nèi)含錯誤消息碌秸,錯誤屬性/字段,錯誤值悄窃,錯誤碼讥电,如下所示:
ComplexResult ret =
FluentValidator.checkAll()
.failOver()
.on(studentDto.getArea(), new AreaValidator())
.doValidate()
.result(toComplex());
System.out.println(ret);
//打印結(jié)果:Result{isSuccess=false, errors=[ValidationError{errorCode=5000, errorMsg='地址不正確', field='area', invalidValue=蘇州}], timeElapsedInMillis=1}
上述都是針對單個屬性值編寫對應(yīng)的Validator代碼,實際開發(fā)中轧抗,我們需要對整個對象的多個屬性進(jìn)行業(yè)務(wù)校驗恩敌,這時我們可以針對整個對象編寫對應(yīng)的Validator,最后用ComplexResult來接收校驗結(jié)果横媚,代碼如下:
public class StudentValidator extends ValidatorHandler<StudentDto>
implements Validator<StudentDto> {
@Override
public boolean validate(ValidatorContext context, StudentDto studentDto) {
if (!"無錫".equals(studentDto.getArea())) {
context.addError(
ValidationError.create("地址不正確")
.setErrorCode(5000)
.setField("area")
.setInvalidValue(studentDto.getArea()));
}
if (!"無錫中學(xué)".equals(studentDto.getSchoolName())) {
context.addError(
ValidationError.create("學(xué)校名稱不正確")
.setErrorCode(5000)
.setField("schoolName")
.setInvalidValue(studentDto.getSchoolName()));
}
//校驗有沒有Error信息
if (CollectionUtils.isNotEmpty(context.result.getErrors())) {
return false;
}
return true;
}
}
on()的一連串調(diào)用實際就是構(gòu)建調(diào)用鏈纠炮,因此理所當(dāng)然可以傳入一個調(diào)用鏈。
ValidatorChain chain = new ValidatorChain();
List<Validator> validators = new ArrayList<Validator>();
validators.add(new StudentValidator());
chain.setValidators(validators);
ComplexResult rets =
FluentValidator.checkAll().on(studentDto, chain).doValidate().result(toComplex());
System.out.println(rets);
//打印結(jié)果:Result{isSuccess=false, errors=[ValidationError{errorCode=5000, errorMsg='地址不正確', field='area', invalidValue=蘇州}, ValidationError{errorCode=5000, errorMsg='學(xué)校名稱不正確', field='schoolName', invalidValue=蘇州中學(xué)}], timeElapsedInMillis=7}
拓展
可根據(jù)項目自定義返回結(jié)果類型灯蝴,實現(xiàn)ResultCollector即可
public interface ResultCollector<T> {
/**
* 轉(zhuǎn)換為對外結(jié)果
*
* @param result 框架內(nèi)部驗證結(jié)果
*
* @return 對外驗證結(jié)果對象
*/
T toResult(ValidationResult result);
}
4.2 onEach
如果要驗證的是一個集合(Collection)或者數(shù)組恢口,那么可以使用onEach,F(xiàn)luentValidator會自動為你遍歷:
ComplexResult result =
FluentValidator.checkAll()
.onEach(list, new StudentValidator())
.doValidate()
.result(toComplex());
4.3 fail fast or fail over
當(dāng)出現(xiàn)校驗失敗時穷躁,也就是Validator的validate()方法返回了false耕肩,那么是繼續(xù)還是直接退出呢?默認(rèn)為使用failFast()方法问潭,直接退出猿诸,如果你想繼續(xù)完成所有校驗,使用failOver()來skip掉狡忙。
ComplexResult result1 =
FluentValidator.checkAll()
.failFast()
.on(liS.getArea(), new AreaValidator())
.on(liS.getSchoolName(), new SchoolNameValidator())
.doValidate()
.result(toComplex());
ComplexResult result2 =
FluentValidator.checkAll()
.failOver()
.on(liS.getArea(), new AreaValidator())
.on(liS.getSchoolName(), new SchoolNameValidator())
.doValidate()
.result(toComplex());
4.4 when
on()后面可以緊跟一個when()梳虽,當(dāng)when滿足expression表達(dá)式on才啟用驗證,否則skip調(diào)用灾茁。
ComplexResult result =
FluentValidator.checkAll()
.failOver()
.on(liS.getArea(), new AreaValidator())
.when("20".equals(liS.getAge()))
.on(liS.getSchoolName(), new SchoolNameValidator())
.doValidate()
.result(toComplex());
System.out.println(result);
4.5 驗證回調(diào)callBack
doValidate()方法接受一個ValidateCallback接口
public interface ValidateCallback {
/**
* 所有驗證完成并且成功后
*
* @param validatorElementList 驗證器list
*/
void onSuccess(ValidatorElementList validatorElementList);
/**
* 所有驗證步驟結(jié)束怖辆,發(fā)現(xiàn)驗證存在失敗后
*
* @param validatorElementList 驗證器list
* @param errors 驗證過程中發(fā)生的錯誤
*/
void onFail(ValidatorElementList validatorElementList, List<ValidationError> errors);
/**
* 執(zhí)行驗證過程中發(fā)生了異常后
*
* @param validator 驗證器
* @param e 異常
* @param target 正在驗證的對象
*
* @throws Exception
*/
void onUncaughtException(Validator validator, Exception e, Object target) throws Exception;
}
我們可以根據(jù)業(yè)務(wù)需求,在校驗回調(diào)接口中做其他邏輯處理删顶,如下所示:
FluentValidator.checkAll()
.on(liS.getSchoolName(), new SchoolNameValidator())
.doValidate(
new DefaultValidateCallback() {
@Override
public void onSuccess(
ValidatorElementList validatorElementList) {
System.out.println("校驗成功");
}
@Override
public void onFail(
ValidatorElementList validatorElementList,
List<ValidationError> errors) {
System.out.println("校驗失敗");
}
})
.result(toComplex());
4.6 RuntimeValidateException
如果驗證中發(fā)生了一些不可控異常竖螃,例如數(shù)據(jù)庫調(diào)用失敗,RPC連接失效等逗余,會拋出一些異常特咆,如果Validator沒有try-catch處理,F(xiàn)luentValidator會將這些異常封裝在RuntimeValidateException,然后再re-throw出去腻格。
4.7 上下文傳遞
通過putAttribute2Context()方法画拾,可以往FluentValidator注入一些鍵值對,在所有Validator中共享菜职,有時候這相當(dāng)有用青抛。
Result result =
FluentValidator.checkAll()
.putAttribute2Context("school", "常州中學(xué)")
.on(liS.getSchoolName(), new SchoolNameValidator())
.doValidate()
.result(toSimple());
可在Validator中通過context.getAttribute
拿到這個值
String name = context.getAttribute("school", String.class);
if (!name.equals(schoolName)) {
context.addErrorMsg("學(xué)校名稱不正確");
return false;
}
return true;
4.8 閉包
通過putClosure2Context()方法,可以往FluentValidator注入一個閉包酬核,這個閉包的作用是在Validator內(nèi)部可以調(diào)用蜜另,并且緩存結(jié)果到Closure中,這樣caller在上層可以獲取這個結(jié)果嫡意。
典型的應(yīng)用場景是举瑰,當(dāng)需要頻繁調(diào)用一個RPC的時候,往往該執(zhí)行線程內(nèi)部一次調(diào)用就夠了蔬螟,多次調(diào)用會影響性能此迅,我們就可以緩存住這個結(jié)果,在所有Validator間和caller中共享旧巾。
下面展示了在caller處存在一個getAreas()方法耸序,它假如需要RPC才能獲取所有地區(qū)信息,顯然是很耗時的鲁猩,可以在validator中調(diào)用佑吝,然后validator內(nèi)部共享的同時,caller可以利用閉包拿到結(jié)果绳匀,用于后續(xù)的業(yè)務(wù)邏輯。
StudentDto liS = new StudentDto("李四", 18, "常州中學(xué)", "常州");
Closure<List<String>> closure =
new ClosureHandler<List<String>>() {
private List<String> allManufacturers;
@Override
public List<String> getResult() {
return allManufacturers;
}
@SneakyThrows
@Override
public void doExecute(Object... input) {
// getAreas()模擬RPC遠(yuǎn)程接口調(diào)用
allManufacturers = getAreas();
}
};
FluentValidator.checkAll()
.putClosure2Context("area", closure)
.on(liS.getArea(), new AreaValidator())
.doValidate()
.result(toSimple());
Validator中獲取接口查詢的數(shù)據(jù):
Closure<List<String>> closure = context.getClosure("area");
List<String> areas = closure.executeAndGetResult();
if (!areas.contains(area)) {
context.addError(
ValidationError.create("地址不正確")
.setErrorCode(5000)
.setField("area")
.setInvalidValue(area));
return false;
}
return true;
}
5.高級玩法
Hibernate Validator是JSR 303 – Bean Validation規(guī)范的一個最佳的實現(xiàn)類庫炸客,他僅僅是jboss家族的一員疾棵,和大名鼎鼎的Hibernate ORM是系出同門,屬于遠(yuǎn)房親戚關(guān)系痹仙。很多框架都會天然集成這個優(yōu)秀類庫是尔,例如Spring MVC的@Valid注解可以為Controller方法上的參數(shù)做校驗。
FluentValidator當(dāng)然不會重復(fù)早輪子开仰,這么好的類庫拟枚,一定要使用站在巨人肩膀上的策略,將它集成進(jìn)來众弓。
想要了解更多Hibernate Validator用法恩溅,參考這個鏈接。
fluent-validator 集成 hibernate-validator 需要添加依賴
<dependency>
<groupId>com.baidu.unbiz</groupId>
<artifactId>fluent-validator-jsr303</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
<version>1.0.5</version>
</dependency>
5.1 注解驗證
上述都是通過顯示的API調(diào)用來進(jìn)行驗證谓娃,F(xiàn)luentValidator同樣提供簡潔的基于注解配置的方式來達(dá)到同樣的效果脚乡。
@FluentValidate可以裝飾在屬性上,內(nèi)部接收一個Class[]數(shù)組參數(shù)滨达,這些個classes必須是Validator的子類奶稠,這叫表明在某個屬性上依次用這些Validator做驗證俯艰。如下,我們改造下StudentDto這個類:
@Data
@AllArgsConstructor
public class StudentDto {
@NotNull private String name;
private Integer age;
@Length(max = 5)
@FluentValidate({SchoolNameValidator.class})
private String schoolName;
@FluentValidate({AreaValidator.class})
private String area;
}
然后還是利用on()或者onEach()方法來校驗锌订,這里只不過不用傳入Validator或者ValidatorChain了竹握。
ComplexResult ret =
FluentValidator.checkAll()
.failOver()
.configure(new SimpleRegistry())
.on(liS)
.doValidate()
.result(toComplex());
默認(rèn)的,F(xiàn)luentValidator使用SimpleRegistry辆飘,它會嘗試從當(dāng)前的class loader中調(diào)用Class.newInstance()方法來新建一個Validator啦辐。
5.2 分組驗證
當(dāng)使用注解驗證時候,會遇到這樣的情況劈猪,某些時候例如添加操作昧甘,我們會驗證A/B/C三個屬性,而修改操作战得,我們需要驗證B/C/D/E 4個屬性
@FluentValidate注解另外一個接受的參數(shù)是groups充边,里面也是Class[]數(shù)組,只不過這個Class可以是開發(fā)人員隨意寫的一個簡單的類常侦,不含有任何屬性方法都可以浇冰,例如:
@Data
@AllArgsConstructor
public class StudentDto {
@NotNull private String name;
private Integer age;
@Length(max = 5)
@FluentValidate(
value = {SchoolNameValidator.class},
groups = {Add.class})
private String schoolName;
@FluentValidate(
value = {AreaValidator.class},
groups = Update.class)
private String area;
}
那么驗證的時候,只需要在checkAll()方法中傳入想要驗證的group聋亡,就只會做選擇性的分組驗證肘习,例如下面例子,只有area(地區(qū))會被驗證坡倔。
ComplexResult result =
FluentValidator.checkAll(new Class<?>[] {Update.class})
.on(liS)
.doValidate()
.result(toComplex());
5.3 級聯(lián)驗證
級聯(lián)驗證(cascade validation)漂佩,也叫做對象圖(object graphs),指一個類嵌套另外一個類的時候做的驗證罪塔。
如下例所示投蝉,我們在車庫(Garage)類中含有一個汽車列表(carList),可以在這個汽車列表屬性上使用@FluentValid注解征堪,表示需要級聯(lián)到內(nèi)部Car做onEach驗證瘩缆。
public class Garage {
@FluentValidate({CarNotExceedLimitValidator.class})
@FluentValid
private List<Car> carList;
}
注意,@FluentValid和@FluentValidate兩個注解不互相沖突佃蚜,如下所示庸娱,調(diào)用鏈會先驗證carList上的CarNotExceedLimitValidator,然后再遍歷carList谐算,對每個car做內(nèi)部的生產(chǎn)商熟尉、座椅數(shù)、牌照驗證洲脂。
6.SpringBoot實戰(zhàn)
6.1 添加依賴
fluent-validator 集成 spring 需要添加依賴
<dependency>
<groupId>com.baidu.unbiz</groupId>
<artifactId>fluent-validator-spring</artifactId>
<version>1.0.9</version>
</dependency>
6.2 注冊 Fluent-validator
fluent-validate 與 spring 結(jié)合使用 annotation 方式進(jìn)行參數(shù)校驗臣樱,需要借助于 spring 的 AOP,fluent-validate 提供了處理類 FluentValidateInterceptor,但是 fluent-validate 提供的默認(rèn)驗證回調(diào)類 DefaultValidateCallback 對校驗失敗的情況并沒有處理雇毫,所以需要自行實現(xiàn)一個
@Slf4j
public class MyValidateCallBack extends DefaultValidateCallback implements ValidateCallback {
@Override
public void onSuccess(ValidatorElementList validatorElementList) {
log.info("校驗成功");
super.onSuccess(validatorElementList);
}
@Override
public void onFail(ValidatorElementList validatorElementList, List<ValidationError> errors) {
log.info("校驗失敗");
throw new RuntimeException(errors.get(0).getErrorMsg());
}
@Override
public void onUncaughtException(Validator validator, Exception e, Object target)
throws Exception {
log.info("校驗異常");
throw new RuntimeException(e);
}
6.3 注冊IOC
注冊 FluentValidateInterceptor攔截器及MyValidateCallBack回調(diào)方法玄捕,最后配置一個 AOP 規(guī)則
@Configuration
public class ValidateCallbackConfig {
@Bean
public FluentValidateInterceptor fluentValidateInterceptor() {
FluentValidateInterceptor fluentValidateInterceptor = new FluentValidateInterceptor();
fluentValidateInterceptor.setCallback(validateCallback());
return fluentValidateInterceptor;
}
public MyValidateCallBack validateCallback() {
return new MyValidateCallBack();
}
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
// 使用BeanNameAutoProxyCreator來創(chuàng)建代理
BeanNameAutoProxyCreator proxyCreator = new BeanNameAutoProxyCreator();
// 設(shè)置要創(chuàng)建代理的那些Bean的名字
proxyCreator.setBeanNames("*ServiceImpl");
proxyCreator.setInterceptorNames("fluentValidateInterceptor");
return proxyCreator;
}
}
6.4 使用校驗
為了方便,在StudentServiceImpl實現(xiàn)類上增加參數(shù)校驗
@Service
public class StudentServiceImpl implements StudentService {
@Override
public Integer getAge(@FluentValid StudentDto studentDto) {
return studentDto.getAge();
}
}
總結(jié)
fluent-validate 可以全方位兼容 hibernate-validate棚放,基于 spring 的 AOP 可以提供基于注解的方法入?yún)⑿r灻墩常瑫r也可以提供流式編程的工具類業(yè)務(wù)校驗,替代 hibernate-validate 的同時提供了更多擴展性
歡迎大家訪問 個人博客 Johnny小屋