在这种情况下,异常逃避try-catch块的异常的最可能原因是什么?

Mar*_*dik 4 generics spring exception kotlin spring-webflux

我在像这样的Kotlin项目中使用Spring WebClient:

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
}
Run Code Online (Sandbox Code Playgroud)

客户端将抛出一个异常,因为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问题而不是Kotlin问题.另一方面,理解你所包含的库如何看起来会导致程序违反其编写的编程语言的规律,这仍然是很好的.

Utk*_*mir 7

我试图代码与Spring引导版本2.0.0.M52.0.0.M6,似乎以下块的行为是那些2个版本之间的不同:

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

沿着链的某个地方,在春季启动2.0.0.M5时,WebClientResponseException返回,在春季启动2.0.0.M6抛出.

如果e.printStackTrace()在外部catch中添加一个,您会注意到堆栈跟踪是:

java.lang.ClassCastException:org.springframework.web.reactive.function.client.WebClientResponseException无法在org的com.example.demo.Runner.run(Test.kt:18)中强制转换为com.example.demo.DTO. 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)at com.example.demo.DemoApplicationKt.main(DemoApplication.kt:10)

所以,实际上,就是返回的问题,WebClientResponseException试图将转换为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);
      }

   }
}
Run Code Online (Sandbox Code Playgroud)

在这里你可以注意到,在内部catch块之后,在方法返回点(不再是返回,因为它是内联的)完成了对DTO的转换:DTO var2 = (DTO)result$iv;.看起来这是具有reified类型参数的内联方法的行为.