夫子与歌 2016-08-23
项目中使用了服务框架Dubbo进行远程调用,框架持久层使用的是Spring data jpa,一同事开发的Dubbo接口类似入如下:
XXXXService.findBy(PageRequest pg );其中PageRequest 是spring data jps提供的分页排序实现类。
当在客户端调用该接口时,PageRequest 中明明传入的油蚕丝,但一到Dubbo反序列化,Dubbo
总时抛 PageRequest could not be instantiated;跟了下Dubbo的源码,发现这个异常是在
JavaDeserializer(Dubbo序列化类)中抛出的,再看异常cause by,发现是 PageRequest的
父类AbstractPageRequest 构造函数检查失败出的问题,
public AbstractPageRequest(int page, int size) {
if (page < 0) {
throw new IllegalArgumentException("Page index must not be less than zero!");
}
if (size < 1) {
throw new IllegalArgumentException("Page size must not be less than one!");
}
this.page = page;
this.size = size;
}
就红的地方抛出类异常,也就是说size=0啦。很奇怪,明明出入的PageRequest有值,为啥Dubbo反序列就把变成零了,带着这个问题,有跟了一遍Dubbo源码,发现在JavaDeserializer
public JavaDeserializer(Class cl)
{
_type = cl;
_fieldMap = getFieldMap(cl);
_readResolve = getReadResolve(cl);
if (_readResolve != null) {
_readResolve.setAccessible(true);
}
Constructor []constructors = cl.getDeclaredConstructors();
long bestCost = Long.MAX_VALUE;
for (int i = 0; i < constructors.length; i++) {
Class []param = constructors[i].getParameterTypes();
long cost = 0;
for (int j = 0; j < param.length; j++) {
cost = 4 * cost;
if (Object.class.equals(param[j]))
cost += 1;
else if (String.class.equals(param[j]))
cost += 2;
else if (int.class.equals(param[j]))
cost += 3;
else if (long.class.equals(param[j]))
cost += 4;
else if (param[j].isPrimitive())
cost += 5;
else
cost += 6;
}
if (cost < 0 || cost > (1 << 48))
cost = 1 << 48;
cost += (long) param.length << 48;
if (cost < bestCost) {
_constructor = constructors[i];
bestCost = cost;
}
}
if (_constructor != null) {
_constructor.setAccessible(true);
Class []params = _constructor.getParameterTypes();
_constructorArgs = new Object[params.length];
for (int i = 0; i < params.length; i++) {
_constructorArgs[i] = getParamArg(params[i]);
}
}
}
有对_constructor和 _constructorArgs初始化的过程,大致意思是会调用所有构造函数中参数最少的那个。
也就是会调用PageRequest中
public PageRequest(int page, int size) {
this(page, size, null);
}
再跟到上面标红的地方,也就是_constructorArgs初始化代码
protected static Object getParamArg(Class cl)
{
if (! cl.isPrimitive())
return null;
else if (boolean.class.equals(cl))
return Boolean.FALSE;
else if (byte.class.equals(cl))
return new Byte((byte) 0);
else if (short.class.equals(cl))
return new Short((short) 0);
else if (char.class.equals(cl))
return new Character((char) 0);
else if (int.class.equals(cl))
return Integer.valueOf(0);
else if (long.class.equals(cl))
return Long.valueOf(0);
else if (float.class.equals(cl))
return Float.valueOf(0);
else if (double.class.equals(cl))
return Double.valueOf(0);
else
throw new UnsupportedOperationException();
}
上面方法加红的部分,答案揭晓,dubbo全返回0了,所有才有了前面的错误Page size must not be less than one!
总结,Dubbo默认使用的是Hessian序列化,具体可以看下Hessian这个序列化框架,若使用Hessian,默认情况下最好都提供一个无参构造函数