常常听到有技术圈的朋友抱怨,尤其是从其他语言转到Java语言的同行,说Java的try-catch语言让代码显得很凌乱。的确,作为一个Java Follower,笔者也觉得Java中的try-catch会导致代码很不整齐,易读性变差。那么有什么好办法让Java工程中尽量不出现try-catch语法块呢?办法还真有,请听我娓娓道来!
首先,笔者带领大家回顾一下Java的异常处理机制。如下图,Java中有个类叫做Throwable。该Throwable类有两个子类,一个是Error,一个是Exception。Error代表JVM系统级别的错误,通常是程序员不可控的,因此不需要程序员过多的关注。因此,在Java中提起异常时,通常指的是Exception类及其子类。
从上图中也可以看出,Exception类的子类分为RuntimeException类和其他子类。这是需要我们重点关注的。其中RuntimeException类是unchecked异常类(是指那些不需要try-catch捕获或者显式抛出的异常类),而Exception类的其他子类是checked异常类(指代那些需要try-catch捕获或者显示抛出的类)。因此,我们的程序中之所以有太多try-catch代码块的原因,就在于我们抛出了太多的checked异常类。
因此,笔者觉得有一种方案可以考虑,我们是否可以将一个软件系统中的所有自定义异常类都定义为RuntimeException类的子类,比如可以叫做SystemException和LogicException。SystemException的子类代表是真的系统运行时错误引起的异常,而LogicException的子类代表那些并非是系统运行时错误引起的异常,比如“用户不存在”这种逻辑异常。
这样在我们的软件系统中,所有的异常都是RuntimeException。即便有些方法会抛出checked异常,我们可以在他的try-catch中二次抛出一个自定义的SystemException或者LogicException。通过这样的方式,我们的系统中就基本上不需要感知异常的处理模块了。
但毕竟异常处理是一个系统健壮性必不可少的一部分。那么我们怎么来处理我们自定义的SystemException和LogicException呢?相比熟练使用Spring框架的童鞋一定知道SpringMVC中有这样一个类:HandlerExceptionResolver。这个类的作用是捕获Spring在运行过程中的所有异常。网上关于这个类的介绍有很多,在此不再赘述。
本来我们写一个HandlerExceptionResolver的实现类,然后在Spring的配置文件中声明该bean即可。但是为了实现可插拔的设计,使用同博客玩转Spring——从拒绝filter开始相同的方式。我们来看代码:
public class DefaultExceptionHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
if (e instanceof BusinessException) {
System.out.println("this is a businessException");
BusinessException businessException = (BusinessException) e;
putErrorEnumToResponse(businessException.getErrorEnum(), httpServletResponse);
} else if (e instanceof SystemException) {
System.out.println("this is a businessException");
SystemException systemException = (SystemException) e;
putErrorEnumToResponse(systemException.getErrorEnum(), httpServletResponse);
} else {
System.out.println("this is a unknownException");
putErrorEnumToResponse(ErrorEnum.UNKNOWN_EXCEPTION, httpServletResponse);
}
return null;
}
private static void putErrorEnumToResponse(ErrorEnum errorEnum, HttpServletResponse response) {
response.setContentType("application/json;charset=utf-8");
response.addHeader("Content-Length", String.valueOf(errorEnum.toString().length()));
String result= JSON.toJSONString(HttpResult.failedResult(errorEnum));
try {
ServletOutputStream servletOutputStream = response.getOutputStream();
servletOutputStream.print(result);
servletOutputStream.flush();
} catch (IOException e) {
System.out.println("put error msg to response exception");
e.printStackTrace();
}
}
}
其中的ErrorEnum是一个枚举,它定义了系统中所有出现的异常信息。
瞧,仅仅上面 一个类,就解决了整个Web系统中的异常处理。业务Developer仅仅需要在异常发生的地方判断该异常是属于SystemException还是BusinessException,然后定义一个说明该Exception的ErrorEnum作为SystemException或者BusinessException的参数,然后直接使用throw语句抛出即可。由于SystemException和BusinessException都是RuntimeException的子类,因此他们不再需要使用try-catch捕获或者显示在方法定义中声明,十分方便。而且HandlerExceptionResolver为我们提供了统一的异常处理入口,可以让我们方便快捷,轻松愉快的完成整个异常系统的处理任务。下面是一个业务代码使用异常的例子:
@Service("userService")
public class UserServiceImpl implements IUserService {
@Resource(name = "userDao")
private UserDao userDao;
@Override
public HttpResult getUserByAccount(String account) {
UserDO userDO = userDao.getUserByAccount(account);
if (userDO == null) {
throw new BusinessException(ErrorEnum.USER_NOT_EXIST);
}
UserDTO userDTO = UserConvent.conventToUserDTO(userDO);
return HttpResult.successResult(userDTO);
}
}
@Controller
@RequestMapping(value = CommonUrl.UrlConstant.TEST_PREFIX)
public class TestExceptionController {
@RequestMapping(value = CommonUrl.UrlConstant.TEST_CONTROLLER_EXCEPTION, produces = CommonUtils.CONTENT_TYPE)
@ResponseBody
public String getUserByAccount(HttpServletRequest request, HttpServletResponse response) {
return CommonExecutor.execute(request, response, BaseParam.class, new CommonExecute() {
@Override
public HttpResult execute(BaseParam param) {
throw new SystemException(ErrorEnum.UNKNOWN_EXCEPTION);
}
});
}
}
可见,通过这样的方式解决系统中的异常处理还是很好的。
觉得本文好的话,别忘了关注我哦。
代码参见本人的github。点击打开链接
笔者开设了一个知乎live,详细的介绍的JAVA从入门到精通该如何学,学什么?
提供给想深入学习和提高JAVA能力的同学,欢迎收听https://www.zhihu.com/lives/932192204248682496