Understanding @ControllerAdvice annotation

Understanding ControllerAdvice_prasadct

@ControllerAdvice is a sub type of @Component annotation and it useful to handle exceptions across the whole application in a single place.

When to use @ControllerAdvice?

If you have number of controllers, handling exception from each controller method is a tedious task. With @ControllerAdvice you can handle all the exceptions in a single(Global) place.

@ControllerAdvice is a annotation-based global exception handling interceptor.

Example of @ControllerAdvice

In this article I’ll explain @ControllerAdvice annotation with a simple REST API.

I’m going to handle exceptions thrown by two controllers: AdminController and CustomerController.

Project structure Understanding @controllerAdvice prasadct.com
Project Structure

Here I have defined two exceptions: AdminException and CustomerNotFoundException.

I’m going to handle those two exceptions and send a meaningful response to the user using @ControllerAdvice.

@ControllerAdvice
public class ControllerExceptionHandler {

    @ExceptionHandler(CustomerNotFoundException.class)
    public ResponseEntity customerNotFoundException(Exception ex){
        //Create customer response
        String output = "Customer not found " + new Date() + " " + ex.getMessage();
        return new ResponseEntity(output, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(AdminException.class)
    public ResponseEntity invalidNameException(Exception ex){
        //Create customer response
        String output = "Invalid Admin " + new Date() + " " + ex.getMessage();
        return new ResponseEntity(output, HttpStatus.BAD_REQUEST);
    }
}

I have created a class annotated with @ControllerAdvice and inside it I have created separate methods to handle each exception. With @ExceptionHandler annotation, I can define which exception should be handled inside each method.

These are my two controllers. For testing purpose I have thrown the defined exceptions when user sends “Invalid” as the request parameter.

@RestController
public class AdminController {
    @GetMapping(value = "/admin")
    public ResponseEntity getCustomer(@RequestParam(name = "admin") String admin) throws AdminException {
        if (admin.equals("Invalid")){//Just for testing
            throw new AdminException("Invalid Admin: " + admin);
        }
        return new ResponseEntity("Admin Request Success", HttpStatus.OK);
    }
}
@RestController
public class CustomerController {

    @GetMapping(value = "/customer")
    public ResponseEntity getCustomer(@RequestParam(name = "name") String name) throws CustomerNotFoundException {
        if (name.equals("Invalid")){//Just for testing
            throw new CustomerNotFoundException("Invalid Customer: " + name);
        }
        return new ResponseEntity("Customer Request Success", HttpStatus.OK);
    }
}

Let’s run the application for success scenario.

D:\>curl -i http://localhost:8080/customer?name=Prasad
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 24
Date: Thu, 18 Jun 2020 00:58:49 GMT

Customer Request Success
D:\>curl -i http://localhost:8080/admin?admin=AdminName
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 21
Date: Thu, 18 Jun 2020 01:00:30 GMT

Admin Request Success

Now we know that both APIs are working properly. Let’s execute the exceptional scenario where our Controller advice should send use formatted output.

D:\>curl -i http://localhost:8080/customer?name=Invalid
HTTP/1.1 400
Content-Type: text/plain;charset=UTF-8
Content-Length: 73
Date: Thu, 18 Jun 2020 01:04:33 GMT
Connection: close

Customer not found Thu Jun 18 09:04:33 CST 2020 Invalid Customer: Invalid
D:\>curl -i http://localhost:8080/admin?admin=Invalid
HTTP/1.1 400
Content-Type: text/plain;charset=UTF-8
Content-Length: 65
Date: Thu, 18 Jun 2020 01:07:21 GMT
Connection: close

Invalid Admin Thu Jun 18 09:07:21 CST 2020 Invalid Admin: Invalid

Now you can see that we have handled exceptions thrown by multiple controllers and forwarded a costume response to the user.

How to control the access?

By default ControllerAdvice will apply to all the classes which are annotated with @Controller or @RestController annotations. But there are few ways to control the access.

By specifying the base package

@ControllerAdvice("com.prasadct.controllerAdvice.controllerAdvice.controller")
@ControllerAdvice(value = "com.prasadct.controllerAdvice.controllerAdvice.controller")
@ControllerAdvice(basePackages = "com.prasadct.controllerAdvice.controllerAdvice.controller")

All of the above three configurations are valid. All of them only apply to the controllers inside that specific package: “com.prasadct.controllerAdvice.controllerAdvice.controller”

Also you can specify array of packages by specifying those inside curly brackets.

@ControllerAdvice(value = {"pkg1, pkg2, pkg3"})

By Specifying the class

You can apply ControllerAdvice to a specific class or array of classes.

@ControllerAdvice(basePackageClasses = AdminController.class)
@ControllerAdvice(basePackageClasses = {AdminController.class, CustomerController.class})

Important points

  • If there are multiple ControllerAdvices and multiple handlers for same exception, you can’t guaranteed that which method will handle the exception. Better you avoid such kind of cases.
  • You can extend your ControllerAdvice with ResponseEntityExceptionHandler and it provides to provide centralized exception handling across all @RequestMapping methods through @ExceptionHandler methods.
  • There is a more specialized version of exception handler wrapped with @ResponseBody called @RestControllerAdvice which is convenient for RESTful web services.

Conclusion

Handling exceptions globally with @ControllerAdvice allows us to make our business logic clean and transfer all exception handling to a separate component. Also we can provide a unique error response across all APIs.

Sample Code

Please find my example project from Github.

Related articles:

What is the difference between @Component, @Repository, @Service, and @Controller annotations in Spring?

What is Spring Boot and Why it is the best way to start learning Spring Framework?

22 thoughts on “Understanding @ControllerAdvice annotation

  1. I am sure this piece of writing has touched all the internet people, itss really really fastidious paragraph on building up neww webpage. Ninnetta Holden Apthorp

  2. Hi mates, how is everything, and what you want to say on the topic of this piece of writing, in my view its in fact remarkable in favor of me. Haley Esra Rizzo

  3. I really love your site.. Excellent colors & theme.
    Did you create this website yourself? Please reply back as
    I’m wanting to create my own personal blog and would love to know where you got this from or exactly what the theme is called.
    Appreciate it!

Leave a Reply

Your email address will not be published. Required fields are marked *