我有一个Java EE应用程序,其中包含许多使用相同模式的Web服务:
public Response myWebService1() {
try {
// do something different depending on the web service called
} catch (MyCustomException e1) {
return Response.status(409).build();
} catch (UnauthorizedException e2) {
return Response.status(401).build();
} catch (Exception e3) {
return Response.status(500).build();
}
}
这可以分解这段代码吗?
答案 0 :(得分:26)
如果这是一个JAX-RS环境,请参阅Tunaki's answer,处理此问题是特别适合的,非常简单。
如果不是:
你可以有一个功能接口接受一个可以抛出异常并返回Response
的函数:
@FunctionalInterface
public interface Responder {
Response handleRequest() throws Exception;
}
(正如Dici指出的那样,您可以将其设为通用ThrowingSupplier
或类似内容,因为您允许它抛出Exception
。)
然后有一个辅助方法接受它的实例:
private static Response respond(Responder responder) {
try {
return responder.handleRequest();
} catch (MyCustomException e1) {
return Response.status(409).build();
} catch (UnauthorizedException e2) {
return Response.status(401).build();
} catch (Exception e3) {
return Response.status(500).build();
}
}
...并通过lambda使用它:
public Response myWebService1() {
return respond(() -> {
// Do stuff here, return a Response or throw / allow throws on error
});
}
答案 1 :(得分:26)
由于这是在JAX-RS上下文中,因此有一种更好的方法,它不依赖于捕获许多不同的异常:使用ExceptionMapper
。这是JAX-RS 1.0的内置机制,它将异常类型转换为适当的Response
对象以发送给客户端。
在您的情况下,您可以在应用程序中定义以下类:
@Provider
public class UnauthorizedExceptionMapper implements ExceptionMapper<UnauthorizedException> {
public Response toResponse(UnauthorizedException e) {
return Response.status(401).build();
}
}
@Provider
public class MyCustomExceptionMapper implements ExceptionMapper<MyCustomException> {
public Response toResponse(MyCustomException e) {
return Response.status(409).build();
}
}
@Provider
public class CatchAllExceptionMapper implements ExceptionMapper<Exception> {
public Response toResponse(Exception e) {
return Response.status(500).build();
}
}
@Provider
注释告诉JAX-RS运行时在扫描时发现此类。这样可以确保在代码中的任何位置抛出MyCustomException
(并且未明确捕获),将返回409响应。您的应用程序中的代码将变为:
public Response myWebService1() {
// do something, and don't catch anything; just care about the happy path
}
正确考虑了异常层次结构。如果应用程序代码抛出MyCustomExceptionMapper
,JAX-RS将查找使用该类型注册的异常映射器,并且如果找不到它,它将上升到超级类:这样,可以有一个catch-all异常映射器处理所有其他情况。
答案 2 :(得分:6)
如果所有方法都以相同的方式处理异常,则可以将异常处理提取到外部方法:
public static Response exceptionHandler (Exception exc)
{
if (exc instanceof MyCustomException) {
return Response.status(409).build();
} else if (exc instanceof UnauthorizedException) {
return Response.status(401).build();
} else {
return Response.status(500).build();
}
}
public Response myWebService1() {
try {
// do something different depending on the web service called
} catch (Exception exc) {
return exceptionHandler(exc);
}
}
答案 3 :(得分:2)
当然,这是可能的。我们有一个看起来像这样的解决方案:
} catch (Exception exception) {
exceptionConverter.convertAndThrow(exception);
}
根据捕获的异常统一重新抛出异常。
因此,异常转换器是我们“切换”异常类型并执行需要完成的工作的中心位置。当然,这里的核心元素是:所有类都需要对传入的异常进行完全相同的处理。
我们甚至更进一步,允许混合潜在的“输入原因”,但我们还有广泛的单元测试,以确保转换始终给出预期的结果。
请注意:我的答案只是重构那些“捕获”级联。你仍然可以转向TJs解决方案;但请记住:通过引入Runnable方面,这种方法会增加一定的复杂性。
答案 4 :(得分:2)
@ T.J.Crowder的回答非常完美。但是对于那些不能使用Java 8的人来说,这就是如何用早期版本的Java实现它:
响应者界面:
public interface Responder {
Response handleRequest() throws Exception;
}
辅助方法:
private static Response respond(Responder responder) {
try {
return responder.handleRequest();
} catch (MyCustomException e1) {
return Response.status(409).build();
} catch (UnauthorizedException e2) {
return Response.status(401).build();
} catch (Exception e3) {
return Response.status(500).build();
}
}
使用匿名类而不是lambda表达式:
Response response = respond(new Responder() {
@Override
public Response handleRequest() throws Exception {
...
return Response.ok().build();
}
});