时隔几月在博客方面,笔者并无想写文的冲动,不过最近笔者负责一个新服务的开发,在过程当中使用了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()方法的数量,也是挺不错的。 说实话,笔者真羡慕那些能本身造轮子的人...想起本身实现的工具,内心就一阵火大。