前言
跨应用的全局封装通过模仿java的异常抛出实现,通过各个服务使用相同的接口返回格式实现服务消息的传递,在规范化的同时快速定位真正出现问题的服务。
全局接口返回格式分为外部接口格式和内部接口格式,这里简化为内外接口格式一致。
整体流程如下:
全局返回封装类
整个spring cloud服务的统一接口
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;
}
}
状态码常量类
这个类为整体服务定义的状态码,可以使用枚举实现
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;
}
自定义异常类
自定义异常类继承RuntimeException
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响应处理类,使用全局封装类封装返回
@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;
}
}
异常处理类
业务中通过抛出异常的方式来处理错误,在此处可以全局处理
@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);
}
}
接口响应处理类
上游服务获得下游服务的响应的处理类
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);
}
}
}
接口例子
上面是所有服务都需要拥有的类,然后是几个调用的小例子
首先是几个接口
@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的全局封装实践,开发人员编写业务逻辑不需要考虑返回的封装,只需要考虑业务和自定义的状态码而已