時隔幾月在博客方面,筆者並無想寫文的衝動,不過最近筆者負責一個新服務的開發,在過程當中使用了protobuf相關技術。開發完成以後,因爲在參數校驗這塊使用了大量的if-else進行校驗,形成code review過程當中被瘋狂的diss。在此,我們就話很少說,直接引出問題:如何對protobuf生成的java對象進行參數校驗?java
名詞解釋:spring
好比protobuf直接生成的對象request,一開始筆者是這樣進行校驗的:apache
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進行自動校驗了。json
因此,首先想到的就是spring或者apache中的beanutils工具,利用該工具能夠實現相同屬性名稱的值轉換,不過這裏有一個缺陷:當基礎數據類型的屬性的值爲null的時候,beanutils會進行賦值操做。segmentfault
好比:Integer i = null,複製出來的屬性會變成,Integer i = 0。此時,若是用轉換後的pojo對象進行@NotNull校驗,校驗結果會直接經過,並不會拋出異常信息。在開發過程當中,不少時候僅僅是須要null,而不是具備默認值的屬性,那麼此時spring或apache中的beanutils工具就再也不適合了。工具
固然,apache的beanutils工具經過註冊代理的方式能夠解決null值變默認值問題。但最終筆者並無採起beanutils工具,緣由是apache的beanutils的copyProperties方法效率較低,並不推薦使用。beanutils註冊方案ui
/** * 將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);
}
複製代碼
在使用該工具的時候,須要添加依賴:google
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
// 版本省略
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
複製代碼
因爲該工具類並非筆者所寫,因此直接貼上原做連接spa
注意事項:pojo的對象的基本數據類型必須是包裝類.net
最後,筆者還想說說關於pojo轉pb對象問題,使用上面的工具中的toProto方法進行轉換的時候,屬性名稱和屬性數量要一致,否則會拋出異常。
通過先後好幾天的探索,發現直接對pb對象直接進行校驗是行不通的,通常會利用中介進行轉換,這裏就使用了pb->json->pojo的策略。筆者認爲工具中的toBean方法最爲重要,利用該方法能夠實現必填項參數校驗,這樣極大減小if-else語句的數量。固然,toProto方法能夠極大省略pb.setXX()方法的數量,也是挺不錯的。 說實話,筆者真羨慕那些能本身造輪子的人...想起本身實現的工具,內心就一陣火大。