目前在這家公司做定制開發(fā),需要對接各種各樣的平臺孽尽,雖然每個平臺都不一樣窖壕,但是在他們的平臺接口中,每個平臺接口都有類似的格式,會要求固定的參數(shù)瞻讽、請求頭狐蜕、用于判斷的條件等。
以前對接開發(fā)的時候卸夕,會定義一個請求對象,一個結(jié)果對象层释,在業(yè)務(wù)代碼中通過http或者其他協(xié)議完成輸入與輸出轉(zhuǎn)換,下面是偽代碼
Req req = new Req();
String result = httpUtil.post(ulr,JsonUtil.toJson(req));
Resp resp =JsonUtil.toBean(result,Resp.class);
這么寫有一個問題快集,業(yè)務(wù)代碼中會有大量的序列化的代碼贡羔。代碼并不簡潔
最近對接交行的相關(guān)業(yè)務(wù),發(fā)現(xiàn)他們使用了泛型來解決這個問題个初,下面是我根據(jù)交行提供的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 為接口的公用參數(shù)
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 定義了返回結(jié)果的通用參數(shù)
public class YdtoBaseResp implements Serializable {
private Integer status;
private String resultCode;
private String message;
... get set
}
注:<T extends YdtoBaseResp> 即便是這個泛型只接受 YdtoBaseResp的子類
接下來是YdtoClient 的實(shí)現(xiàn)類
@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();
//統(tǒng)一序列化入?yún)? 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:{},第[{}]次請求出現(xiàn)異常[{}]",
finalUrl, finalJson, times, e.getMessage(), e);
}
}
if(StrUtil.isBlank(returnValue)){
throw new YdtoServiceException("獲取開放平臺數(shù)據(jù)失敗");
}
//統(tǒng)一序列化返回結(jié)果
T resp = JsonUtil.toBean(returnValue,req.getResponseClass());
if(resp ==null){
throw new YdtoServiceException("開放平臺數(shù)據(jù)轉(zhuǎn)換失敗");
}
return resp;
}
}
//至此楣嘁,我們就抽象化出了這個接口調(diào)用的共同部分,對于第三方的單個接口珍逸,我們應(yīng)該怎么調(diào)用呢
以http://openydt.yidianting.xin/Api/getParkRemainCarport 為例逐虚,我們在創(chuàng)建一個請求參數(shù)和返回結(jié)果的對象
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{
/**
* 總空車位數(shù)
*/
private Integer totalRemainCount;
/**
* 總車位數(shù)
*/
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;
}
}
調(diào)用
@Override
public GetParkRemainCarportResp getParkRemainCarport(String parkCode) {
GetParkRemainCarportReq req = new GetParkRemainCarportReq();
req.setParkCode(parkCode);
return ydtoClient.execute(req);
}
對象結(jié)果反序列化一氣呵成。即使接口調(diào)用結(jié)果返回失敗谆膳,只要他符合接口定義的標(biāo)準(zhǔn)格式叭爱,也一樣能反序列化成功
對于resp類有兩點(diǎn)擴(kuò)充
1、對于內(nèi)部類漱病,如果結(jié)果層級過多可能會導(dǎo)致內(nèi)部類難以閱讀
2买雾、編輯器會提示相關(guān)接口層級,在使用值時相對比較友好