首先说说我用到的应用场景:
需求描述:
在工作流(workflow)中,存在着各种各样的工作流程,这些流程呢有一些特征——1.流程基本信息相同 2.流程内容不同
在给前端写接口请求实体的时候,在我接手功能之前是将流程内容实体作为流程属性放在请求实体内,导致不同的流程要写不同的请求实体,违背了开闭原则。
我第一个想到的方案是泛型。
下面是方案——————————————————
流程实体(ApplyEntity ):
/**
* Created by xmtx on 2018/9/8.
*/
public class ApplyEntity {
/**
* 流程id
*/
private String applyId;
/**
* 流程名称
*/
private String applyName;
/**
* 流程类型
*/
private Integer applyType;
/**
* 流程基本信息
*/
private String applyBaicInfo;
public String getApplyId() {
return applyId;
}
public void setApplyId(String applyId) {
this.applyId = applyId;
}
public String getApplyName() {
return applyName;
}
public void setApplyName(String applyName) {
this.applyName = applyName;
}
public String getApplyBaicInfo() {
return applyBaicInfo;
}
public void setApplyBaicInfo(String applyBaicInfo) {
this.applyBaicInfo = applyBaicInfo;
}
public Integer getApplyType() {
return applyType;
}
public void setApplyType(Integer applyType) {
this.applyType = applyType;
}
@Override
public String toString() {
return "ApplyEntity{" +
"applyId='" + applyId + '\'' +
", applyName='" + applyName + '\'' +
", applyType=" + applyType +
", applyBaicInfo='" + applyBaicInfo + '\'' +
'}';
}
流程内容 类型一(职位):
/**
* 岗位信息
* Created by xmtxon 2018/9/3.
*/
public class PostInfo extends BaseEntity{
// 岗位编码
private String postCode;
// 岗位名称
private String postName;
public String getPostCode() {
return postCode;
}
public void setPostCode(String postCode) {
this.postCode = postCode;
}
public String getPostName() {
return postName;
}
public void setPostName(String postName) {
this.postName = postName;
}
@Override
public String toString() {
return "PostInfo{" +
"postCode='" + postCode + '\'' +
", postName='" + postName + '\'' +
'}';
}
流程内容 类型二 (报表):
/**
* @Author: xmtx
* @Description: report
* @Date: Created by xmtxon 2018/9/8.
*/
public class RptBaseDto {
//报表Id
private String reportId;
//报表版本id
private String versionId;
public RptBaseDto() {
// this is a construct
}
public RptBaseDto(String reportId, String versionId) {
this.reportId = reportId;
this.versionId = versionId;
}
public String getReportId() {
return reportId;
}
public void setReportId(String reportId) {
this.reportId = reportId;
}
public String getVersionId() {
return versionId;
}
public void setVersionId(String versionId) {
this.versionId = versionId;
}
/*@Override
public String toString() {
return reportId + '_' + versionId;
}*/
}
请求参数 泛型类:
/**
* Created by xmtx on 2018/9/8.
*/
public class ApplyRequest<T> extends ApplyEntity{
/**
* 泛型属性,作为流程内容
*/
private T applyContent;
public T getApplyContent() {
return applyContent;
}
public void setApplyContent(T applyContent) {
this.applyContent = applyContent;
}
@Override
public String toString() {
return "ApplyRequest{" +
"applyContent=" + applyContent +
'}';
}
到此为止,请求参数泛型类已经满足功能需求,我们有新增的流程,只需要创建新的流程内容实体即可。
下面要解决的是前后台参数序列化的问题:
我们先构造两种类型的请求参数:
public static void main(String[] args) {
//流程基本信息
ApplyRequest applyRequest = new ApplyRequest();
applyRequest.setApplyId("2333");
applyRequest.setApplyName("applyName");
applyRequest.setApplyType(1);
applyRequest.setApplyBaicInfo("basic infomation");
/**
* 流程内容 类型一:岗位信息
*/
PostInfo postInfo = new PostInfo();
postInfo.setPostCode("postCode");
postInfo.setPostName("postName");
applyRequest.setApplyContent(postInfo);
System.out.println("=============流程内容一:岗位================");
System.out.println(JSON.toJSONString(applyRequest));
/**
* 流程内容 类型二 :报表信息
*/
RptBaseDto reportEntity = new RptBaseDto();
reportEntity.setReportId("reportId");
reportEntity.setVersionId("versionid");
applyRequest.setApplyType(2);
applyRequest.setApplyContent(reportEntity);
System.out.println("=============流程内容二:报表================");
System.out.println(JSON.toJSONString(applyRequest));
}
得到的结果如下:
=============流程内容一:岗位================
{"applyBaicInfo":"basic infomation","applyContent":{"id":0,"postCode":"postCode","postName":"postName"},"applyId":"2333","applyName":"applyName","applyType":1}
=============流程内容二:报表================
{"applyBaicInfo":"basic infomation","applyContent":{"reportId":"reportId","versionId":"versionid"},"applyId":"2333","applyName":"applyName","applyType":2}
编写post请求方法,在wagger或者postman中模拟post请求。
@RequestMapping(value = "/testFX", method = RequestMethod.POST)
public ResponseObject testFX(@RequestBody ApplyRequest applyRequest) {
System.out.println(JSON.toJSONString(applyRequest));
Integer applyType = applyRequest.getApplyType();
String instance = JSON.toJSONString(applyRequest);
switch (applyType){
case 1:
ApplyRequest<PostInfo> applyRequest1 = applyRequest;
PostInfo postInfo = applyRequest1.getApplyContent();
return new ResponseObject(JSON.toJSONString(postInfo));
case 2:
ApplyRequest<RptBaseDto> applyRequest2 = applyRequest;
RptBaseDto rptBaseDto = applyRequest2.getApplyContent();
return new ResponseObject(JSON.toJSONString(rptBaseDto));
default:
return null;
}
}
测试方法,于是我们得到了如下异常:
{
"statusCode": "100000",
"msg": "系统未知异常",
"error": "java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.suning.drp.common.applicationcenter.bean.PostInfo"
}
于是乎,我们进入debug模式:
我们看到参数被序列化成LinkedHashMap[],看来Spring MVC 的 @RequestBody 并不能将泛型参数反序列化。
我们于是找google爸爸(Casting LinkedHashMap to Complex Object)。
解决方案如下:
POJO pojo = mapper.convertValue(singleObject, POJO.class);
// or:
List<POJO> pojos = mapper.convertValue(listOfObjects, new TypeReference<List<POJO>>() { });
于是改变代码为:
@RequestMapping(value = "/testFX", method = RequestMethod.POST)
public ResponseObject testFX(@RequestBody ApplyRequest applyRequest) {
System.out.println(JSON.toJSONString(applyRequest));
Integer applyType = applyRequest.getApplyType();
String instance = JSON.toJSONString(applyRequest);
switch (applyType){
case 1:
PostInfo postInfo = mapper.convertValue(applyRequest.getApplyContent(), PostInfo.class);
return new ResponseObject(JSON.toJSONString(postInfo));
case 2:
ApplyRequest<RptBaseDto> applyRequest2 = applyRequest;
RptBaseDto rptBaseDto = applyRequest2.getApplyContent();
return new ResponseObject(JSON.toJSONString(rptBaseDto));
default:
return null;
}
}
但得到的结果却不是我们想要的:
问题在哪里呢,我们稍后再分析,先给出解决方案:
我们先把请求参数转换为JSONString ,然后再转换为泛型对实体(这样就完美解决了fastjson的不足):
@RequestMapping(value = "/testFX", method = RequestMethod.POST)
public ResponseObject testFX(@RequestBody ApplyRequest applyRequest) {
Integer applyType = applyRequest.getApplyType();
String instance = JSON.toJSONString(applyRequest);
switch (applyType) {
case 1:
ApplyRequest<PostInfo> postInfo1 = JSON.parseObject(instance, ApplyRequest.class);
//do something
return new ResponseObject(JSON.toJSONString(postInfo1));
case 2:
ApplyRequest<RptBaseDto> applyRequest2 = JSON.parseObject(instance, ApplyRequest.class);
//do something
return new ResponseObject(JSON.toJSONString(applyRequest2));
default:
return new ResponseObject("error");
}
}
关于泛型的其它应用场景可参考csdn的这篇(https://blog.csdn.net/u011240877/article/details/53545041),谢谢您的阅读。