Spring @ExceptionHandler – Multiple exceptions and global handler

Exception handling is very essential feature of any Java application. Every good open source framework allows to write the exception handlers in such a way that we can separate then from our application code. Well, Spring framework also allows us to do so using annotation Spring @ExceptionHandler

@ExceptionHandler annotation is used for handling exceptions in specific handler classes and/or handler methods.

Table of Contents

1. Spring @ExceptionHandler annotation
2. Spring @ExceptionHandler example
3. Spring @ExceptionHandler - Handle multiple exceptions
4. Spring @ExceptionHandler Global exception handling with @ControllerAdvice

1. Spring @ExceptionHandler annotation

To handle exceptions in String 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 exception handler for argument exception class and its subclasses.

1.1. Exception handler method arguments

Handler methods which 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.

1.2. Exception handler method 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.

1.3. Exception handler method example

@ExceptionHandler(NullPointerException.class)
public ModelAndView handleException(NullPointerException ex)
{
	//Do something additional if required
	ModelAndView modelAndView = new ModelAndView();
	modelAndView.setViewName("error");
	modelAndView.addObject("message", ex.getMessage());
	return modelAndView;
}

Now every time, controller encounter NullPointerException in request processing for any web request in this controller, control will automatically come to this handler method.

2. Spring @ExceptionHandler example

For example, 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 above application and hit the URL [/SpringApplication/users/demo/not-exist] in browser, it will show the “error” page as configured in 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 browser.

message-on-browser-3924864

3. Spring @ExceptionHandler – Handle multiple exceptions

As mentioned earlier, above exception handler will handle all exceptions which are either instance of given class or sub-classes of argument exception. But, if we want to configure @ExceptionHandler for multiple exceptions of different types, then we can specify all such exceptions in form of array.

@ExceptionHandler({NullPointerException.class, ArrayIndexOutOfBoundsException.class, IOException.class})
public ModelAndView handleException(NullPointerException ex)
{
	//Do something additional if required
	ModelAndView modelAndView = new ModelAndView();
	modelAndView.setViewName("error");
	modelAndView.addObject("message", ex.getMessage());
	return modelAndView;
}

4. Spring @ExceptionHandler Global exception handling with @ControllerAdvice

If we want to centralize the exception handling logic to one class which is capable to handle 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 classes in it.

Notice we extended the exception handler class with ResponseEntityExceptionHandler. It is 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);
    }
}
@XmlRootElement(name = "error")
public class ErrorResponse
{
    public ErrorResponse(String message, List<String> details) {
        super();
        this.message = message;
        this.details = details;
    }
 
    private String message;
    private List<String> details;

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public List<String> getDetails() {
		return details;
	}

	public void setDetails(List<String> details) {
		this.details = details;
	}
}

Happy Learning !!

References:

ExceptionHandler Java Doc
ResponseEntityExceptionHandler Java Doc

Leave a Reply

3 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

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.

Our Blogs

REST API Tutorial