Какова наиболее вероятная причина исключений, загадочно избегающих блока try-catch в этом случае?

Я использую Spring WebClient в проекте Kotlin следующим образом:

data class DTO(val name: String) @Component class Runner: ApplicationRunner { override fun run(args: ApplicationArguments?) { try { val dto = get<DTO>() } catch (e: Exception) { println("ERROR, all exceptions should have been caught in 'get' ") } } } inline private fun<reified TResult: Any> get(): TResult? { var result: TResult? = null try { result = WebClient.create("https://maps.googleapis.com/maps/api/nonexisting") .get() .retrieve() .bodyToMono<TResult>() .block() } catch (e: Exception) { println("WORKS AS EXPECTED!!") } return result } 

Клиент будет генерировать исключение, потому что API вернет 404. Однако исключение не поймано там, где оно должно быть, а именно в теле функции get , но оно распространяется на внешний обработчик исключений.

Интересно отметить, что это происходит, только если исключение WebClient . Если я заменил код в предложении try простым throw Exception("error") , исключение поймается там, где оно должно быть.

Точно так же, когда я меняю подпись на get не общего родового inline private fun get(): DTO? проблема также уходит.

Для исключения для исключения блок try-catch кажется фундаментальной ошибкой в ​​инструментах Kotlin. С другой стороны, тот факт, что это происходит только с классом WebClient указывает на то, что это проблема Spring. Или это может быть только я, используя инструменты неправильно.

Я действительно озадачен здесь и понятия не имею, как действовать дальше. Любые идеи о том, почему это может происходить, приветствуются. Просто для полноты, вот что выглядит в отладчике:

введите описание изображения здесь

РЕДАКТИРОВАТЬ

Проблема уходит после обновления Spring Boot до 2.0.0.M6, она все еще присутствует в M5.

Похоже, что это была проблема Весны, а не проблема Котлина. С другой стороны, было бы неплохо понять, как библиотека, которую вы включаете, может, по-видимому, заставлять программу нарушать законы языка программирования, на котором она написана.

Я пробовал код с Spring Boot версии 2.0.0.M5 и 2.0.0.M6 , и кажется, что поведение следующего блока отличается от двух версий:

 result = WebClient.create("https://maps.googleapis.com/maps/api/nonexisting") .get() .retrieve() .bodyToMono<TResult>() .block() 

где-то вдоль цепи, в Spring Boot 2.0.0.M5 , возвращается 2.0.0.M5 WebClientResponseException , в Spring Boot 2.0.0.M6 оно выбрасывается .

Если вы добавите e.printStackTrace() в свой внешний catch, вы заметите, что трассировка стека:

java.lang.ClassCastException: org.springframework.web.reactive.function.client.WebClientResponseException не может быть добавлено в com.example.demo.DTO на com.example.demo.Runner.run (Test.kt: 18) в org. springframework.boot.SpringApplication.callRunner (SpringApplication.java:780) в org.springframework.boot.SpringApplication.callRunners (SpringApplication.java:770) в org.springframework.boot.SpringApplication.afterRefresh (SpringApplication.java:760) в org .springframework.boot.SpringApplication.run (SpringApplication.java:328) в org.springframework.boot.SpringApplication.run (SpringApplication.java:1245) в org.springframework.boot.SpringApplication.run (SpringApplication.java:1233) в com.example.demo.DemoApplicationKt.main (DemoApplication.kt: 10)

Итак, на самом деле проблема заключается в том, что возвращенное WebClientResponseException будет DTO классу DTO в момент возврата вызова val dto = get<DTO>() . Это означает, что когда вы назначаете result = ... , проверка типа еще не завершена. Итак, если вы измените свой код, например, вызовите get<Object>() вместо get<DTO>() , он не ударит по блокам catch.

Если вы конвертируете его в байт-код в IntelliJ Idea, а затем декомпилируете его на Java, вы можете увидеть этот блок:

 public class Runner implements ApplicationRunner { public void run(@Nullable ApplicationArguments args) { try { Object result$iv = null; try { ResponseSpec $receiver$iv$iv = WebClient.create("https://maps.googleapis.com/maps/api/nonexisting").get().retrieve(); Mono var10000 = $receiver$iv$iv.bodyToMono((ParameterizedTypeReference)(new Runner$run$$inlined$get$1())); Intrinsics.checkExpressionValueIsNotNull(var10000, "bodyToMono(object : Para…zedTypeReference<T>() {})"); result$iv = var10000.block(); } catch (Exception var7) { String var5 = "WORKS AS EXPECTED!!"; System.out.println(var5); } DTO var2 = (DTO)result$iv; } catch (Exception var8) { String var3 = "ERROR, all exceptions should have been caught in 'get' "; System.out.println(var3); } } } 

Здесь вы можете заметить, что кастинг в DTO выполняется в точке возврата метода (который больше не является возвратом, потому что он встроен ), после внутреннего блока catch: результат DTO var2 = (DTO)result$iv; , Похоже, что это поведение для встроенных методов с параметрами типа reified.

Это связано с SPR-16025 (см. Соответствующую фиксацию ), поскольку расширение Kotlin использует внутренний вариант ParameterizedTypeReference , который был исправлен в Spring Framework 5.0.1 и транзитивно в Spring Boot 2.0.0.M6.

Обратите внимание, что если вы используете bodyToMono(TResult::class.java) с Spring Boot 2.0.0.M5, он будет работать так, как ожидалось.

Intereting Posts
Вызов функции в фрагменте представления пейджера из активности XML в / из Java / Kotlin, кросс-платформенный Почему я не могу получить значения моего файла JSON? Android Kotlin не может использовать list.sort () с lambda Kotlin VerifyError: неинициализированный объект существует на обратной ветви 90 Может ли кто-нибудь сказать мне, как работает компилятор kotlin? Какова его архитектура? Проблема Android в выборе и отмене выбора элементов в адаптере kotlin? Тип вывода с функциональными строителями Как исправить подпись обобщенного метода расширения в kotlin для разрешения «Ошибка вывода типа» в kotlin Как изменить переменную экземпляра из отдельной AsyncTask Летучие свойства в Котлине? Android – добавьте UNDO SnackBar для прокрутки, чтобы удалить общие интерфейсы с методами reified Собственная собственность Котлина свойство lateinit не инициализируется при восстановлении активности