如何做好序列化
# 如何做好序列化
## 为什么要序列化?
- 苍穹基于微服务架构来实现,框架层面的缓存也是基于微服务来实现,微服务相互调用的过程中对于对象的传输都是基于字节流的形式,所以RPC框架底层都通过序列化工具对要传输的内容进行序列化,对于我们来说,当页面进行showform时如果调用的两个页面是不同的应用,那么在回调时可能进行的就是跨服务的调用;
- 分布式缓存我们是基于redis实现,因为redis只支持一些基本类型,所以对于复杂对象的缓存也需要借助于序列化工具将对象序列化为字符串进行存储
## 序列化怎么做?
1. 使用kd.bos.dataentity.serialization.SerializationUtils
2. 使用kd.bos.dataentity.serialization.DcJsonSerializer
3. 使用kd.bos.dtx.util.DynamicObjectSerializeUtil
上面三种方式推荐使用DcJsonSerializer进行序列化,因为DcJsonSerializer提供了给属性增加Attribute 属性来选择需要序列化的内容,这样性能会更好,DynamicObjectSerializeUtil是单独用了序列化DynamicObject对象的,使用范围有限
对于RPC框架的序列化,我们没法干预,RPC框架我们用的是Dubbo,这个Dubbo版本是我们修改2.8.4版本的,包名加了kd.bos。dubbo底层序列化使用的是Hessian,我们也对Hessian进行了修改,增加了kd.bos包名。实际的框架代码中包含了三种版本的Hessian框架,一个是dubbo的,一个是hessian原版,还有一个是我们修改的khessian。
## 怎么对继承了ArrayList和 Map的对象进行序列化?
以ListSelectedRowCollection 为例,ListSelectedRowCollection 继承了ArrayList , 如果需要在ListSelectedRowCollection内部添加一个boolean类型的字段并通过dubbo 进行传输,那么做如下几件事情:
### 新增一个ListSelectedRowCollectionHandle
``` java
package kd.bos.entity.datamodel;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import kd.bos.com.caucho.hessian.io.HessianHandle;
class ListSelectedRowCollectionHandle implements Serializable, HessianHandle {
/**
*
*/
private static final long serialVersionUID = 2697859790479295931L;
private List<ListSelectedRow> ls = new ArrayList<>();
private boolean isClearFlag = false;
List<ListSelectedRow> getLs() {
return ls;
}
void addAll(Collection<ListSelectedRow> c) {
ls.addAll(c);
}
boolean isClearFlag() {
return isClearFlag;
}
void setClearFlag(boolean isClearFlag) {
this.isClearFlag = isClearFlag;
}
Object readResolve() {
ListSelectedRowCollection list = new ListSelectedRowCollection();
list.addAll(ls);
list.setClearFlag(isClearFlag);
return list;
}
}
```
注意上面引用的是kd.bos.com.caucho.hessian.io.HessianHandle,即khessian
### 在ListSelectRowCollection添加writeReplace事件
``` java
public class ListSelectedRowCollection extends ArrayList<ListSelectedRow> {
private boolean clearFlag;
/**
* 用来标记是否为基础资料清空
*
* @return
*/
@SimplePropertyAttribute(name = "ClearFlag")
public boolean isClearFlag() {
return clearFlag;
}
public void setClearFlag(boolean clearFlag) {
this.clearFlag = clearFlag;
}
//...其它逻辑
private Object writeReplace() {
ListSelectedRowCollectionHandle handle = new ListSelectedRowCollectionHandle();
handle.addAll(this);
handle.setClearFlag(this.clearFlag);
return handle;
}
}
```
### 测试
- 新建一个包装ListSelectedRowCollection的测试类
``` java
import java.io.Serializable;
import kd.bos.entity.datamodel.ListSelectedRowCollection;
public class TestA implements Serializable {
/**
*
*/
private static final long serialVersionUID = 3094250203697054755L;
ListSelectedRowCollection listSelectedRowCollection = new ListSelectedRowCollection();
public ListSelectedRowCollection getListSelectedRowCollection() {
return listSelectedRowCollection;
}
public void setListSelectedRowCollection(ListSelectedRowCollection listSelectedRowCollection) {
this.listSelectedRowCollection = listSelectedRowCollection;
}
}
```
- 编写测试
``` java
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import kd.bos.com.caucho.hessian.io.Hessian2Input;
import kd.bos.com.caucho.hessian.io.Hessian2Output;
public class Test {
public static void main(String[] args) throws Exception {
// Hessian 序列化用这个模拟测试
ByteArrayOutputStream os = new ByteArrayOutputStream();
Hessian2Output output = new Hessian2Output(os);
// ObjectOutputStream output = new ObjectOutputStream(os);
// ListSelectedRowCollection listSelectedRowCollection = new
// ListSelectedRowCollection();
// listSelectedRowCollection.setClearFlag(true);
TestA testA = new TestA();
testA.getListSelectedRowCollection().setClearFlag(true);
output.writeObject(testA);
output.close();
System.out.println(new String(os.toByteArray()));
ByteArrayInputStream bis = new ByteArrayInputStream(os.toByteArray());
Hessian2Input input = new Hessian2Input(bis);
// ObjectInputStream input = new ObjectInputStream(bis);
TestA newobj = (TestA) input.readObject();
System.out.println(newobj.getListSelectedRowCollection().isClearFlag());
}
}
```
这里测试时引用的也同样是khessian,不然无法模拟真实的场景,最后可以发现测试通过,可以拿到设置的ClearFlag为true
## 如何序列化DynamicObject ?
DynamicObject 是动态的数据包,其包含的实体、字段类型不确定,不能使用普通的序列化工具进行序列化。
有两种方法,对DynamicObject进行序列化。
方法一:使用 DataEntitySerializer 工具序列化与反序列化
序列化:
``` java
/**
* 保存源单
*
* @param subMainType 单据主实体模型节选,只包含部分字段
* @param dataEntities 单据数据包,只包含部分字段
* @param selectFields 包含的字段清单:反序列化时,需要据此重构节选单据主实体模型
*/
public void saveSrcBill(IDataEntityType subMainType, Object[] dataEntities, List<String> selectFields){
this.opName = OPNAME_SAVESRCBILL;
// 把实体编码、字段清单存储在本地,反序列化时要用到
this.srcEntityNumber = subMainType.getName();
this.srcSelectFields = selectFields;
// 把源单数据包,序列化为字符串,以便后续发布MQ消息时,可以被序列化
if (dataEntities != null && dataEntities.length > 0) {
DataEntitySerializerOption option = new DataEntitySerializerOption();
option.setIncludeDataEntityState(true);
option.setIncludeComplexProperty(true);
option.setIncludeCollectionProperty(true);
option.setIncludeType(false);
this.srcDataStrings = new String[dataEntities.length];
for(int i = 0 ; i < dataEntities.length; i++){
this.srcDataStrings[i] = DataEntitySerializer.serializerToString(dataEntities[i], option);
}
}
}
```
反序列化:
``` java
/**
* 输出源单数据包
* @return
* @remark 使用单据节选实体模型,反序列化数据包(实体模型中的字段,必须与序列化时数据包中包含的字段一致)
*/
public DynamicObject[] getSrcDataEntities() {
if (this.srcDataEntities == null){
if (this.srcDataStrings != null){
MainEntityType subMainType = (MainEntityType)EntityMetadataCache.getSubDataEntityType(srcEntityNumber, this.srcSelectFields);
this.srcDataEntities = new DynamicObject[this.srcDataStrings.length];
for (int i = 0; i < this.srcDataStrings.length; i++){
this.srcDataEntities[i] = (DynamicObject)DataEntitySerializer.deSerializerFromString(this.srcDataStrings[i], subMainType);
}
}
}
return this.srcDataEntities;
}
```
要点:
1. 序列化、反序列化时,实体模型中的字段,必须一致;建议在序列化时记录下字段集合,如上述示例中的selectFields;
2. 因为要求字段一致,本方法仅适用于实时传递数据,传递过程单据实体不太可能变化;不适用于持久化数据,如果期间修改了单据设计,反序列化会中断;
3. 典型的应用场景,如生成一个MQ消息,异步执行前,序列化参数;
方案二:使用 DcJsonSerializer 序列化
序列化:
``` java
ParameterModel model = (ParameterModel) this.getView().getModel();
ParameterEntityType entityType = (ParameterEntityType) (model).getDataEntityType();
DynamicObject dataEntity = model.getDataEntity(true);
DynamicObjectSerializationBinder binder=new DynamicObjectSerializationBinder(dataEntity.getDynamicObjectType());
binder.setOnlyDbProperty(false);
binder.setSerializeDefaultValue(true);
DcJsonSerializer jser=new DcJsonSerializer(binder);
IDataModel defaultModel = new ParameterModel(getEntityId(), Uuid16.create().toString(), new HashMap<>(), model.getBindFormId());
String jsonString = jser.serializeToString(dataEntity, defaultModel.createNewData());
```
反序列化:
``` java
private static DynamicObject serializeToDataEntity(DynamicObjectCollection existData, String formId){
if(StringUtils.isNotBlank(formId)){
MainEntityType entityType = EntityMetadataCache.getDataEntityType(formId);
DynamicObject dataEntity= (DynamicObject) entityType.createInstance();
createDefaultEntity(entityType, dataEntity);
String jsonStr = (existData != null && !existData.isEmpty()) ? existData.get(0).getString(0) : null;
if(StringUtils.isNotBlank(jsonStr)){
DynamicObjectSerializationBinder binder=new DynamicObjectSerializationBinder(entityType);
DcJsonSerializer jser=new DcJsonSerializer(binder);
dataEntity = (DynamicObject) jser.deserializeFromString(jsonStr, dataEntity);
}
return dataEntity;
}
return null;
}
```
要点:
1. 本方案,使用差量化序列化,只输出与传入的基准数据包不同的字段值;
2. 本方案,适用于把数据包序列化后持久存储下来,后续可以用最新的实体模型(字段可能有变化)反序列化。
3. 典型的应用场景,是系统参数配置的存储;
如何做好序列化
# 如何做好序列化## 为什么要序列化?- 苍穹基于微服务架构来实现,框架层面的缓存也是基于微服务来实现,微服务相互调用的过程中对于对...
点击下载文档
上一篇:设计器中提示“控件类型不符合,不能添加。”下一篇:插件开发典型场景汇总贴
本文2024-09-23 00:21:26发表“云苍穹知识”栏目。
本文链接:https://wenku.my7c.com/article/kingdee-cangqiong-138942.html
您需要登录后才可以发表评论, 登录登录 或者 注册
最新文档
热门文章