技术数据结构SpringFramework

Spring 两种全局异常比较---泛型,建造者设计模式

2016-09-24  本文已影响1063人  Rick____

把我两次书写全局异常捕获统一处理返回JSON的经验分享给大家。其中真的还是有点小进步的。里面涉及到泛型和建造者的使用,还是能学到挺多的。

实现的思路是

  1. 全局异常的抓取。
  2. 封装统一返回结果对象

第一种方式:我记得好像是参考阿里巴巴的黄勇同志写出来的。其实很简单通过ControllerAdvice做一Controller的AOP。然后通过拦截对应的Exception可以自定义返回对应的HttpStatus。然后封装Reponse统一返回数据即可。甚至连正常返回都可以封装为一个Exception返回,可以参照下面的StatusSuccess,具体实现比较简单。

别人亲测,这里有一个天大的坑,入门的小伙伴要注意了。事务回滚的异常处理是需要extend RunTimeException 而继承Exception 是没办法做到事务回滚的。

第二种方式:是我参考简书小伙伴的文章写出来的。另外加入了建造者设计模式,个人见解使用简单,代码可读性更好。实现思路更第一种类似,但是在代码上有了比较大的改动。尤其是泛型的使用,和设计模式的使用。

两种方式的比较:

  1. 弱点一:Controller返回的都是Object 。虽然可以满足大部分的业务,但是过了两个月,前端问你这个接口返回的是什么东西。多半你要找到你的dao层才能判断了。而第二种方式则可以避免这种问题。使用了泛型。很容易让你清楚的知道data里面放到是什么

  2. 弱点二:代码可读性更强了。不会在Service里面throw new StatusSuccess()了。这段代码始终让我感觉奇奇怪怪的又说不是是啥。
    第二种实现方式,则是在Controller里面使用Builder设计模式,让代码更加优雅帅气,MVC分层明显,代码可读性强,真的是一个很棒的实现方式。

第二种实现方式,直接上代码了。

1 在controller 的实现效果。代码简洁。可读性👍。

@RestController
@RequestMapping("/app/user")
public class ApiController {

    @RequestMapping("data")
    public RestResult<Object> text() throws Exception {
        throw new Exception();
    }

    @RequestMapping("getData")
    public RestResult<String> getData() throws Exception {
        return RestResultGenrator.build(
                xxxService.getData();
        );
    }
}

2 统一异常抓取。注意看我的RestResultBuild的构建。看起来更加美观。


/**
 * RestExceptionHandler
 *
 * @author zf
 * @date 9/23/16
 */
@ControllerAdvice(annotations = RestController.class)
public class RestExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(RestExceptionHandler.class);

    @ExceptionHandler
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    private <T> RestResult<T> runtimeExceptionHandler(Exception e){
        LOGGER.error("------->error !" ,e);
        return new RestResultBuilder<T>()
                .setErrorCode(ErrorCode.ERROR)
                .setMessage(ErrorCode.ErrorMessage.ERROR)
                .build();
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    private <T> RestResult<T> illegalParamsExceptionHandler(MethodArgumentNotValidException e) {
        LOGGER.error("---------> invalid request!", e);
        return new RestResultBuilder<T>()
                .setErrorCode(ErrorCode.ERROR_METHOD)
                .setMessage(ErrorCode.ErrorMessage.ERROR_METHOD)
                .build();
    }


    @ExceptionHandler(ExpireException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    private <T> RestResult<T> expireHandler(MethodArgumentNotValidException e) {
        LOGGER.error("---------> invalid expire!", e);
        return new RestResultBuilder<T>()
                .setErrorCode(ErrorCode.EXPIRED)
                .setMessage(ErrorCode.ErrorMessage.EXPIRED)
                .build();
    }

}

3 使用统一泛型处理,增强Controller的可读性。

/**
 * RestResult
 *
 * @author zf
 * @date 9/23/16
 */
public class RestResult <T>{
    private int errorCode;
    private String message;
    private T data;

    private RestResult(){}

    protected RestResult(int errorCode, String message, T data) {
        this.errorCode = errorCode;
        this.message = message;
        this.data = data;
    }

    public int getErrorCode() {
        return errorCode;
    }

    public RestResult setErrorCode(int errorCode) {
        this.errorCode = errorCode;
        return this;
    }

    public String getMessage() {
        return message;
    }

    public RestResult<T> setMessage(String message) {
        this.message = message;
        return this;
    }

    public T getData() {
        return data;
    }

    public RestResult<T> setData(T data) {
        this.data = data;
        return this;
    }
}

4 使用builder设计模式构建统一返回对象。

/**
 * RestResultBuilder
 *
 * @author zf
 * @date 9/23/16
 */
public class RestResultBuilder<T> {

    private int errorCode = ErrorCode.VALID;

    private String message =ErrorCode.ErrorMessage.VALID;;

    private T data ;

    protected RestResultBuilder<T> setErrorCode(int errorCode){
        this.errorCode = errorCode;
        return this;
    }

    public RestResultBuilder<T> setMessage(String message){
        this.message = message;
        return this;
    }

    public RestResultBuilder<T> setData(T data){
        this.data = data;
        return this;
    }

    public RestResult<T> build(){
        return new RestResult<T>(errorCode,message,data);
    }


}

5 我是为了不想写new三个字母,所以写了第五个类,Genrator。

public class RestResultGenrator {

    public static <T> RestResult<T> build(){
        return build(null);
    }

    public static <T> RestResult<T> build(T t){
        return new RestResultBuilder<T>().setData(t).build();
    }
}

下面是第一种实现方式。

  1. 全局异常抓取类
@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {
    Logger logger = Logger.getGlobal();

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Response handleHttpMessageNotReadableException(
            HttpMessageNotReadableException e) {
        logger.info("参数解析失败" + e);
        return new Response().failure("could_not_read_json");
    }

    /**
     * 405 - Method Not Allowed
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Response handleHttpRequestMethodNotSupportedException(
            HttpRequestMethodNotSupportedException e) {
        logger.info("不支持当前请求方法" + e);
        return new Response().failure("request_method_not_supported");
    }

    /**
     * 415 - Unsupported Media Type
     */
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Response handleHttpMediaTypeNotSupportedException(Exception e) {
        logger.info("不支持当前媒体类型" + e);
        return new Response().failure("content_type_not_supported");
    }

    /**
     * 500 - Internal Server Error
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(Exception.class)
    public Response handleException(Exception e) {
        e.printStackTrace();
        return new Response().failure(e.getMessage());
    }

    /**
     * - Internal Server Error
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(StatusSuccess.class)
    public Response handleSuccessStatus(StatusSuccess e) {
        logger.info("StatusSuccess");
        return new Response(Response.SUCCESS_CODE, e.getMessage(), e.getData());
    }

    /**
     * 500 - Internal Server Error
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ExpireException.class)
    public Response handleExipreStatus(Exception e) {
        logger.info("内部错误");
        return new Response().timeOut(e.getMessage());
    }

    /**
     * 500 - 内部错误
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(IllegalArgumentException.class)
    public Response parameterException(IllegalArgumentException e) {
        logger.info("非法参数" + e.toString());
        return new Response().failure(e.getMessage());
    }

    /**
     * 500 - Internal Server Error
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ShopStatusException.class)
    public Response errorShopStatus(Exception e) {
        logger.info("店铺状态异常");
        return new Response().shopStatusException(e.getMessage());
    }

    /**
     * 订单状态异常拦截器
     * 
     * @author yangyanchao
     * @date 2016年8月16日
     * @param e
     * @return
     */
    public Response orderStatusException(Exception e) {
        return new Response().shopStatusException(e.getMessage());
    }

}
  1. 统一返回实体类
/**
 * Response
 * 
 * @author zf
 * @date 16/3/21
 */
public class Response {

    private static final String OK = "success";
        private static final String ERROR = "failure";
    private static final String TIMEOUT = "expired";

    public static final int SUCCESS_CODE = 1;
    public static final int FAIL_CODE = 0;
    public static final int TOKEN_INVALID = -1;
    public static final int SHOP_ABNORMAL = -2;
    

    @JsonView(BaseView.BaseResponse.class)
    private int status;

    @JsonView(BaseView.BaseResponse.class)
    private String msg;

    @JsonView(BaseView.BaseResponse.class)
    private Object data;

    public Response success() {
        this.status = SUCCESS_CODE;
        this.msg = OK;
        return this;
    }

    public Response success(Object data) {
        this.status = SUCCESS_CODE;
        this.msg = OK;
        this.data = data;
        return this;
    }

    public Response failure() {
        this.status = FAIL_CODE;
        this.msg = ERROR;
        return this;
    }

    public Response() {
    }

    public Response(int status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }

    public Response expireException(String msg) {
        this.status = TOKEN_INVALID;
        this.msg = msg;
        return this;
    }

    public Response timeOut(String msg) {
        this.status = TOKEN_INVALID;
        this.msg = msg;
        return this;
    }
    
    public Response shopStatusException(String msg) {
        this.status = SHOP_ABNORMAL;
        this.msg = msg;
        return this;
    }

    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 void setData(Object data) {
        this.data = data;
    }

    public Response failure(String message) {
        this.status = FAIL_CODE;
        this.msg = message;
        return this;
    }

    public Object getData() {
        return data;
    }

}

3.service 使用例子

   /**
     * 保存用户
     *
     * @param principle
     *            用户信息
     * @param uadId
     *            用户地址id
     * @return 保存
     */
    public void save(Principle principle, Integer uadId) throws NotSamePeopleException, StatusSuccess {
        AsUserAddress userAddress = asUserAddressMapper.selectByPrimaryKey(uadId);
        if (userAddress.getUserId().equals(principle.getUserId())) {
            principle.setAddressId(uadId);
            AsUser u = new AsUser();
            u.setUserId(principle.getUserId());
            u.setAddressId(uadId);
            asUsersMapper.updateByPrimaryKeySelective(u);
            throw new StatusSuccess();
        } else {
            throw new NotSamePeopleException();
        }

    }

3 . 正确返回的异常处理,注意看这里是封装了data 的。

/**
 * OrderStatusException
 *
 * @author zf
 * @date 16/7/14
 */
public class StatusSuccess extends Exception{
    private static String msg = "操作成功";
    private Object data;
    public StatusSuccess(){
        super(msg);
    }

    public StatusSuccess(String msg) {
        super(msg);
    }

    public StatusSuccess(Object data) {
        super(msg);
        this.data = data;
    }

    public StatusSuccess(String message, Object data) {
        super(message);
        this.data = data;
    }

    public static String getMsg() {
        return msg;
    }

    public static void setMsg(String msg) {
        StatusSuccess.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

最后用postMan得出最终相同的结果。

Paste_Image.png
上一篇下一篇

猜你喜欢

热点阅读