Dubbo, Hessian 序列化注意事项

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

原因很简单

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

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

public interface A {
    /* 注意是 Object(或 `Map<String, Object>` 中的 `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
在这里插入图片描述
上图代码中,会从 SerializerFactory.getObjectDeserializer 获取类型对应的反序列化实现。
在这里插入图片描述
通过一层层调用,最后到下面的方法:
在这里插入图片描述
在该方法中,有如下代码:
在这里插入图片描述
上面会反射查找类,找到就能正确的反序列化为真正的对象,如果找不到,继续看后面的代码。

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

<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>jul-to-slf4j</artifactId>
   <version>1.7.28</version>
</dependency>

相关文档:Bridging legacy APIs

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

回到外层代码,如下:
在这里插入图片描述
找不到类型对应的序列化实例时,就会使用 MapDeserializer 进行反序列化。 MapDeserializer 中的方法如下:
在这里插入图片描述
至此就完全清楚 Dubbo 中反序列化的一条重要原则了。

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

发布了299 篇原创文章 · 获赞 1651 · 访问量 595万+

猜你喜欢

转载自blog.csdn.net/isea533/article/details/102786322