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

常常听到有技术圈的朋友抱怨,尤其是从其他语言转到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。点击打开链接

笔者开设了一个知乎live,详细的介绍的JAVA从入门到精通该如何学,学什么?

提供给想深入学习和提高JAVA能力的同学,欢迎收听https://www.zhihu.com/lives/932192204248682496

 



 


                
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值