业务异常
场景:系统提供一个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 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。