随着ServiceMock项目的推广实施,遇到的坑也越来越稀奇古怪了。
这次是介绍一个使用了Mybatis Pro的项目中遇到的问题。Mybatis是在Spring项目中非常常见的持久层框架。在享受其带来的便利的同时,也给SeriveMock带来了新的挑战。
有如下的一个典型的数据库Mapper的类。
import java.util.List;
import tk.mybatis.mapper.entity.Example;
public interface BaseService<T> {
public List<T> selectAll();
public List<T> selectByExample(Example example);
public T selectByPrimarikey(Object key);
public int updateByPrimarikey(T t);
public int updateByExample(T t,Example example);
public int insert(T t);
public int insertList(List<T> t);
public int deleteByPrimarikey(Object key);
public int deleteByExample(Example example);}
*源码来自网络。
制造问题的是起头的这三个泛型的方法。如 selectByPrimaryKey这个方法,由于这是一个泛型方法,其返回值为T 。
而ServiceMock的录制回放的基本套路就是通过获取到被录制的方法的返回值类型来进行回放。
简单来说,是这样的一个过程,
@Around("recordLog()")
public Object around(ProceedingJoinPoint pjp)
throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Type returnType =signature.getMethod().
getAnnotatedReturnType().getType();
//***
return GsonUtil.fromJson(record.getReturning(),returnType);
}
通过切面获取到了执行中的方法,然后根据方法的returnType来对录制的数据进行反序列化并作为本次执行的结果返回,从而就实现了对服务依赖的回放。
而如果是泛型的方法,那么returnType的结果就是“T”或者是”List<T>”, 这也就意味着以下反序列化方法的执行失败。
GsonUtil.fromJson(record.getReturning(),returnType);
反序列化时,gson并不知道T具体是什么类型,导致反序列化的失败,或者List<T>会被以List<Object>的方式进行反序列化,造成了在回放时的数据类型与调用者的预期不匹配。
问题解决
在被这个问题卡壳之后,还是要重新温习一下Java的基础。
这是Class类的定义,
public final class Class<T> implements
Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
}
可以看到, Class类实现了Type接口。这样,只要获取到了切点中正在执行方法的返回值(原始类型或者是某个类的实例),然后根据返回值来获取到Class,并最终获取到Type。这样就可以继续愉快地进行反序列化了。
有如下的方式,
proceed = pjp.proceed();
String typeName=proceed.getClass().getTypeName();
if (returnType.getTypeName().equals("T") ||
returnType.getTypeName().equals("java.util.List<T>")) {
对于T 或者是List<T>的泛型方法,就在执行时获取一下执行类型并记录。
而在回放时,可以通过获取记录的类型来进行反射,获取对应的类,并最终实现反序列化。
if(returnType.getTypeName().equals("T")) {
Class clazz=Class.forName(record.getReturnType());
return GsonUtil.fromJson(data,clazz);
}
于是,就可以继续愉快地进行录制回放了。
当然,坑还是要继续填的。例如,