Currently, when using Spring AOP with Kotlin, UndeclaredThrowableException is thrown when a method with advice throws a checked exception. There is a workaround in Kotlin in that you can add @Throws annotations to methods, but this is not really feasible as it would require annotating every method of every project, and methods in other libraries might not be under your control.

What would solve this problem would be to have an option in Spring to disable the throwing of UndeclaredThrowableException and simply throw the original exception.

Comment From: ryandanielspmc

Looks like the relevant code is here: https://github.com/spring-projects/spring-framework/blob/9bd3a535cd524cf771423e67feeea523ac985f87/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java#L752-L759

Comment From: caio-bos-ifood

This issue is also impacting me when using resilience4j circuit breaker. The AOP is encapsulating the real exception so the circuit breaker is never switched to OPEN.

Comment From: jhoeller

The reason for that custom UndeclaredThrowableException throwing logic in CglibAopProxy is that interface-based proxies unavoidably get such a UndeclaredThrowableException out of the JDK implementation, so for consistency with JdkDynamicAopProxy, we manually enforce it in the CGLIB variant.

So whatever we do about it, we can only do it for CGLIB proxies. @sdeleuze what's your take on this? Is CGLIB+Kotlin a common enough case to be handled specifically - or on the inverse, are JDK proxies with Kotlin uncommon enough? Are checked exceptions without explicit declaration common enough in Kotlin?

Generally speaking for the scenarios above, why the need for checked exceptions coming out of an interceptor to begin with? From my perspective, this is bad practice with Java-based AOP, even more so with Kotlin-based AOP, and it's also a general Java interoperability issue when used in Kotlin-based arrangements.

Comment From: ryandanielspmc

@jhoeller

why the need for checked exceptions coming out of an interceptor to begin with?

In our case, we wanted to add logging around @Service methods with AOP. And in this scenario, the logging code should not change the behavior of the methods being logged.

Additionally, this method of logging would allow us to:

  • Avoid manual modification of dozens of methods with identical boilerplate code to add logging
  • Avoid modification of each of these again later if we want to change how the logging works
  • Have cleaner code by not mixing the logging aspect with the business logic

Comment From: ryandanielspmc

@jhoeller

Are checked exceptions without explicit declaration common enough in Kotlin?

This is what happens the majority of the time as Kotlin does not require declaration of checked exceptions.

Comment From: jhoeller

I've just realized that - within Spring AOP - we'd even turn a checked exception thrown from a Kotlin target method into an UndeclaredThrowableException, not just a checked exception thrown from an interceptor (which is the primary purpose of that check). While this works fine with Java code that explicitly declares those exceptions on the target method (so undeclared exceptions can only really come out of the interceptor), it's really not appropriate for Kotlin code since it enforces a non-idiomatic @Throws declaration there. Thanks for raising this, and I'm sorry that we missed it the first time around.

For 5.3.3, I'll add a bypass that we'll always propagate the original exception for methods declared on Kotlin types.

Comment From: jhoeller

This is committed now, as default behavior for Kotlin code behind CGLIB proxies.

Please give the upcoming 5.3.3 snapshot a try and let me know whether it works for your scenario: https://repo.spring.io/snapshot/org/springframework/spring-framework-bom/5.3.3-SNAPSHOT/

Comment From: sdeleuze

Thanks @jhoeller for those changes.

Comment From: ryandanielspmc

@jhoeller Thanks for the change!

Comment From: aslak-dirdal

@jhoeller Would it be possible to also get this fix in 5.2.15?

Comment From: jhoeller

It's proven itself in 5.3.x for several months already, without any backlash, so let's schedule a backport for 5.2.15 indeed.

Comment From: aslak-dirdal

Great. Thanks!

On 21 Apr 2021, at 11:13, Juergen Hoeller @.***> wrote:

It's proven itself in 5.3.x for several months already, without any backlash, so let's schedule a backport for 5.2.15 indeed.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/spring-projects/spring-framework/issues/23844#issuecomment-823910448, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAQ4FIDM465VIRUXSFGLAWLTJ2JNNANCNFSM4JDJFPUQ.