Dubbo, Hessian 序列化注意事项

最近遇到一个问题,B 服务调用 A 服务时,返回值反序列化时,POJO对象变成了Map类型。在A服务单独测试的时候一直还原不了,在 B 服务进行测试的时候,跟到反序列化数据时才看到原因。

原因很简单

A 服务的接口方法返回的结果是一个Object(或 Map 中的 value),Object 的具体实现不在 A 服务的 API 包中,因此在 B 服务找不到该返回值真正的实现类,在 B 服务调用接口返回结果反序列化找不到具体的类型时,就会以 Map 类型进行实例化。

下面是简单接口示例:
A-api 模块中:

public interface A {
    /* 注意是 Object(或 `Map` 中的 `value`) */
    Object say();
}

A-service 实现中:

public class AModel implements Serializable {
    private static final long serialVersionUID = 1L;
    private String text;
    /* 省略 getter setter */
}

public class AImpl implements A {
	public Object say() {
		return new AModel("hello");
	}
}

B-service 中用到了 A-api,但是不依赖 A-service

public class BImpl implements B {
	@Autowire
	private A a;

	public void saySomething() {
		Object s = a.say();
		//强制转换会出错,实际的返回值类型为HashMap
		AModel am = (AModel)s;
		//可以下面这样调用,真正使用时这样肯定也算错误
		String text = ((HashMap)am).getText();
	}
}

下面是简单的分析过程。

反序列化过程:

Hessian2Input 开始,反序列 Object 时调用 readObjectInstance
Dubbo, Hessian 序列化注意事项_第1张图片
上图代码中,会从 SerializerFactory.getObjectDeserializer 获取类型对应的反序列化实现。
Dubbo, Hessian 序列化注意事项_第2张图片
通过一层层调用,最后到下面的方法:
Dubbo, Hessian 序列化注意事项_第3张图片
在该方法中,有如下代码:
Dubbo, Hessian 序列化注意事项_第4张图片
上面会反射查找类,找到就能正确的反序列化为真正的对象,如果找不到,继续看后面的代码。

可以看到这里有警告日志,这里需要特别的设置才能输出日志。
首先,这里的 log 是 java.util.Logger 实现,目前流行的日志框架一般都选择 slf4j 作为日志的中间层,这时可以通过下面依赖从 jul 桥接到 slf4j:

<dependency>
   <groupId>org.slf4jgroupId>
   <artifactId>jul-to-slf4jartifactId>
   <version>1.7.28version>
dependency>

相关文档:Bridging legacy APIs

有了上面的依赖后,就能按照 slf4j 的方式进行输出。

回到外层代码,如下:
Dubbo, Hessian 序列化注意事项_第5张图片
找不到类型对应的序列化实例时,就会使用 MapDeserializer 进行反序列化。 MapDeserializer 中的方法如下:
Dubbo, Hessian 序列化注意事项_第6张图片
至此就完全清楚 Dubbo 中反序列化的一条重要原则了。

接口中返回的 POJO 如果是个接口,如果需要正确反序列化,那么实现类一定要能访问到。

你可能感兴趣的:(JAVA编程,解决问题)