服务器未被控制器捕获的异常抛出

时间:2013-11-14 20:28:53

标签: exception grails stripe-payments

在我的Grails服务中,我有以下代码:

def createCharge(chargeParams) {
  try {
    def charge = Charge.create(chargeParams)
  } catch (CardException e) {
    throw e
  }
}

从我的控制器中执行以下操作:

try  {
   service.createCharge(chargeParams)
} catch(CardException e) {

}

但是,我的控制器没有捕获重新抛出的CardException。如果我通过以下方法在RuntimeException中包装CardException:

throw new RuntimeException(e)

和/或从catch中删除签名只是捕获(e)而不键入它,它可以工作,但是我从异常中丢失了一些信息,比如消息。

作为注释,CardException是一个Exception,而不是RuntimeException。我不确定这是否重要。

4 个答案:

答案 0 :(得分:14)

与Java不同,您不必声明Groovy方法抛出的(已检查)异常,因为任何未声明的已检查异常都包含在UndeclaredThrowableException中。所以这个:

def createCharge(chargeParams) {
  try {
    def charge = Charge.create(chargeParams)
  } catch (CardException e) {
    throw e
  }
}

实际上与:

相同
def createCharge(chargeParams) throws UndeclaredThrowableException {
  try {
    def charge = Charge.create(chargeParams)
  } catch (CardException e) {
    throw new UndeclaredThrowableException(e)
  }
}

上述抛出的异常,显然不会被以下情况所捕获:

try  {
   service.createCharge(chargeParams)
} catch(CardException e) {

}

但它会被抓住:

try  {
   service.createCharge(chargeParams)
} catch(e) {

}

因为这只是一个简写:

try  {
   service.createCharge(chargeParams)
} catch(Exception e) {

}

答案 1 :(得分:8)

  

与Java不同,您不必声明Groovy方法抛出的(已检查)异常,因为任何未声明的已检查异常都包含在UndeclaredThrowableException中。

您似乎暗示 Groovy 通过UndeclaredThrowableException包装已检查的异常,但事实并非如此。如果Grails服务抛出未经检查的异常,则异常最终会被UndeclaredThrowableException包装,但这是一个java.lang.reflection机制,只有在涉及代理类时才会发生。

这恰好是因为涉及Grails服务。我不确定至少有一个代理类涉及多少代理类:一个执行事务处理的类(由Spring提供)。

该类将使用事务包装Service中的任何方法,并在发生RuntimeException时回滚事务。默认情况下,Spring事务管理在检查Exception时不会回滚。

<强>爪哇

这在普通的旧java中很有意义,因为开发人员会在应用程序代码中看到异常,并会被警告要对它做些什么。如果开发人员很聪明,他将在交易范围内处理任何例外情况。如果他不回滚交易,他基本上会说:“在这种情况下,交易提供的数据完整性对我来说并不重要。我将以其他方式从这个错误中恢复“

<强> Groovy的

这在Groovy世界中没有意义,因为Groovy编译器不强制处理异常。它实际上与RuntimeExceptions完全相同地处理异常。

但有一点需要注意:反射机制会看到代理抛出的异常,该异常不在原始服务的方法签名中。这是可能的,因为:

  1. Groovy不强制处理异常
  2. 代理方法总是可以抛出任何Throwable(检查InvocationHandler JavaDoc)
  3. 因为使用的反射机制来自Java,所以它必须符合Java规则。所以它必须在RuntimeException中包装Exception,在这种情况下是UndeclaredThrowableException。

    <强> Grails的

    现在它变得非常棘手,因为如果从Controller调用Service方法并发生异常。您将看到RuntimeException冒泡(由于某些隐藏机制)但您的事务将不会回滚(因为某些隐藏机制)。

    这种行为非常危险,因为开发人员必须记住正确处理任何异常(编译器无法帮助)或开发人员必须确保正确指示任何服务使用@Transactional(rollbackFor = Throwable)。

    这是一个设计问题,我认为Grails的开发人员在他们第一次设计时忽略了这一点。但我认为默认行为是错误的,而且非常危险,这应该真的改变。

答案 2 :(得分:2)

我认为问题的简单解决方案是向服务方法添加throws CardException语句,因此异常将不再包含在UndeclaredThrowableException中,并且控制器将捕获正确的异常类型。

答案 3 :(得分:0)

只需捕获UndeclaredThrowableException,从中获取消息,然后在需要时重新抛出。

catch (UndeclaredThrowableException e) {
    def s = e.getUndeclaredThrowable().getMessage()
    // do something with the message
    throw e
}

上面的代码片段将捕获您在代码中明确抛出的异常(例如CardException)。例如,不会捕获NullPointerException并冒泡。