IDEA專案搭建十三——服務消費端與生產端通訊實現
阿新 • • 發佈:2018-12-30
一、簡介
之前已經完成了EurekaClient的服務生產者和Feign的服務消費者模組的搭建,現在實現統一的通訊約定
(1) 統一Request結構
(2) 統一Response結構
(3) 統一Error通知
二、程式碼
1、建立統一請求物件ServiceRequest<>實際引數就是這個泛型,使用統一的構造進行建立便於對資料進行統一的加密傳輸
import java.util.Date; /** * 客戶端請求內容物件 */ public class ServiceRequest<T> { //region 屬性 /** * 請求唯一ID*/ private String requestID; /** * 請求時間 */ private Date requestTime; /** * 客戶端代號 */ private String clientCode; /** * 請求籤名 */ private String requestSign; /** * 請求引數 */ private T reqData; public String getRequestID() { returnrequestID; } public void setRequestID(String requestID) { this.requestID = requestID; } public Date getRequestTime() { return requestTime; } public void setRequestTime(Date requestTime) { this.requestTime = requestTime; } public String getClientCode() {return clientCode; } public void setClientCode(String clientCode) { this.clientCode = clientCode; } public String getRequestSign() { return requestSign; } public void setRequestSign(String requestSign) { this.requestSign = requestSign; } /** * 禁止使用此方法-不能刪除內部自動取值需要使用 * @return */ @Deprecated public T getReqData() { return reqData; } /** * 禁止使用此方法-不能刪除內部自動賦值需要使用 * @param reqData */ @Deprecated public void setReqData(T reqData) { this.reqData = reqData; } //endregion //設定類的無參構造為私有禁止外部例項化 private ServiceRequest() { } /** * 建立請求物件 * @param data */ public ServiceRequest(T data) { //後期從此處增加加解密程式碼... this.reqData = data; } /** * 獲取請求引數 * @param req * @param <T> * @return */ public static <T> T getRequestData(ServiceRequest<T> req) { //後期從此處增加加解密程式碼... T obj = req.getReqData(); return obj; } }
2、建立統一響應物件ServiceResponse<>實際響應就是這個泛型,使用統一的取值便於有需求時對響應資料進行統一的解密
import com.google.common.base.Strings; import java.util.Date; /** * 服務端響應結果物件 */ public class ServiceResponse<T> { //region 屬性 /** * 請求唯一ID */ private String requestID; /** * 響應代號 * <p> * 000000 - 正確 */ private ServiceCodeMsgEnum resCodeMsg; /** * 響應時間 */ private Date resTime; /** * 響應結果 */ private T resData; public String getRequestID() { return requestID; } public void setRequestID(String requestID) { this.requestID = requestID; } public ServiceCodeMsgEnum getResCodeMsg() { return resCodeMsg; } public void setResCodeMsg(ServiceCodeMsgEnum resCodeMsg) { this.resCodeMsg = resCodeMsg; } public Date getResTime() { return resTime; } public void setResTime(Date resTime) { this.resTime = resTime; } /** * 禁止使用此方法-不能刪除內部取值需要使用 * * @return */ @Deprecated public T getResData() { return resData; } /** * 禁止使用此方法-不能刪除內部賦值需要使用 * * @param resData */ @Deprecated public void setResData(T resData) { this.resData = resData; } //endregion //設定類的無參構造為私有禁止外部例項化,只能通過下方靜態方法建立 private ServiceResponse() { } /** * 建立執行正確響應物件 * @param data */ public ServiceResponse(T data) { this.resCodeMsg = ServiceCodeMsgEnum.Success; this.resData = data; } /** * 建立執行錯誤響應物件 * @param codeMsg */ public ServiceResponse(ServiceCodeMsgEnum codeMsg) { this.resCodeMsg = codeMsg; this.resData = null; } /** * 獲取響應CodeMsg(外部WebApi專用) * * @param res * @param <T> * @return ServiceCodeMsgEnum.Success為正確 */ private static <T> ServiceCodeMsgEnum getResponseCodeMsg(ServiceResponse<T> res) { return res.getResCodeMsg(); } /** * 獲取響應Msg(內部站點專用) * * @param res * @param <T> * @return null為正確 */ public static <T> String getResponseMsg(ServiceResponse<T> res) { return Strings.emptyToNull(res.getResCodeMsg().getMsg()); } /** * 獲取響應引數 * * @param res * @param <T> * @return */ public static <T> T getResponseData(ServiceResponse<T> res) { return res.getResData(); } }
3、建立統一響應結果列舉,這裡可以統一控制響應CodeMsg的對應關係,使用也簡單直觀
/** * 服務通訊CodeMsg列舉 */ public enum ServiceCodeMsgEnum { Success("000000", null), Error("999999", "系統異常"); //region private String code; private String msg; ServiceCodeMsgEnum(String code, String msg) { this.code = code; this.msg = msg; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } //endregion }
4、服務生產者這邊,統一入參和返回值都是ServiceRequest和ServiceResponse,又可以根據泛型來識別到底是什麼物件
import com.google.gson.Gson; import com.ysl.ts.common.serviceModel.ServiceCodeMsgEnum; import com.ysl.ts.common.serviceModel.ServiceRequest; import com.ysl.ts.common.serviceModel.ServiceResponse; import com.ysl.ts.core.model.base.ts_base.SysUserModel; import com.ysl.ts.core.service.base.service.SysUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; import java.util.Map; @Controller @RequestMapping("/api/sysUser") public class SysUserController extends BaseController { @Autowired SysUserService service; @ResponseBody @RequestMapping("/save") public ServiceResponse<Integer> save(@RequestBody ServiceRequest<SysUserModel> req) { try { //使用統一的方法獲取請求Data SysUserModel agent = ServiceRequest.getRequestData(req); //呼叫Service int result = service.save(agent); //響應-成功 return new ServiceResponse<>(result); } catch (Exception e) { //響應-錯誤 return new ServiceResponse<>(ServiceCodeMsgEnum.Error); } } @ResponseBody @RequestMapping("/delete") public ServiceResponse<Integer> delete(@RequestBody ServiceRequest<Integer> req) { try { //獲取請求Data int id = ServiceRequest.getRequestData(req); //呼叫Service int result = service.delete(id); //響應-成功 return new ServiceResponse<>(result); } catch (Exception e) { //響應-錯誤 return new ServiceResponse<>(ServiceCodeMsgEnum.Error); } } @ResponseBody @RequestMapping("/get") public ServiceResponse<SysUserModel> get(@RequestBody ServiceRequest<Integer> req) { try { //獲取請求Data int id = ServiceRequest.getRequestData(req); //呼叫Service SysUserModel result = service.get(id); //響應-成功 return new ServiceResponse<>(result); } catch (Exception e) { //響應-錯誤 return new ServiceResponse<>(ServiceCodeMsgEnum.Error); } } @ResponseBody @RequestMapping("/list") public ServiceResponse<List<SysUserModel>> list(@RequestBody ServiceRequest<String> req) { try { //獲取請求Data String search = ServiceRequest.getRequestData(req); //呼叫Service List<SysUserModel> result = service.list(); //響應-成功 return new ServiceResponse<>(result); } catch (Exception e) { //響應-錯誤 return new ServiceResponse<>(ServiceCodeMsgEnum.Error); } } }
5、服務消費者這邊,使用了Feign所以需要一個介面來實現呼叫,我們直接傳入ServiceRequest<>來做統一的請求物件,返回ServiceResponse<>來做統一的響應物件
import com.ysl.ts.common.serviceModel.ServiceRequest; import com.ysl.ts.common.serviceModel.ServiceResponse; import com.ysl.ts.core.model.base.ts_base.SysUserModel; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import java.util.List; @Service @FeignClient("YSL-TS-Core-Service-Base")//服務生產者名稱 @RequestMapping("/api/sysUser")//服務路由 public interface SysUserService { @RequestMapping("/save") ServiceResponse<Integer> save(@RequestBody ServiceRequest<SysUserModel> req); @RequestMapping("/delete") ServiceResponse<Integer> delete(@RequestBody ServiceRequest<Integer> req); @RequestMapping("/get") ServiceResponse<SysUserModel> get(@RequestBody ServiceRequest<Integer> req); @RequestMapping("/list") ServiceResponse<List<SysUserModel>> list(@RequestBody ServiceRequest<String> req); }
6、獲得基礎資料實體後,在頁面展現之前可能有些欄位需要進行翻譯,比如狀態1:啟用,0:禁用等等,這部分建立一個ModelEx類繼承Model類,把其中需要翻譯的欄位寫在ModelEx中,
用以下轉換類對實體值進行拷貝,然後頁面接收這個ModelEx物件,這樣預設可以使用父類中的屬性,如果要顯示翻譯的就用子類中的屬性即可
import java.util.ArrayList; import java.util.List; /** * Model 轉換類 * * @param <TModel> Model型別物件 * @param <TModelEx> ModelEx型別物件*/ public abstract class AbstractModelConvertor<TModel, TModelEx extends TModel> { /** * 轉換 * * @param model Model型別物件 * @param modelEx ModelEx型別物件 * @return */ public TModelEx convert(TModel model, Class<TModelEx> modelEx) { TModelEx ex = new DeepClone().clone(model, modelEx); convertFields(ex);//填充翻譯欄位,需要子類重寫 return ex; } /** * 列表轉換 * * @param modelList Model型別物件列表 * @param modelEx ModelEx型別物件 * @return */ public List<TModelEx> convert(List<TModel> modelList, Class<TModelEx> modelEx) { List<TModelEx> list = new ArrayList<>(); for (TModel tModel : modelList) { list.add(convert(tModel, modelEx)); } return list; } /** * 欄位轉換介面 * * @param modelEx */ protected abstract void convertFields(TModelEx modelEx); }
實際上就是使用Gson對實體做了一次序列化很簡單
import com.google.gson.Gson; /** * 深入拷貝 */ public class DeepClone { private Gson gson = new Gson(); /** * 深拷貝 * * @param t 源資料 * @param clazz 目標類 * @param <T> 源資料型別 * @param <K> 目標型別 * @return */ public <T, K> K clone(T t, Class<K> clazz) { return gson.fromJson(gson.toJson(t), clazz); } }
7、每一個實體都繼承抽象基類,這樣就可以直接使用轉換方法了
import com.ysl.ts.common.AbstractModelConvertor; import com.ysl.ts.core.model.base.ts_base.SysUserModel; import com.ysl.ts.core.model.base.ts_base.ex.SysUserModelEx; public class SysUserConvertor extends AbstractModelConvertor<SysUserModel, SysUserModelEx> { /** * 填充待翻譯欄位 */ @Override protected void convertFields(SysUserModelEx sysUserModelEx) { } }
8、下面就是頁面Controller的Action呼叫了
import com.ysl.ts.common.serviceModel.ServiceRequest; import com.ysl.ts.common.serviceModel.ServiceResponse; import com.ysl.ts.core.model.base.ts_base.SysUserModel; import com.ysl.ts.core.model.base.ts_base.ex.SysUserModelEx; import com.ysl.ts.web.base.modelConvertors.ts_base.SysUserConvertor; import com.ysl.ts.web.base.service.SysUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.ArrayList; import java.util.List; @Controller @Component("AdminSysUser") @RequestMapping("/sysUser") public class SysUserController extends BaseController { //自動注入Feign介面物件 @Autowired SysUserService service; @RequestMapping("/save") public boolean save(SysUserModel agent) { boolean result = false; //建立ServiceRequest ServiceRequest<SysUserModel> req = new ServiceRequest<>(agent); //呼叫Service並獲得ServiceResponse ServiceResponse<Integer> res = service.save(req); //解析獲得Code String msg = ServiceResponse.getResponseMsg(res); //NULL代表響應正常 if (null == msg) { //解析獲得T型別 result = ServiceResponse.getResponseData(res) > 0; } return result; } @RequestMapping("/delete") public boolean delete(int id){ boolean result = false; //建立ServiceRequest ServiceRequest<Integer> req = new ServiceRequest<>(id); //呼叫Service並獲得ServiceResponse ServiceResponse<Integer> res = service.delete(req); //解析獲得Code String msg = ServiceResponse.getResponseMsg(res); //NULL代表響應正常 if (null == msg) { //解析獲得T型別 result = ServiceResponse.getResponseData(res) > 0; } return result; } @ResponseBody @RequestMapping("/get") public SysUserModelEx get(Model model, int id) { SysUserModelEx result = new SysUserModelEx(); //建立ServiceRequest ServiceRequest<Integer> req = new ServiceRequest<>(id); //呼叫Service並獲得ServiceResponse ServiceResponse<SysUserModel> res = service.get(req); //解析獲得Code String msg = ServiceResponse.getResponseMsg(res); //NULL代表響應正常 if (null == msg) { //解析獲得T型別 SysUserModel tmp = ServiceResponse.getResponseData(res); //翻譯所需欄位 result = new SysUserConvertor().convert(tmp, SysUserModelEx.class); } return result; //model.addAttribute("model", result); //return "/hotel/get"; } //直接返回json不寫頁面了 @ResponseBody @RequestMapping("/list") public List<SysUserModelEx> list(String search) { List<SysUserModelEx> result = new ArrayList<>(); //建立ServiceRequest ServiceRequest<String> req = new ServiceRequest<>(search); //呼叫Service並獲得ServiceResponse ServiceResponse<List<SysUserModel>> res = service.list(req); //解析獲得Code String msg = ServiceResponse.getResponseMsg(res); //NULL代表響應正常 if (null == msg) { //解析獲得T型別 List<SysUserModel> tmp = ServiceResponse.getResponseData(res); //翻譯所需欄位 result = new SysUserConvertor().convert(tmp, SysUserModelEx.class); } return result; } }
111