在我的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。我不确定这是否重要。
答案 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完全相同地处理异常。
但有一点需要注意:反射机制会看到代理抛出的异常,该异常不在原始服务的方法签名中。这是可能的,因为:
因为使用的反射机制来自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并冒泡。