业务异常

场景:系统提供一个API,用于修改用户信息,首先我们定义ServiceException,用来表示业务逻辑受理失败,它仅表示我们处理业务的时候发现无法继续执行下去。

自定义业务异常类:

  1. /**
  2. * 业务受理失败异常
  3. */
  4. public class ServiceException extends RuntimeException {
  5. // 接收reason参数用来描述业务失败原因.
  6. public ServiceException(String reason) {
  7. super(reason);
  8. }
  9. }

在修改用户信息前,需要做如下判断:

  • 要修改的用户 ID 不存在;
  • 用户被锁定,不允许修改;
  • 乐观锁机制发现用户已经被人修改过;

我们看下 Service 代码如何编写:

  1. /**
  2. * 修改用户信息
  3. *
  4. * @param user
  5. * 要修改的用户数据
  6. */
  7. public void updateUser(User user) {
  8. User userOrig = userDao.getUserById(user.getUserID());
  9. if (null == userOrig) {
  10. throw new ServiceException("用户不存在");
  11. }
  12. if (userOrig.isLocked()) {
  13. throw new ServiceException("用户被锁定,不允许修改");
  14. }
  15. if (!user.getVersion().equals(userOrig.getVersion())) {
  16. throw new ServiceException("用户已经被别人修改过,请刷新重试");
  17. }
  18. // TODO 保存用户数据 ...
  19. }

接下来考虑两个问题:

  1. 为什么定义一个运行时异常,而非受查异常

如果是一个受查异常,那么意味着 Controller 必须要处理你的异常,并且如果有一天你的业务逻辑变了,可能多一种检查项,就需要增加一个异常,反之需要删除一个异常,那么你的方法签名也需要改变,Controller 也随之要改变,这又变成了紧耦合。

  1. Controller 层如何处理 Service 层抛出的异常

有两种选择:

  • 在 Controller 层使用 try-catch 进行处理;
  • 直接把异常抛给上层框架统一处理;

第一种方式是不可取的,注意我们抛出的 ServiceException,它仅仅逻辑处理异常,并且我们的方法前面没有声明throws ServiceException,这表示他是一个非受查异常,Controller 也没有必要关心会发生什么异常。

如此一来,我们只需要全局统一处理下 ServiceException 就可以了,Spring 为我们提供了 ControllerAdvice 机制。

  1. @ControllerAdvice(basePackages = { "com.xxx.xxx.bussiness.xxx" })
  2. public class ModuleControllerAdvice {
  3. private static final Logger LOGGER = LoggerFactory.getLogger(ModuleControllerAdvice.class);
  4. private static final Logger SERVICE_LOGGER = LoggerFactory.getLogger(ServiceException.class);
  5. /**
  6. * 业务受理失败
  7. */
  8. @ResponseBody
  9. @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  10. @ExceptionHandler(ServiceException.class)
  11. private JSONResult handleServiceException(ServiceException exception) {
  12. String message = "业务受理失败,原因:" + exception.getLocalizedMessage();
  13. SERVICE_LOGGER.info(message);
  14. JSONResult json = new JSONResult();
  15. json.serCode(500001);
  16. // 500000表示系统异常,500001表示业务逻辑异常
  17. json.setMessage(message);
  18. return json;
  19. }
  20. }

参考:如何优雅的处理Java异常,可以参考这些建议

作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/zz1ugo 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。