Spring Cloud的全局封装实践

前言

跨应用的全局封装通过模仿java的异常抛出实现,通过各个服务使用相同的接口返回格式实现服务消息的传递,在规范化的同时快速定位真正出现问题的服务。

全局接口返回格式分为外部接口格式和内部接口格式,这里简化为内外接口格式一致。

整体流程如下:

clipboard.png

全局返回封装类

整个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;
    }

}

然后是结果

clipboard.png

clipboard.png

clipboard.png

总结

以上就是spring cloud的全局封装实践,开发人员编写业务逻辑不需要考虑返回的封装,只需要考虑业务和自定义的状态码而已

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值