跨應用的全局封裝經過模仿java的異常拋出實現,經過各個服務使用相同的接口返回格式實現服務消息的傳遞,在規範化的同時快速定位真正出現問題的服務。java
全局接口返回格式分爲外部接口格式和內部接口格式,這裏簡化爲內外接口格式一致。spring
總體流程以下:json
整個spring cloud服務的統一接口app
public class ResponseResult<T> implements Serializable { /** * */ private static final long serialVersionUID = 1679552421651455773L; private int status; //狀態碼,根據服務實際需求自定義 private String msg; private T data; private String url; //請求的url private Long host; //出現問題的根服務 public static ResponseResult ok(Object data, String url) { return new ResponseResult(ResponseStatusCode.OK, data, url, null); } public static ResponseResult ok(String msg, Object data, String url) { return new ResponseResult(ResponseStatusCode.OK, msg, data, url, null); } public static ResponseResult ok(String msg, String url) { return new ResponseResult(ResponseStatusCode.OK, msg, null, url, null); } public static ResponseResult fail(int status, String msg, String url, Long host) { return new ResponseResult(status, msg, url, host); } public ResponseResult() { } public ResponseResult(String msg, T data, String url, Long host) { this.msg = msg; this.data = data; this.url = url; this.host = host; } public ResponseResult(int status, String msg, String url, Long host) { this.status = status; this.msg = msg; this.url = url; this.host = host; } public ResponseResult(int status, T data, String url, Long host) { this.status = status; this.data = data; this.url = url; this.host = host; } public ResponseResult(int status, String msg, T data, String url, Long host) { this.status = status; this.msg = msg; this.data = data; this.url = url; this.host = host; } public ResponseResult(int status, String msg, T data, Long host) { this.status = status; this.msg = msg; this.data = data; this.host = host; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public Long getHost() { return host; } public void setHost(Long host) { this.host = host; } }
這個類爲總體服務定義的狀態碼,能夠使用枚舉實現ide
public class ResponseStatusCode { /** * OK */ public static final int OK = 0; /** * 未知異常 */ public static final int UNKNOW_EXCEPTION = 100; /** * 參數異常 */ public static final int ARGUMENT_EXCEPTION = 104; /** * 自定義異常 */ public static final int ARGUMENT_EXCEPTION = XXX; }
自定義異常類繼承RuntimeExceptionthis
public class CustomException extends RuntimeException{ public static final long serialVersionUID = 1L; private int status; private Long host; public CustomException(int status) { this.status = status; } /** * 拋出異常使用自定義的異常碼 * @param status 自定義異常碼 * @param message 異常信息 */ public CustomException(int status, String message) { super(message); this.status = status; } public CustomException(String message, Throwable cause, int status) { super(message, cause); this.status = status; } public CustomException(Throwable cause, int status) { super(cause); this.status = status; } public CustomException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, int status) { super(message, cause, enableSuppression, writableStackTrace); this.status = status; } public CustomException(int status, Long host) { this.status = status; this.host = host; } /** * 拋出異常使用自定義的異常碼 * @param status 自定義異常碼 * @param message 異常信息 * @param host 主機 */ public CustomException(int status, String message, Long host) { super(message); this.status = status; this.host = host; } public CustomException(String message, Throwable cause, int status, Long host) { super(message, cause); this.status = status; this.host = host; } public CustomException(Throwable cause, int status, Long host) { super(cause); this.status = status; this.host = host; } public CustomException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, int status, Long host) { super(message, cause, enableSuppression, writableStackTrace); this.status = status; this.host = host; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public Long getHost() { return host; } public void setHost(Long host) { this.host = host; } }
使用spring自帶controller響應處理類,使用全局封裝類封裝返回url
@ControllerAdvice public class CustomResponseAdivce implements ResponseBodyAdvice<Object> { // 這個方法表示對於哪些請求要執行beforeBodyWrite,返回true執行,返回false不執行 @Override public boolean supports(MethodParameter methodParameter, Class aClass) { return true; } // 對於返回的對象若是不是最終對象ResponseResult,則選包裝一下 @Override public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) serverHttpRequest; String url = servletServerHttpRequest.getServletRequest().getRequestURL().toString(); if (!(o instanceof ResponseResult)) { ResponseResult responseResult = null; // 由於handler處理類的返回類型是String,爲了保證一致性,這裏須要將ResponseResult轉回去 if (o instanceof String) { responseResult = ResponseResult.ok(o, url); ObjectMapper mapper = new ObjectMapper(); String responseString = ""; try { responseString = mapper.writeValueAsString(responseResult); } catch (JsonProcessingException e) { e.printStackTrace(); } return responseString; }else { responseResult = ResponseResult.ok(o, url); return responseResult; } } return o; } }
業務中經過拋出異常的方式來處理錯誤,在此處能夠全局處理spa
@RestControllerAdvice public class ExceptionAdvice { private Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 出錯服務端口 */ @Value("${server.port}") private int servicePort; /** * 封裝異常 * * @param req 請求 * @param e 異常類 * @return 返回封裝類 */ @ExceptionHandler(value = Exception.class) public ResponseResult jsonErrorHandler(HttpServletRequest req, Exception e) { logger.error(req.getRequestURL().toString(), e); Long host = null; //分配異常碼與host int status = ResponseStatusCode.UNKNOW_EXCEPTION; String msg = e.getMessage(); if (e instanceof IOException || e instanceof CustomIOException) { status = ResponseStatusCode.IO_EXCEPTION; if (e instanceof CustomIOException){ host = ((CustomIOException) e).getHost(); } } else if (e instanceof NullPointerException || e instanceof CustomNullPointerException) { status = ResponseStatusCode.NULL_EXCEPTION; if (e instanceof CustomNullPointerException){ host = ((CustomNullPointerException) e).getHost(); } } else if (e instanceof SQLException || e instanceof CustomSQLException) { status = ResponseStatusCode.SQL_EXCEPTION; if (e instanceof CustomSQLException){ host = ((CustomSQLException) e).getHost(); } } else if (e instanceof ArgumentException) { status = ResponseStatusCode.ARGUMENT_EXCEPTION; if (((ArgumentException) e).getHost() != null && !((ArgumentException) e).getHost().equals(0)){ host = ((ArgumentException) e).getHost(); } } else if (e instanceof CustomException) { status = ((CustomException) e).getStatus(); if (((CustomException) e).getHost() != null && !((CustomException) e).getHost().equals(0)){ host = ((CustomException) e).getHost(); } } else if (e instanceof UndeclaredThrowableException) { Throwable targetEx = ((UndeclaredThrowableException) e).getUndeclaredThrowable(); if (targetEx != null) { msg = targetEx.getMessage(); } } //獲取出錯服務ip int ip = 0; try { ip = Integer.valueOf( Arrays.stream( InetAddress.getLocalHost().getHostAddress() .split("\\.")).collect(Collectors.joining())); } catch (Exception e1) { } return ResponseResult.fail( status, msg, req.getRequestURL().toString(), host == null? Long.valueOf(String.valueOf(ip) + servicePort): host); } }
上游服務得到下游服務的響應的處理類3d
public class ResponseHandler<T> { /** * 處理調用遠程接口的返回 * @param responseResult * @return */ public T handler(ResponseResult<?> responseResult) { int statusToken = responseResult.getStatus(); String msg = responseResult.getMsg(); Long host = responseResult.getHost(); if (ResponseStatusCode.OK != statusToken){ exceptionHandler(statusToken,msg, host); } return (T) responseResult.getData(); } /** * 處理異常 * @param statusToken 狀態碼 * @param msg 錯誤消息 * @param host 主機 */ private static void exceptionHandler(int statusToken, String msg, Long host) { if (ResponseStatusCode.IO_EXCEPTION == statusToken) { throw new CustomIOException(msg, host); } else if (ResponseStatusCode.NULL_EXCEPTION== statusToken) { throw new CustomNullPointerException(msg, host); } else if (ResponseStatusCode.SQL_EXCEPTION== statusToken) { throw new CustomSQLException(msg, host); } else if (ResponseStatusCode.ARGUMENT_EXCEPTION== statusToken) { throw new ArgumentException(msg, host); } else if (ResponseStatusCode.UNKNOW_EXCEPTION== statusToken) { throw new UnknowException(msg, host); } else { throw new CustomException(statusToken, msg, host); } } }
上面是全部服務都須要擁有的類,而後是幾個調用的小例子code
@RestController @RefreshScope public class DcController { final ClientService clientService; @Autowired public DcController(ClientService clientService) { this.clientService = clientService; } @GetMapping("exception1") public String exception1() { throw new ArgumentException("錯誤"); } @GetMapping("exception2") public int exception2() { throw new CustomException(250, "業務異常,自定義異常碼"); } @GetMapping("exception3") public int exception3(){ return new ResponseHandler<List<MetaVO>>().handler(clientService.generateList()).get(0).getDbid(); } @GetMapping("list") public List<MetaVO> generateList() { List<MetaVO> list = new ArrayList<>(); for (int i = 0; i < 2; i++) { MetaVO metaVO = new MetaVO(); metaVO.setDbid(i); list.add(metaVO); } return list; } }
以上就是spring cloud的全局封裝實踐,開發人員編寫業務邏輯不須要考慮返回的封裝,只須要考慮業務和自定義的狀態碼而已