#java #vavr #resilience4j
#java #vavr #отказоустойчивость 4j
Вопрос:
Я хочу генерировать исключения, которые расширяются от Exception
if Try.ofCallable()
fails.
У меня есть вызываемый тип:
final Callable<MyResponse> decoratedCallable =
circuitBreakerService.getDecoratedMethod(
myArg1,
() -> myFunction(myArg1, myArg2, myArg3)
);
Я пытаюсь сделать что-то вроде этого:
Try.ofCallable(decoratedCallable).onFailure(throwable -> {
if (throwable instanceof CallNotPermittedException) {
throw new MyRuntimeExceptionA("msg1", throwable);
} else {
throw new MyRuntimeExceptionB("msg2", throwable);
}
});
Это работает (функция, которая обертывает два приведенных выше оператора, выдает правильное исключение MyRuntimeExceptionA и MyRuntimeExceptionB), если оба MyRuntimeExceptionA
и MyRuntimeExceptionB
расширяются RuntimeException
, но не если они расширяются Exception
.
Если они расширяются Exception
, то я не могу выбросить их из основной функции. IDE просит обернуть их в try / catch, чего я не хочу.
Комментарии:
1. Есть ли особая причина, по которой, если вы используете Vavr, вы не возвращаете
Try<MyResponse>
?2. В этом (казалось бы, сокращенном) контексте кажется, что вы попали в ловушку захватывающих новых библиотек, не полностью понимая, почему они существуют или должны использоваться. Попробуйте написать тот же код на стандартной Java и посмотрите, как это работает.
3. Основная цель использования
Try
— НЕ создавать исключений. Немного больше контекста о том, почему, можно найти в документации Vavr о побочных эффектах
Ответ №1:
У вас есть два варианта. Вы можете генерировать исключение при попытке развернуть Try
, получая значение с помощью следующего кода:
Try.ofCallable(decoratedCallable)
.getOrElseThrow(throwable -> {
if (throwable instanceof CallNotPermittedException) {
return new MyExceptionA("msg1", throwable);
} else {
return new MyExceptionB("msg2", throwable);
}
})
или переместите код сопоставления ошибок в перед развертыванием с аналогичным кодом:
Try.ofCallable(decoratedCallable)
.mapFailure(
Case(
$(instanceOf(CallNotPermittedException.class)),
throwable -> new MyExceptionA("msg1", throwable)
),
Case($(), throwable -> new MyExceptionB("msg2", throwable))
)
.get()
Оба решения будут генерироваться только при отмене, поэтому, если вы хотите выполнить ранний запуск, вам придется выполнить ранний запуск.
В противном случае я бы воспользовался советом, который другие опубликовали в комментариях, не создавать исключений, если вы используете Try
. Весь смысл использования Try
заключается в работе с полными функциями вместо частичных функций, которые могут генерировать исключения.
Ответ №2:
Я мало что знаю о vavr, но, просмотрев библиотеку javadoc, вы можете увидеть, что метод OnFailure принимает значение Consumer<? super Throwable> в качестве параметра. Проблема в том, что потребители не объявляют проверяемые исключения, поэтому вы никогда не сможете генерировать проверенные исключения из вашего lambda.
При этом, что я обычно делаю в этих случаях, так это создаю «обтекающий» класс, который будет принимать проверенные исключения, все, что этот обтекающий класс будет делать, это перехватывать любые проверенные исключения и оборачивать их в исключение во время выполнения. Например:
public class ThrowingConsumerHelper {
public static <T> Consumer<T> throwingConsumer(
ThrowingConsumer<T> consumer) {
return object -> {
try {
consumer.accept(object);
} catch (Throwable t) {
throw new RuntimeException(t);
}
};
}
@FunctionalInterface
public interface ThrowingConsumer<T> {
void accept(T t) throws Exception;
}
}
А затем используйте это следующим образом:
import static ThrowingConsumerHelper.throwingConsumer;
public static void main(String[] args) {
onFailure(throwingConsumer(object -> { throw new Exception("Bug"); }));
}
public static void onFailure(Consumer<? super Throwable> consumer) {
// Do something
}