In this post, I will show how we can use the annotation @ControllerAdvice – Controller Advice – an exception handler in the Spring Boot application. If you want to read how to handle uncaught exceptions in Spring Boot, you can check my old post.
What is @ControllerAdvice
?
Spring 3.2 introduced an annotation @ControllerAdvice
. The annotation allows the handling of exceptions across the application. Before this, Spring offered another annotation @ExceptionHandler
for exception handling. But, you have to add this annotation in each controller class of your application. It doesn’t help on the application level.
@ControllerAdvice
is an annotation-driven interceptor. It intercepts most of those classes that include @RequestMapping
.
Comparison with @ExceptionHandler
In most controller classes, you can add @ExceptionHandler
annotation to handle exceptions for that class. Nevertheless, in such classes, one can add an extra method to handle exceptions thrown by @RequestMapping
methods in the same controller. These exception handling methods can redirect the user to the error page OR build a custom error response.
This will look like below:
@RestController
@RequestMapping("/companies")
public class CompanyController
{
@GetMapping
public List<Company> getAllCompanies(HttpServletRequest req) throws Exception {
}
@ExceptionHandler(Exception.class)
public ModelAndView handleError(HttpServletRequest req, Exception ex) {
logger.error("Request: " + req.getRequestURL() + " raised " + ex);
ModelAndView mav = new ModelAndView();
mav.addObject("exception", ex);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error");
return mav;
}
}
Additionally, you can see the method getAllCompanies
throw an Exception. The method handleError
will handle the exception thrown by getAllCompanies
.
Furthermore, if I have another controller like UserController
, I will end up writing like below if it has to handle exceptions.
@RestController
@RequestMapping("/users")
public class UserController
{
@GetMapping
public List<User> getAllUsers(HttpServletRequest req) throws Exception {
}
@ExceptionHandler(Exception.class)
public ModelAndView handleError(HttpServletRequest req, Exception ex) {
logger.error("Request: " + req.getRequestURL() + " raised " + ex);
ModelAndView mav = new ModelAndView();
mav.addObject("exception", ex);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error");
return mav;
}
}
Henceforth, this makes a lot of duplicate code. This is where Controller Advice comes into the picture with an advantage.
Example of Controller Advice
A Controller Advice allows you to use the same exception handling technique across applications, without repeating any code.
Consequently, a class annotated with @ControllerAdvice
implements three types of methods:
- Exception handling method annotated with
@ExceptionHandler
- Model enhancement methods annotated with
@ModelAttribute
- Binder initialization methods annotated with
@InitBinder
@ControllerAdvice
public class GlobalExceptionHandler
{
@ExceptionHandler(CompanyNotFoundException.class)
public ModelAndView handleError(HttpServletRequest req, CompanyNotFoundException ex)
{
logger.error("Request: " + req.getRequestURL() + " raised " + ex);
ModelAndView mav = new ModelAndView();
mav.addObject("exception", ex);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error");
return mav;
}
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity handleUserExceptionError(HttpServletRequest req, HttpServletResponse res, UserNotFoundException ex)
{
List errors = Collections.singletonList(ex.getMessage());
// Get headers
if(HttpStatus.INTERNAL_SERVER_ERROR.equals(res.getStatus()))
{
// do something
}
return new ResponseEntity<>(new ApiError(errors), headers, status);
}
}
This will allow now to intercept any exception thrown from controllers. This makes implementing exception handling easier.
Conclusion
In this post, I showed how we can implement Controller Advice – exception handler in Spring Boot. This is a very effective way to handle exceptions in the current applications built with Spring Boot.