Skip to content

全局异常:不遵守一定规范就是鸡肋

分类:

全局异常最重要的是异常的约定。什么异常类型属于哪个异常分类、该如何处理,这样的约定是前端接口统一异常的关键。

Spring AOP的实现统一异常的方式:

java
/**
 * 全局异常处理
 * @author lee
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionConfig  {
	/**
	 * 兜底异常
	 * @return
	 */
	@ExceptionHandler(Exception.class)
	public ResponseJsonDTO handlerException(Exception e) {
		log.error(e.getMessage(),e);
		return ResponseJsonDTO.fail().msg(e.getMessage()).build();
	}
}

通常情况下,我们在实现业务的时候,根据业务的不同会有不一样的异常类型。比如,你正在开发一个与订单相关服务,在开发的时候有一个下单失败的异常,于是你可能这样定义该异常:

java
public class AddTradeException extends Exception {
}

像这样定义是完全没有问题的,问题是在你的系统有大量的异常类型时你需要在上面的全局异常代码上将他们的类型全部加到上面去。这是一个繁琐的过程,并且如果有人忘记加了,那该异常就会使用兜底异常的处理方式处理该异常。这就引出了这样一个问题:如果所有的异常的处理方式都和兜底异常的处理方式一样,那么我不就可以不用添加新的异常处理方式区处理该异常了呢?

于是我们的问题就变成了,如何保证所有的异常处理方式是统一的呢?答案是如果没有统一的规范,那不可能保证处理方式已一致的。比如我们可以定义订单异常统一为以下异常:

java
public class TradeException extends Exception {
	private String msg;//给用户的友好信息
	
	public TradeException(String msg) {
		super(msg);
		this.msg=msg;		
	}
	
	public TradeException(String msg,String message) {
		super(message);
		this.msg=msg;		
	}
	
	public TradeException(String msg,String message,Throwable throwable) {
		super(message,throwable);
		this.msg=msg;		
	}
	
	public String getMsg(){
		return msg;
	}
}

现在我们有了一个强约束, 规定了错误信息放哪,给的用户信息放哪,但是他有一个问题,不能适应变化。比如我现在下单异常有一种情况是有俩种出处理方式需要透出给用户选择的,该如何处理呢?从这个角度看是不可能有统一的方式处理,毕竟计划是永远跟不上变化的。但是我们依然可以在最初设计的时候将我们需要的异常种类划分清楚(用异常处理方式划分)。

但是这样还是不够的,很显然我们需要定义一套标准,并且为这套异常标准写工具。以下是我们为统一异常处理需要做的两件事: 1.根据异常处理方式划分异常种类:假如我们的业务中有以下异常APIException、TradeException,分别表示所有外部API异常和订单业务异常。他们的处理方式分别是解析错误码返回有用信息和解析将信息直接返回到用户端同时返回该异常的可操作选项。于是你可以通过给这两种操作方式定义不同的抽象类,然后具体实现继承以实现并适应标准。类似下面这样:

java
public abstract class CodeParseException extends Exception {
	private String code;//错误码
	
	public CodeParseException(String code) {
		super(code);
		this.code=code;		
	}
	
	public CodeParseException(String code,String message) {
		super(message);
		this.code=code;		
	}
	
	public CodeParseException(String code,String message,Throwable throwable) {
		super(message,throwable);
		this.code=code;		
	}
	
	public String getCode(){
		return code;
	}
}

但是很显然,这样的操作会与异常定义的见名知义相冲突。所以只能用来解析异常,别无它用。 2.为有自定义处理逻辑的异常定义接口或者抽象类,当异常有自己的处理逻辑时应该继承相关的类或者实现相关的接口,并且提供解析异常的实现方法。如下:

java
public abstract class CustomeParseException extends Exception {
	public abstract Object parseException();//处理异常的抽象方法
}

或者如下:

java
public interface CustomeParseException {
	public abstract Object parseException();
}

他们的本质是一样的,提供一个方法解析异常。抽象类的方式可以将该类直接加入统一异常配置。

你可能会有这样的疑问:既然我已经知道怎么处理该异常了,我为什么还要抛出该异常。首先这里的异常处理是指将异常信息解析成用户能够看懂并接受的样子,与我们编程如何处理异常是不一样的;其次,如标题所示,并不是所有人都了解遵守约定。总而言之,所有的需要统一的东西都一定是规范与约定先行的。如果你需要统一一个规范,那么你需要尽可能让不和规范的东西无法工作。想象一下,如果大家不按照HTTP协议规范来互联网还会如此繁荣吗?

注:以上是个人观点,有意见或建议欢迎评论指出。转载请标明作者与出处。

实践、认识、再实践、再认识,这种形式,循环往复以至无穷,而实践和认识之每一循环的内容,都比较地进到了高一级的程度。