我有一个Spring JobRunner组件,该组件具有一个run()
方法,该方法会引发我的自定义异常:
public Response run(final Request request, final String id) {
try {
execution = jobLauncher.run(job, parameters);
} catch (JobExecutionAlreadyRunningException e) {
throw new MyCustomException("already running ", e);
} catch (JobRestartException e) {
throw new MyCustomException("Restart Exception", e);
}
return generateResponse(request, id, execution);
}
在服务类中,我在run()
内调用此process()
方法
protected Response process(final Request request, final String id) {
//get entity
//save
//bla bla
Response response;
try {
response = jobRunner.run(request, id);
updateStatus(entity, response.getStatus(), "");
} catch (MyCustomException ex) {
updateStatus(entity, FAILED);
throw new MyCustomException(ex.getMessage(), ex);
}
}
您会注意到我在捉住MyCustomException
并再次抛出。我必须赶上去,因为我需要在此服务类中相应地更新状态,因为它具有存储库依赖关系以更新db中的状态以进行跟踪。
我也要抛出这个错误,因为附加到此自定义异常的是针对API请求的ControllerAdvice
,因此它会做出相应的响应。
我想知道的是,这是好的设计还是不好的做法?我有什么可以改变的?
答案 0 :(得分:2)
根据情况,这可能是一种好的做法,也可能是不好的做法,也可能是不必要的。当您使用以下方法捕获并引发新异常时:
throw new MyCustomException(ex.getMessage(), ex);
您会得到一个新异常,它带有指向该行代码的新堆栈跟踪,并且您将原始异常与其堆栈跟踪保持为“原因”。
如果原始异常是在异步上下文中产生的,那么这是一个好习惯。这是反应式编程的一个常见问题:当通过某些异步操作创建异常时,其堆栈跟踪没有引用调用代码。在捕获点创建一个新异常会添加查找导致该异常的操作所需的上下文。
如果原始异常包含应保密的特权信息,则这是一个不好的做法。在这种情况下,应创建新的异常而没有原因,并且应记录原始异常。该代码应如下所示:
log.error(ex);
throw new MyCustomException("whoopsie");
} catch (MyCustomException ex) {
updateStatus(entity, FAILED);
throw ex;
}
答案 1 :(得分:1)
实际上这是一个很好的问题。
我看到您正在按层次划分关注点。对于例外情况,您应该这样做。
而且您还应该知道要抛出的异常类型(已检查还是未检查)。
JobCustomException
我建议您为您的工作创建一个检查异常。在这里,您将封装与作业错误相关的所有详细信息。通过使用检查的异常,您可以实现两件事:
来自Java Docs:
如果可以合理预期客户会从异常中恢复过来, 使其成为检查异常。如果客户无能为力 从异常中将其设为未经检查的异常
ServiceCustomException
然后,可以为您的服务层创建另一个异常。如果您认为不再可以处理这种异常,则应该从RuntimeException扩展它(当然,除了ControllerAdvice)。
转换或引入其他信息也可能有用。
最后,虽然您可能有一个通用的Job异常,但是可能有许多客户端类(服务)可能引发不同的基于运行时的异常,以添加有关发生错误的业务流程的更多业务详细信息。
投掷,接住和重新投掷
只要您注意上述项目符号,您就做对了。这就是抛出异常的全部要点。
异常不必是终端错误,尽管有人认为RuntimeException是唯一接受的异常类型。
大多数精心设计的对象的API和框架都使用适当的Checked异常来与客户端类通信,可能会发生什么错误。