Spring @ExceptionHandler with Example

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.

message-on-browser-3924864

Happy Learning !!

References:

ExceptionHandler Java Doc
ResponseEntityExceptionHandler Java Doc

3 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments

Comments are closed for this article!

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.