Does @AfterThrowing swallow or handle the exception? How would you actually suppress or translate it?
answer
- Observe-only, exception still escapes
- Advice throwing = masks the real cause
- @Around + proceed() to actually handle
- Repository + translator for persistence
- Around swallow can hide it from AfterThrowing
basics
~20 sNo. @AfterThrowing only observes the exception, then the original exception keeps propagating to the caller. To suppress, replace, or recover, use @Around: catch inside proceed() and either return a fallback or throw a different exception.
solid answer
~50 s@AfterThrowing is observe-only. When the join point throws, Spring invokes the advice and then re-propagates the *same* exception to the caller — the advice cannot catch it, change it, or return a fallback. Even throwing a new exception from inside the advice is a bad idea: it replaces the original and masks the real failure (and mixes advice bugs with business failures). If you need to actually handle the exception — translate it (e.g. wrap a low-level exception in a domain exception), swallow it and return a default, or retry — you must use @Around, which gives you a ProceedingJoinPoint. You call proceed() inside a try/catch and decide the outcome: rethrow, throw a translated exception, or return a fallback value. So the rule of thumb: @AfterThrowing for failure observability (log/metrics/alert), @Around for failure control (translate/recover/retry).
code
java · 10 lines// @AfterThrowing: can only observe
@AfterThrowing(pointcut = "repo()", throwing = "ex")
public void log(Throwable ex) { /* ex still propagates after this */ }
// @Around: can translate/suppress/recover
@Around("repo()")
public Object handle(ProceedingJoinPoint pjp) throws Throwable {
try { return pjp.proceed(); }
catch (SQLException ex) { throw new DataAccessResourceFailureException("db", ex); }
}go deeper
Knows it doesn't swallow the exception.
Explains that @Around is needed to handle/translate.
Discusses advice-throws-masks-cause and the observe-vs-control split.
Weighs @Around translation vs framework exception-translation and advice ordering effects.
### The core semantic `@AfterThrowing` is **non-intercepting on the exception**: it runs *because* an exception was thrown, but it has **no control over the exception's fate**. After the advice returns, Spring lets the **original exception continue to propagate** to the caller. There is no mechanism in after-throwing advice to catch, suppress, or substitute the exception. Contrast the after-family: - `@AfterReturning` — observes the return value (can read, not replace). - `@AfterThrowing` — observes the exception (cannot suppress). - `@After` — finally-style; runs on both paths, observes neither. None of these can alter control flow. Only `@Around` can. ### What if the advice itself throws? If your `@AfterThrowing` method throws an exception, that **new exception replaces the original** on the way out — the caller sees the advice's exception instead of the real business failure. This is almost always a mistake: it hides the root cause and couples advice bugs to business flow. Keep after-throwing advice side-effect-only and defensive (never let logging/metrics code throw). ### How to actually handle: @Around ```java @Around("execution(* com.example.repo.*.*(..))") public Object translate(ProceedingJoinPoint pjp) throws Throwable { try { return pjp.proceed(); } catch (SQLException ex) { // translate low-level to domain exception throw new DataAccessResourceFailureException("DB call failed", ex); } } ``` With `@Around` you own the outcome: - **Swallow + fallback**: `catch (...) { return defaultValue; }` — the caller sees a normal return. - **Translate**: rethrow a different exception type (often wrapping the cause). - **Retry**: loop around `proceed()`. - **Rethrow**: `catch (...) { log(); throw ex; }` — behaves like @AfterThrowing but with full control. ### Exception translation the framework way For persistence specifically, Spring already does this via `@Repository` + `PersistenceExceptionTranslationPostProcessor`, which converts vendor exceptions into the `DataAccessException` hierarchy — a purpose-built alternative to hand-rolled @Around translation. ### Gotchas - Don't try to 'recover' in @AfterThrowing — you can't; the exception still escapes. - Order matters: if both @Around and @AfterThrowing apply and the @Around swallows the exception, the @AfterThrowing may not see it (no exception propagates from the inner chain). - Keep the advice free of throwing side effects. ### When to use which - **Observe only** (log, count, alert) → `@AfterThrowing`. - **Control the outcome** (translate, recover, retry, fallback) → `@Around`.
- What happens if your @AfterThrowing method itself throws a new exception?That new exception replaces the original one seen by the caller, masking the real failure. Advice should never throw for this reason.
- You need to return a cached fallback when a downstream call fails. Which advice, and why?@Around — only it can catch inside proceed() and substitute a return value; @AfterThrowing cannot change the outcome, the exception would still propagate.
- How does Spring translate JDBC/JPA exceptions without you writing @Around?Via @Repository and PersistenceExceptionTranslationPostProcessor, which map vendor exceptions to the DataAccessException hierarchy.
saying these in an interview costs you the question
- Claiming @AfterThrowing catches/handles the exception so the caller sees success
- Recommending throwing a translated exception from @AfterThrowing to convert error types
- Thinking you can return a fallback value from @AfterThrowing