【问题标题】:In Java or Scala, is there a way to add a callback to an exception so that when the exception is caught, the callback is invoked?在 Java 或 Scala 中,有没有办法为异常添加回调,以便在捕获到异常时调用回调?
【发布时间】:2022-12-11 10:56:51
【问题描述】:

问题陈述:我有一个 RPC 客户端 lib 1,它被许多服务使用,所有代码都在一个单一的 repo 中。此 RPC 客户端库 1 抛出一组运行时异常 (S1)。 现在有另一个 RPC 客户端 lib 2,我尝试将所有服务从使用 lib 1 迁移到 lib 2。但是,RPC 客户端 lib 2 抛出一组不同的运行时异常 (S2)。 如果 RPC 客户端异常语义从 S1 更改为 S2,我正在尝试获取将受到影响的服务列表。

方法一:通读当前使用 RPC 客户端库 1 的所有服务的代码。显然,这种方法不可扩展,因为有许多服务和异常处理逻辑可能很复杂且不直接。

方法二:静态分析。但是,我不确定是否有任何静态分析可以检测到 RPC lib 抛出的异常在调用堆栈的上方许多级别被捕获的情况。

方法三:探索是否有一种方法可以注册异常的回调,并期望在捕获到异常时调用回调。理想情况下,当调用回调时,它会提供有关异常捕获位置的信息。

【问题讨论】:

    标签: java scala exception error-handling analysis


    【解决方案1】:

    解决方案 1 可以工作。如果您只担心不同的语义,那么在客户端代码中搜索 lib1 定义的异常的出现就足够了。如果客户端代码捕捉到更通用的异常(例如 Exception 或 Throwable),那么精确的语义就无关紧要了,您可以不理会该代码。

    假设:

    • Lib1 的异常是特定的,不会被其他代码抛出(例如,不是 RuntimeException 但 Lib1Exception 没问题)。
    • Lib1 和 lib2 抛出不同的异常,但原因相同:如果 lib1 抛出异常,lib2 也会抛出异常(反之亦然)。

    【讨论】:

      【解决方案2】:

      “方法 3”是一个“不行”。我会选择“方法 4”:编写一个包装器客户端库(L3?),它会抛出您想要的异常。

      最初看起来可能需要做更多的工作,但当您改变主意并想回到 L1 时,或者当您想要尝试 L4、L5 和 L6 时,肯定会收回成本。单元测试也可能会容易得多。

      【讨论】:

      • 我喜欢这个答案胜过我自己的答案:)
      【解决方案3】:

      听起来像 AspectJ 切入点“在捕获之后”(不存在)。现有的是“投掷后”

      https://coderanch.com/t/498256/frameworks/AOP-pointcut-CATCH-block

      https://www.eclipse.org/aspectj/doc/next/progguide/printable.html#the-handler-join-point

      How to intercept method which handles its own exceptions using AspectJ

      Spring AOP - Invoking advice from catch block

      也许 java 代理/字节码操作会有所帮助

      Using Instrumentation to record unhandled exception

      您可以考虑使用 Scalameta(或 Scalafix)在整个代码库中检测 catch 块

      import scala.meta._
      
      val transformer = new Transformer {
        override def apply(tree: Tree): Tree = tree match {
          case q"try $expr catch { ..case $cases } finally $expropt" =>
            val cases1 = cases.map {
              case p"case $pat if $expropt => $expr" =>
                pat match {
                  case p"${pat: Pat.Var}: $tpe" =>
                    p"""case $pat: $tpe if $expropt =>
                      println("Exception " + ${pat.name} + " is caught")
                      $expr
                    """
                  case p"$pat" =>
                    p"""case $pat if $expropt =>
                      println("Exception " + ${pat.toString} + " is caught")
                      $expr
                    """
                }
            }
            q"try $expr catch { ..case $cases1 } finally $expropt"
          case _ => super.apply(tree)
        }
      }
      
      transformer(q"""
        try {
          1 / 0
        } catch {
          case e: RuntimeException =>
            println(e)
            throw e
        }
      """)
      
      //try {
      //  1 / 0
      //} catch {
      //  case e: RuntimeException =>
      //    println("Exception " + e + " is caught")
      //    {
      //      println(e)
      //      throw e
      //    }
      //}
      
      transformer(q"""
        try {
          1 / 0
        } catch {
          case _ =>
            println("swallowed")
        }
      """)
      
      //try {
      //  1 / 0
      //} catch {
      //  case _ =>
      //    println("Exception " + "_" + " is caught")
      //    println("swallowed")
      //}
      
      transformer(q"""
        try {
          1 / 0
        } finally {
          case _ =>
            println("not catching")
        }
      """)
      
      //try {
      //  1 / 0
      //} finally {
      //  case _ =>
      //    println("not catching")
      //}
      

      https://scalameta.org/

      https://www.scala-sbt.org/1.x/docs/Howto-Generating-Files.html

      Scala conditional compilation

      Macro annotation to override toString of Scala function

      How to merge multiple imports in scala?

      https://scalacenter.github.io/scalafix

      对于 Java 源,Spoon (https://spoon.gforge.inria.fr) 可以替代 Scalameta。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-03-28
        • 2011-03-25
        • 2014-06-30
        • 2012-02-03
        • 2020-09-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多