Подтвердить что ты не робот

Факторинг попробуйте поймать

У меня есть приложение 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();
    }
}

Можно ли факторизовать этот кусок кода?

4b9b3361

Ответ 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();
    }
});