Controller Advice – Exception Handler in Spring Boot

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.