如何對protobuf生成的Java物件進行引數校驗
前言
時隔幾月在部落格方面,筆者並沒有想寫文的衝動,不過最近筆者負責一個新服務的開發,在過程中使用了protobuf相關技術。開發完成之後,由於在引數校驗這塊使用了大量的if-else進行校驗,造成code review過程中被瘋狂的diss。在此,咱們就話不多說,直接引出問題:如何對protobuf生成的java物件進行引數校驗?
名詞解釋:
- protobuf生成的java物件統稱為pb物件。普通的java物件統稱為pojo物件
- hibernate validator統稱為validator
正文
比如protobuf直接生成的物件request,一開始筆者是這樣進行校驗的:
if(!request.hasXXX()) {
// do....
throw new Exception();
} else if(request.hasXXX) {
.....
throw new Exception();
}
....以下省略多個if-else
複製程式碼
為什麼會使用這種複雜校驗方式?一開始筆者也曾想過使用hibernate validator進行自動校驗,但在實現過程中,發現pb物件體積較大,內部屬性繁複無法對其加上validator校驗註解。後來,筆者便想方設法讓pb物件轉成普通的java pojo物件,一旦實現無縫轉換,那麼就可以使用hibernate validator進行自動校驗了。
所以,首先想到的就是spring或者apache中的beanutils工具,利用該工具可以實現相同屬性名稱的值轉換,不過這裡有一個缺陷:當基礎資料型別的屬性的值為null的時候,beanutils會進行賦值操作。
比如:Integer i = null,複製出來的屬性會變成,Integer i = 0。此時,如果用轉換後的pojo物件進行@NotNull校驗,校驗結果會直接通過,並不會丟擲異常資訊。在開發過程中,很多時候僅僅是需要null,而不是具有預設值的屬性,那麼此時spring或apache中的beanutils工具就不再適合了。
當然,apache的beanutils工具通過註冊代理的方式可以解決null值變預設值問題。但最終筆者並沒有採取beanutils工具,原因是apache的beanutils的copyProperties方法效率較低,並不推薦使用。beanutils註冊方案
工具介紹:複製即可使用
/**
* 將ProtoBean物件轉化為POJO物件
*
* @param targetClass 目標POJO物件的型別別
* @param sourceMessage 含有資料的ProtoBean物件例項
* @param <PojoType> 目標POJO物件的類型別範型
* @return
* @throws IOException
*/
public static <PojoType> PojoType toBean(Message sourceMessage,Class<PojoType> targetClass) throws Exception {
if (targetClass == null) {
throw new IllegalArgumentException("No destination pojo class specified");
}
if (sourceMessage == null) {
throw new IllegalArgumentException("protobuf message is no specified");
}
String json = JsonFormat.printer().print(sourceMessage);
return new Gson().fromJson(json,targetClass);
}
/**
* 將POJO物件轉化為ProtoBean物件
*
* @param destBuilder 目標Message物件的Builder類
* @param sourcePojoBean 含有資料的POJO物件
* @return
* @throws IOException
*/
public static void toProto(Message.Builder destBuilder,Object sourcePojoBean) throws Exception {
if (destBuilder == null) {
throw new IllegalArgumentException
("No destination message builder specified");
}
if (sourcePojoBean == null) {
throw new IllegalArgumentException("No source pojo specified");
}
String json = new Gson().toJson(sourcePojoBean);
JsonFormat.parser().merge(json,destBuilder);
}
複製程式碼
在使用該工具的時候,需要新增依賴:
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
// 版本省略
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
複製程式碼
由於該工具類並不是筆者所寫,所以直接貼上原作連結
注意事項:pojo的物件的基本資料型別必須是包裝類
最後,筆者還想說說關於pojo轉pb物件問題,使用上面的工具中的toProto方法進行轉換的時候,屬性名稱和屬性數量要一致,不然會丟擲異常。
結束語
經過前後好幾天的探索,發現直接對pb物件直接進行校驗是行不通的,一般會利用中介進行轉換,這裡就使用了pb->json->pojo的策略。筆者認為工具中的toBean方法最為重要,利用該方法可以實現必填項引數校驗,這樣極大減少if-else語句的數量。當然,toProto方法可以極大省略pb.setXX()方法的數量,也是挺不錯的。 說實話,筆者真羨慕那些能自己造輪子的人...想起自己實現的工具,心裡就一陣火大。