Факторинг попробуйте поймать
У меня есть приложение Java EE с десятками веб-сервисов, использующих один и тот же шаблон:
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();
}
}
Можно ли факторизовать этот кусок кода?
Ответы
Ответ 1
Если это среда JAX-RS, см. ответ Tunaki, обращение с этим специально предназначено и замечательно просто.
Если нет:
У вас может быть функциональный интерфейс, принимающий функцию, которая может генерировать исключения и возвращает 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();
}
}
... и используйте его через лямбда:
public Response myWebService1() {
return respond(() -> {
// Do stuff here, return a Response or throw / allow throws on error
});
}
Ответ 2
Так как это в контексте 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 обнаруживает этот класс при сканировании. Это гарантирует, что везде, где в вашем коде, если a MyCustomException
выбрано (и не явно выловлено), возвращается ответ 409. Код в вашем приложении просто станет:
public Response myWebService1() {
// do something, and don't catch anything; just care about the happy path
}
Иерархия исключений правильно учтена. Если код приложения выбрасывает MyCustomExceptionMapper
, JAX-RS будет искать перехватчик исключений, зарегистрированный в этом типе, и пойдет вверх по суперклассу, если он не сможет его найти: таким образом, может быть исключение catch-all Mapper обрабатывает каждый другой случай.
Ответ 3
Если все методы обрабатывают исключения одинаково, вы можете извлечь обработку исключений внешнему методу:
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);
}
}
Ответ 4
Конечно, это возможно. У нас есть решение, которое выглядит так:
} catch (Exception exception) {
exceptionConverter.convertAndThrow(exception);
}
чтобы унифицировать повторное бросание исключений на основе исключенного исключения.
Таким образом, преобразователь исключений является одним из центральных мест, где мы "переключаемся" над типом исключения и делаем то, что нужно сделать. Конечно, центральный элемент здесь: все ваши классы нуждаются в точно такой же обработке для входящих исключений.
Мы даже делаем еще один шаг вперед и допускаем дикое сочетание потенциальных "причин ввода", но у нас также есть теги расширенные, чтобы гарантировать, что преобразование всегда дает ожидаемый результат.
Обратите внимание: мой ответ - это рефакторинг этих каскадов "поймать". Вы все равно можете обратиться к решению TJs; но имейте в виду: этот подход добавляет определенную сложность, введя этот Runnable аспект.
Ответ 5
@Ответ 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();
}
}
С анонимным классом вместо выражения лямбда:
Response response = respond(new Responder() {
@Override
public Response handleRequest() throws Exception {
...
return Response.ok().build();
}
});