Exception handling is a very essential feature of any Java application. Every good open-source framework allows writing the exception handlers in such a way that we can separate them from our application code. Well, Spring framework also allows us to do so using annotation @ExceptionHandler.
The @ExceptionHandler
annotation is used for handling exceptions in specific handler classes and/or handler methods.
1. Spring @ExceptionHandler
To handle exceptions in Spring MVC, we can define a method in @Controller class and use the annotation @ExceptionHandler on it. Spring configuration will detect this annotation and register the method as an exception handler for the argument exception class and its subclasses.
@Controller
public class PageController {
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ModelAndView getPage(@PathVariable("id") String id) throws Exception {
Page page = ...;
if(page == null) {
throw new RecordNotException("Page not found for id : " + id);
}
return new ModelAndView("index");
}
@ExceptionHandler(NullPointerException.class)
public ModelAndView handleException(NullPointerException ex) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("message", ex.getMessage());
return modelAndView;
}
@ExceptionHandler(RecordNotException.class)
public ModelAndView handleException(RecordNotException ex) {
...
return modelAndView;
}
}
Now every time, the controller encounter NullPointerException
in request processing for any web request in this controller, control will automatically come to this handler method.
1.1. Method Arguments
Handler methods that are annotated with this annotation are allowed to have very flexible signatures. They can accept arguments of different types. For example, an exception argument, request and/or response objects, session object, locale object and model object etc.
@ExceptionHandler(NullPointerException.class)
public ModelAndView handleException(NullPointerException ex, HttpServletRequest request, Model model) {
...
}
1.2. Return Types
Similar to arguments, return types can be of different types. For example, ModelAndView object, Model object, View object, view name as String etc. We can mark the method to void
also if the method handles the response itself by writing the response content directly to HttpServletResponse
.
We may combine the ExceptionHandler
annotation with @ResponseStatus
for a specific HTTP error status.
@ExceptionHandler(AjaxException.class)
@ResponseBody
public ErrorResponse handleException(AjaxException ex, HttpServletResponse response) {
...
}
2. Handling Multiple Exceptions
As mentioned earlier, the above exception handler will handle all exceptions, either instance of a given class or sub-classes of argument exceptions. But, if we want to configure @ExceptionHandler for multiple exceptions of different types, then we can specify all such exceptions in form of an array.
@ExceptionHandler({NullPointerException.class, ArrayIndexOutOfBoundsException.class, IOException.class})
public ModelAndView handleException(Exception ex) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("generic-error");
modelAndView.addObject("message", ex.getMessage());
return modelAndView;
}
3. Global @ExceptionHandler with @ControllerAdvice
If we want to centralize the exception-handling logic to one class that is capable of handling exceptions thrown from any handler class/ controller class – then we can use @ControllerAdvice
annotation.
By default, the methods in an @ControllerAdvice apply globally to all controllers. We can create a class and add @ControllerAdvice annotation on top. Then add @ExceptionHandler methods for each type of specific exception class in it.
Notice we extended the exception handler class with ResponseEntityExceptionHandler. It is a convenient base class for @ControllerAdvice
classes that wish to provide centralized exception handling across all @RequestMapping
methods through @ExceptionHandler
methods.
@ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler
{
@ExceptionHandler(Exception.class)
public final ResponseEntity<ErrorResponse> handleAllExceptions(Exception ex, WebRequest request) {
List<String> details = new ArrayList<>();
details.add(ex.getLocalizedMessage());
ErrorResponse error = new ErrorResponse(ApplicationConstants.SERVER_ERROR, details);
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(RecordNotFoundException.class)
public final ResponseEntity<ErrorResponse> handleUserNotFoundException(RecordNotFoundException ex, WebRequest request) {
List<String> details = new ArrayList<>();
details.add(ex.getLocalizedMessage());
ErrorResponse error = new ErrorResponse(ApplicationConstants.RECORD_NOT_FOUND, details);
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
}
Where supporting classes are. This is for example only. Please create your application-specific exception and handle them.
@ResponseStatus(HttpStatus.NOT_FOUND)
public class RecordNotFoundException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public RecordNotFoundException(String exception) {
super(exception);
}
}
public class ErrorResponse {
private String message;
private List<String> details;
}
4. Demo
For the demo, the below handler method is intentionally returning NullPointerException
.
@RequestMapping(value="/demo/not-exist", method = RequestMethod.GET, headers="Accept=*/*")
public @ResponseBody ModelAndView oneFaultyMethod()
{
if(true)
{
throw new NullPointerException("This error message if for demo only.");
}
return null;
}
If we deploy the above application and hit the URL [/SpringApplication/users/demo/not-exist] in the browser, it will show the “error” page as configured in the first section.
< %@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
< %@ taglib prefix="x" uri="http://java.sun.com/jstl/xml" %>
< %@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %>
< %@ taglib prefix="sql" uri="http://java.sun.com/jstl/sql" %>
<html>
<head>
<title>This is sample error page</title>
</head>
<body>
<h1>This is sample error page : <c:out value="${message}"></c:out></h1>
</body>
<html>
Below will be the output in the browser.

Happy Learning !!
References:
ExceptionHandler Java Doc
ResponseEntityExceptionHandler Java Doc