Spring framework offers developers several options for handling exceptions in their applications. One of which is global exception handler with @ControllerAdvice and @ExceptionHandler annotations. And in this article, I’d like to share with you some code examples about application-wide exception handling in Spring with both Web MVC controller and REST controller.

 

1. Why Global Exception Handling?

In this kind of exception handling, you write a separate class that handles all exceptions which might be thrown by the application - in one central place:

@ControllerAdvice
public class GlobalExceptionHandler {

	@ExceptionHandler(Exception1.class)
	public String handleException1() {
		
		// handles exception type Exception1
		
	}
	
	@ExceptionHandler(Exception2.class)
	public String handleException2() {
		
		// handles exception type Exception2
		
	}	
	
	// handlers for other exceptions...
}

You see, the class is annotated with @ControllerAdvice, which is a kind of @Component. It tells Spring to intercept (via AOP) all handler methods in all controllers and execute the exception handler methods when the matching exceptions are thrown.

In exception handler methods, you can log the exception, redirect to user-friendly error pages, modify the response status code, any custom logics you want to process when the matching exception occurs.

Compared with controller-based exception handler, this approach is more convenient in terms of centralizing all exception handling code in one place.


2. Spring Web MVC Global Exception Handler Examples

In a classic Spring Web MVC application with template-based response, the exception handler method should return a logical view name, which is then resolved by Spring’s view resolver. Here’s an example:

package net.codejava;

import org.hibernate.exception.JDBCConnectionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobalExceptionHandler {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(
						GlobalExceptionHandler.class); 
	
	@ExceptionHandler(JDBCConnectionException.class)
	public String handleConnectionError(Exception ex) {
		
		LOGGER.error(ex.getMessage(), ex);
		
		return "connect_error";
	}
}

Here, the handleConnectionError()method will get executed if an exception of type JDBCConnectionException is thrown (by any controller). It returns a logical view name “connect_error” which will be processed by a template engine (JSP or Thymeleaf).

In the signature of exception handler method, you can access the request, response and exception objects if needed. Here’s another example:

@ExceptionHandler(Exception.class)
public String handleGeneralError(HttpServletRequest request, 
		HttpServletResponse response, Exception ex) {
	
	LOGGER.error(ex.getMessage(), ex);
	
	// do something with request and response
	
	return "general_error";
}

In case you want to put an object to model, the method should return a ModelAndView object, as shown below:

@ExceptionHandler(ProductNotFoundException.class)
public ModelAndView handleProductNotFoundError(Exception ex) {
	
	ModelAndView mav = new ModelAndView("productNotFound");
	mav.addObject("message", ex.getLocalizedMessage());
	
	return mav;
}

Then in the view template, you can access the object like this (Thymeleaf):

<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Product Not Found</title>
</head>
<body>
	<h2>Sorry, the product you requested not found</h2>
	<h3>[[${message}]]</h3>
</body>
</html>

You can also catch multiple exceptions in a single hander, as shown below:

@ExceptionHandler({Exception1.class, Exception2.class, Exception3.class})

 

3. Spring REST Global Exception Handler Examples

In a Spring application with REST APIs, an exception handler method should be annotated with @ExceptionHandler, @ResponseStatus and @ResponseBody annotations. Let’s look at the following example:

package net.codejava.payroll;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

@ControllerAdvice
public class GlobalErrorHandler {

	@ResponseBody
	@ExceptionHandler(EmployeeNotFoundException.class)
	@ResponseStatus(HttpStatus.NOT_FOUND)
	String employeeNotFoundHandler(HttpServletRequest request, 
			HttpServletResponse response, Exception ex) {
		
		// do something with request or response	
		
		return ex.getMessage();
	}
	
}

Here, the @ResponseStatus annotation specifies the HTTP status code of the response, and the @ResponseBody annotation tells Spring to include the return value of the handler in the response’s body.

You can also return a custom object in exception handler methods, as shown in the below example:

@ControllerAdvice
public class GlobalExceptionHandler {

	@ExceptionHandler(MultipartException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ResponseBody
	public ErrorInfo handleMultipartException(HttpServletRequest request) {
		return new ErrorInfo(request, "Invalid Upload Request");
	}
	
	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
	@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
	@ResponseBody
	public ErrorInfo handleMethodNotSupported(HttpServletRequest request) {
		return new ErrorInfo(request, "HTTP request method not supported for this operation.");
	}
	
	@ExceptionHandler(IOException.class)
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	@ResponseBody
	public ErrorInfo handleIOException(HttpServletRequest request, Exception ex) {
		return new ErrorInfo(request, "IO Error: " + ex.getMessage());
	}	
}

The ErrorInfo class is as follows:

package net.codejava.payroll;

public class ErrorInfo {
	private final String url;
	private final String ex;
	
	public ErrorInfo(String url, Exception ex) {
		this.url = url;
		this.ex = ex.getLocalizedMessage();
	}

	// getters and setters are not shown for brevity	
	
}

Then in case of error, the response would be like this:

{
  "url": "http://localhost:8080/orders/38",
  "ex": "Could not find order ID 38"
}

NOTES: You can combine both exception handlers for classic Web MVC & REST in one global exception handler class. And if an exception is handled at both controller level and global level, the controller-based one takes precedence.

That’s a few code examples about global exception handling in Spring application. I hope you find them helpful. To see the coding in action, I recommend you watch the following video:

 

Related Tutorials:

 

Other Spring Boot Tutorials:


About the Author:

is certified Java programmer (SCJP and SCWCD). He began programming with Java back in the days of Java 1.4 and has been passionate about it ever since. You can connect with him on Facebook and watch his Java videos on YouTube.



Add comment