业务异常
场景:系统提供一个API,用于修改用户信息,首先我们定义ServiceException,用来表示业务逻辑受理失败,它仅表示我们处理业务的时候发现无法继续执行下去。
自定义业务异常类:
/*** 业务受理失败异常*/public class ServiceException extends RuntimeException {// 接收reason参数用来描述业务失败原因.public ServiceException(String reason) {super(reason);}}
在修改用户信息前,需要做如下判断:
- 要修改的用户 ID 不存在;
 - 用户被锁定,不允许修改;
 - 乐观锁机制发现用户已经被人修改过;
 
我们看下 Service 代码如何编写:
/*** 修改用户信息** @param user* 要修改的用户数据*/public void updateUser(User user) {User userOrig = userDao.getUserById(user.getUserID());if (null == userOrig) {throw new ServiceException("用户不存在");}if (userOrig.isLocked()) {throw new ServiceException("用户被锁定,不允许修改");}if (!user.getVersion().equals(userOrig.getVersion())) {throw new ServiceException("用户已经被别人修改过,请刷新重试");}// TODO 保存用户数据 ...}
接下来考虑两个问题:
- 为什么定义一个运行时异常,而非受查异常
 
如果是一个受查异常,那么意味着 Controller 必须要处理你的异常,并且如果有一天你的业务逻辑变了,可能多一种检查项,就需要增加一个异常,反之需要删除一个异常,那么你的方法签名也需要改变,Controller 也随之要改变,这又变成了紧耦合。
- Controller 层如何处理 Service 层抛出的异常
 
有两种选择:
- 在 Controller 层使用 try-catch 进行处理;
 - 直接把异常抛给上层框架统一处理;
 
第一种方式是不可取的,注意我们抛出的 ServiceException,它仅仅逻辑处理异常,并且我们的方法前面没有声明throws ServiceException,这表示他是一个非受查异常,Controller 也没有必要关心会发生什么异常。
如此一来,我们只需要全局统一处理下 ServiceException 就可以了,Spring 为我们提供了 ControllerAdvice 机制。
@ControllerAdvice(basePackages = { "com.xxx.xxx.bussiness.xxx" })public class ModuleControllerAdvice {private static final Logger LOGGER = LoggerFactory.getLogger(ModuleControllerAdvice.class);private static final Logger SERVICE_LOGGER = LoggerFactory.getLogger(ServiceException.class);/*** 业务受理失败*/@ResponseBody@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)@ExceptionHandler(ServiceException.class)private JSONResult handleServiceException(ServiceException exception) {String message = "业务受理失败,原因:" + exception.getLocalizedMessage();SERVICE_LOGGER.info(message);JSONResult json = new JSONResult();json.serCode(500001);// 500000表示系统异常,500001表示业务逻辑异常json.setMessage(message);return json;}}
作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/zz1ugo 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
