Reputation: 3653
I have a @ControllerAdvice
class to handle exceptions from my SpringMVC controllers. I would like to catch an exception of a known type (RuntimeException) in an @ExceptionHandler
method then throw the e.getCause()
exception and have this exception caught by the same @ControllerAdvice class.
Sample code:
@ControllerAdvice
public class ExceptionHandlingAdvice
{
@ExceptionHandler( RuntimeException.class )
private void handleRuntimeException( final RuntimeException e, final HttpServletResponse response ) throws Throwable
{
throw e.getCause(); // Can be of many types
}
// I want any Exception1 exception thrown by the above handler to be caught in this handler
@ExceptionHandler( Exception1.class )
private void handleAnException( final Exception1 e, final HttpServletResponse response ) throws Throwable
{
// handle exception
}
}
Is this possible?
Upvotes: 11
Views: 4131
Reputation: 1139
Few years late on this.. but just ran into a need for this in dealing with @Async
services - when throwing an exception, they get wrapped in the ExecutionException.class
and wanted my controller advice to direct them to their proper handler, an identical situation you were in.
Using reflection, can gather all the methods on the controller advice, sift for the matching @ExceptionHandler
annotation for e.getCause().getClass()
then invoke the first found method.
@ControllerAdvice
public class ExceptionHandlingAdvice
{
@ExceptionHandler( RuntimeException.class )
private void handleRuntimeException( final RuntimeException e, final HttpServletResponse response )
{
if (e.getCause() != null) {
Optional<Method> method = Arrays.stream(Rest.Advice.class.getMethods())
.filter(m -> {
// Get annotation
ExceptionHandler annotation = m.getAnnotation(ExceptionHandler.class);
// Annotation exists on method and contains cause class
return annotation != null && Arrays.asList(annotation.value()).contains(e.getCause().getClass());
})
.findFirst();
if (method.isPresent()) {
try {
method.get().invoke(this, e.getCause(), response);
} catch (IllegalAccessException | InvocationTargetException ex) {
// Heard you like exceptions on your exceptions while excepting
ex.printStackTrace();
}
}
}
// Handle if not sent to another
}
... other handlers
}
Didn't test with void
-- Personally, I return ResponseEntity<MyStandardErrorResponse>
from my handlers, so my invoke line looks like:
return (ResponseEntity<MyStandardErrorResponse>) method.get().invoke(this, e.getCause(), request);
Upvotes: 0
Reputation: 5146
You can check if that RuntimeException is instance of Exception1.class and call the method directly:
private void handleRuntimeException( final RuntimeException e, final HttpServletResponse response ) throws Throwable
{
if (e instanceof Exception1) handleAnException(e,response);
else throw e.getCause(); // Can be of many types
}
Upvotes: 1