与 Java 不同,您不必声明 Groovy 方法抛出的(已检查)异常,因为任何未声明的已检查异常都包装在 UndeclaredThrowableException 中。
您似乎暗示Groovy用 UndeclaredThrowableException 包装了已检查的异常,但事实并非如此。如果 Grails 服务抛出未经检查的异常,该异常最终会被 UndeclaredThrowableException 包装,但这是一种 java.lang.reflection 机制,仅在涉及代理类时才会发生。
发生这种情况是因为涉及到 Grails 服务。我不确定至少有一个代理类到底涉及多少个代理类:一个执行事务处理的类(由 Spring 提供)。
该类将使用事务包装 Service 中的任何方法,并在发生 RuntimeException 时回滚事务。默认情况下,Spring 事务管理在检查异常的情况下不会回滚。
爪哇
这在普通的旧 java 中是有意义的,因为开发人员会在应用程序代码中看到异常,并且会被警告要对其进行处理。如果开发人员很聪明,他将在事务范围内处理任何异常。如果他不回滚事务,他基本上是在说:“在这种情况下,事务提供的数据完整性对我来说并不重要。我会以其他方式从这个错误中恢复过来”</p>
时髦的
这在 Groovy 世界中没有意义,因为 Groovy 编译器不强制处理异常。它实际上以与 RuntimeExceptions 完全相同的方式处理异常。
但是有一个警告:反射机制看到代理抛出的异常不在原始服务的方法签名中。这是可能的,因为:
- Groovy 不强制处理异常
- 代理方法总是可以抛出任何 Throwable(检查 InvocationHandler JavaDoc)
因为使用的反射机制来自Java,所以必须符合Java规则。所以它必须将异常包装在 RuntimeException 中,.. 在这种情况下是 UndeclaredThrowableException。
圣杯
现在它变得非常棘手,因为如果您从 Controller 调用 Service 方法并发生异常。您将看到一个 RuntimeException 冒泡(由于某些隐藏机制),但您的事务不会回滚(由于某些隐藏机制)。
这种行为非常危险,因为开发人员确实必须记住正确处理任何异常(编译器不会提供帮助),或者开发人员必须确保使用 @Transactional(rollbackFor = Throwable) 正确指示任何服务。
这是一个设计问题,我认为 Grails 的开发人员在他们最初设计它时忽略了它。但我认为默认行为是如此错误和如此危险,这真的应该改变。