这样搞——保证你的代码没有try-catch

来源:互联网 发布:淘宝双11营销策略分析 编辑:程序博客网 时间:2024/06/07 05:30

常常听到有技术圈的朋友抱怨,尤其是从其他语言转到Java语言的同行,说Java的try-catch语言让代码显得很凌乱。的确,作为一个Java Follower,笔者也觉得Java中的try-catch会导致代码很不整齐,易读性变差。那么有什么好办法让Java工程中尽量不出现try-catch语法块呢?办法还真有,请听我娓娓道来!

首先,笔者带领大家回顾一下Java的异常处理机制。如下图,Java中有个类叫做Throwable。该Throwable类有两个子类,一个是Error,一个是Exception。Error代表JVM系统级别的错误,通常是程序员不可控的,因此不需要程序员过多的关注。因此,在Java中提起异常时,通常指的是Exception类及其子类。

Java异常结构图

从上图中也可以看出,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<UserDTO> 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);    }}

上面是UserController对应的逻辑层的Service类,当用户不存在时,Service层直接抛出逻辑异常,这个异常会被DefaultExceptionHandler捕获,而UserController完全不需要感知该异常的存在。在来看一个Controller层抛出异常的例子:

@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。点击打开链接




                                             
1 0