目前在这家公司做定制开发,需要对接各种各样的平台,虽然每个平台都不一样,但是在他们的平台接口中,每个平台接口都有类似的格式,会要求固定的参数、请求头、用于判断的条件等。
以前对接开发的时候,会定义一个请求对象,一个结果对象,在业务代码中通过http或者其他协议完成输入与输出转换,下面是伪代码
Req req = new Req();
String result = httpUtil.post(ulr,JsonUtil.toJson(req));
Resp resp =JsonUtil.toBean(result,Resp.class);
这么写有一个问题,业务代码中会有大量的序列化的代码。代码并不简洁
最近对接交行的相关业务,发现他们使用了泛型来解决这个问题,下面是我根据交行提供的demo自己修改的
首先定义一个接口,使用泛型的输入与输出
public interface YdtoClient {
<T extends YdtoBaseResp> T execute(YdtoBaseReq<T> req) throws YdtoServiceException;
<T extends YdtoBaseResp> T execute(YdtoBaseReq<T> req, String key, String secret) throws YdtoServiceException;
}
YdtoBaseResp与YdtoBaseReq 为接口的公用参数
YdtoBaseReq 定义了两个抽象方法,getCmd()就是HTTP请求的链接,getResponseClass() 定义了返回类型
public abstract class YdtoBaseReq<T extends YdtoBaseResp> implements Serializable {
protected String cmd;
public abstract String getCmd();
@JsonIgnore
public abstract Class<T> getResponseClass();
}
YdtoBaseResp 定义了返回结果的通用参数
public class YdtoBaseResp implements Serializable {
private Integer status;
private String resultCode;
private String message;
... get set
}
注:<T extends YdtoBaseResp> 即便是这个泛型只接受 YdtoBaseResp的子类
接下来是YdtoClient 的实现类
@Service
public class DefaultYdtoClient implements YdtoClient{
private Logger logger = LoggerFactory.getLogger(DefaultYdtoClient.class);
@Autowired
private RequestProfile requestConfig;
@Autowired
private HttpClientService httpClientService;
@Autowired
private YdtoProfile ydtoProfile;
@Override
public <T extends YdtoBaseResp> T execute(YdtoBaseReq<T> req) throws ServiceException {
return execute(req,ydtoProfile.getKey(),ydtoProfile.getSecret());
}
@Override
public <T extends YdtoBaseResp> T execute(YdtoBaseReq<T> req, String key, String secret) throws ServiceException {
int tryTimes = requestConfig.getTryTimes();
if (tryTimes < 1) {
tryTimes = 1;
}
String finalUrl = ydtoProfile.getUrl()+req.getCmd();
//统一序列化入参
String finalJson = JsonUtil.toJson(req);
String returnValue= "";
for(int times=0;times< tryTimes;times++){
if (times > 1) {
try {
Thread.sleep(requestConfig.getSleepTime());
} catch (InterruptedException e) {
logger.error(e.getMessage());
}
}
try {
returnValue = httpClientService.sendDataToOpen(
finalUrl, finalJson, ydtoProfile.getKey(), ydtoProfile.getSecret());
if(StrUtil.isNotBlank(returnValue)){
break;
}
} catch (Exception e) {
logger.error("openydtRequestURL:{}--requestData:{},第[{}]次请求出现异常[{}]",
finalUrl, finalJson, times, e.getMessage(), e);
}
}
if(StrUtil.isBlank(returnValue)){
throw new YdtoServiceException("获取开放平台数据失败");
}
//统一序列化返回结果
T resp = JsonUtil.toBean(returnValue,req.getResponseClass());
if(resp ==null){
throw new YdtoServiceException("开放平台数据转换失败");
}
return resp;
}
}
//至此,我们就抽象化出了这个接口调用的共同部分,对于第三方的单个接口,我们应该怎么调用呢
以http://openydt.yidianting.xin/Api/getParkRemainCarport 为例,我们在创建一个请求参数和返回结果的对象
public class GetParkRemainCarportReq extends YdtoBaseReq<GetParkRemainCarportResp> {
@Override
public String getCmd() {
return "getParkRemainCarport";
}
@Override
public Class<GetParkRemainCarportResp> getResponseClass() {
return GetParkRemainCarportResp.class;
}
private String parkCode;
public String getParkCode() {
return parkCode;
}
public void setParkCode(String parkCode) {
this.parkCode = parkCode;
}
}
public class GetParkRemainCarportResp extends YdtoBaseResp {
private Data data;
public static class Data{
/**
* 总空车位数
*/
private Integer totalRemainCount;
/**
* 总车位数
*/
private Integer totalPermitCount;
public Integer getTotalRemainCount() {
return totalRemainCount;
}
public void setTotalRemainCount(Integer totalRemainCount) {
this.totalRemainCount = totalRemainCount;
}
public Integer getTotalPermitCount() {
return totalPermitCount;
}
public void setTotalPermitCount(Integer totalPermitCount) {
this.totalPermitCount = totalPermitCount;
}
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
}
调用
@Override
public GetParkRemainCarportResp getParkRemainCarport(String parkCode) {
GetParkRemainCarportReq req = new GetParkRemainCarportReq();
req.setParkCode(parkCode);
return ydtoClient.execute(req);
}
对象结果反序列化一气呵成。即使接口调用结果返回失败,只要他符合接口定义的标准格式,也一样能反序列化成功
对于resp类有两点扩充
1、对于内部类,如果结果层级过多可能会导致内部类难以阅读
2、编辑器会提示相关接口层级,在使用值时相对比较友好