保理尝试抓住

时间:2017-01-10 09:15:40

标签: java

我有一个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();
    }
}

这可以分解这段代码吗?

5 个答案:

答案 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();
    }
});