DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Java, Spring Boot, and MongoDB: Performance Analysis and Improvements
  • How To Build Web Service Using Spring Boot 2.x
  • How To Build Self-Hosted RSS Feed Reader Using Spring Boot and Redis
  • Visually Designing Views for Java Web Apps

Trending

  • The Full-Stack Developer's Blind Spot: Why Data Cleansing Shouldn't Be an Afterthought
  • Ensuring Configuration Consistency Across Global Data Centers
  • Breaking Bottlenecks: Applying the Theory of Constraints to Software Development
  • Data Quality: A Novel Perspective for 2025
  1. DZone
  2. Coding
  3. Frameworks
  4. Global Exception Handling With @ControllerAdvice

Global Exception Handling With @ControllerAdvice

Centralize your error handling logic in spring by using the @ControllerAdvice annotation. Reduce duplicate code and keep your code clean!

By 
Dan Newton user avatar
Dan Newton
·
Sep. 13, 17 · Tutorial
Likes (15)
Comment
Save
Tweet
Share
269.0K Views

Join the DZone community and get the full member experience.

Join For Free

@ControllerAdvice is an annotation provided by Spring allowing you to write global code that can be applied to a wide range of controllers — varying from all controllers to a chosen package or even a specific annotation. In this brief tutorial, we will focus on handling exceptions using @ControllerAdvice and @ExceptionHandler (@InitBinder and @ModalAttribute can also be used with @ControllerAdvice).

I will be making use of the VndErrors class in this post and therefore the required dependencies will reflect that. spring-boot-starter-hateoas is included to allow VndErrors to be used, if you do not wish to use this class, spring-boot-start-web will be sufficient and will still provide access to everything else used in this post.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>


By default, @ControllerAdvice will apply to all classes that use the @Controller annotation (which extends to classes using @RestController). If you wanted this to be more specific, there are a few properties provided that allow this.

To reduce the applicable classes down by package, you simply need to add the name of the package to the annotation. When a package is chosen, it will be enabled for classes inside that package as well as sub-packages. Multiple packages can also be chosen by following the same process, but using an array instead of a singular string (all properties in @ControllerAdvice can be singular or multiple).

@ControllerAdvice("my.chosen.package")
@ControllerAdvice(value = "my.chosen.package")
@ControllerAdvice(basePackages = "my.chosen.package")


Another way to specify a package is via the basePackageClasses property, which will enable @ControllerAdvice to all controllers inside the package that the class (or interface) lives in.

@ControllerAdvice(basePackageClasses = MyClass.class)


To apply to specific classes use assignableTypes.

@ControllerAdvice(assignableTypes = MyController.class)


And finally, what if you want to apply it to controllers with certain annotations? The below snippet would only assist controllers annotated with @RestController (which it covers by default) but will not include @Controller annotated classes.

@ControllerAdvice(annotations = RestController.class)


@ExceptionHandler allows you to define a method that, as the name suggests, handles exceptions. If you weren’t using @ControllerAdvice , the code for handling these exceptions would be in the controllers themselves, which could add quite a bit of duplication and clutter to the class and leading to it not being as “clean”. You could move the @ExceptionHandler methods into a base class that the controller extends to separate the code. This method is not perfect and comes with the issue that every controller where you need this global exception handling will now need to extend the base controller. Therefore, when you create a new controller and forget to extend this base class, you are now no longer handling some exceptions and might get bitten in the butt later on. Using @ControllerAdvice along with @ExceptionHandler prevents this by providing global (and more specific) error handling so you don’t need to remember to implement them yourself or extend another class every time.

Below is a basic example of a class annotated with @ControllerAdvice.

@ControllerAdvice @RequestMapping(produces = "application/vnd.error+json") public class PersonControllerAdvice {
    @ExceptionHandler(PersonNotFoundException.class) public ResponseEntity < VndErrors > notFoundException(final PersonNotFoundException e) {
        return error(e, HttpStatus.NOT_FOUND, e.getId().toString());
    }
    private ResponseEntity < VndErrors > error(final Exception exception, final HttpStatus httpStatus, final String logRef) {
        final String message = Optional.of(exception.getMessage()).orElse(exception.getClass().getSimpleName());
        return new ResponseEntity < > (new VndErrors(logRef, message), httpStatus);
    }
    @ExceptionHandler(IllegalArgumentException.class) public ResponseEntity < VndErrors > assertionException(final IllegalArgumentException e) {
        return error(e, HttpStatus.NOT_FOUND, e.getLocalizedMessage());
    }
}


This class provides @ExceptionHandler methods globally to all controllers, as (which you can’t see from this code alone) there are multiple controllers that throw PersonNotFoundException, which need handling. The RequestMapping annotation here is used to set the content type that is returned by the ResponseEntity. These could be added to the methods themselves instead of the different types needed to be returned. Each instance of @ExceptionHandler marks an exception that it is in charge of dealing with. The methods in this example simply catch the exception and take its error message and combine it with an appropriate response code.

Without this code, when PersonNotFoundException is thrown, the following output is produced (along with a stacktrace in your log).

{
    "timestamp": "2017-09-12T13:33:40.136+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "Person could not be found with id: 1",
    "path": "/people/1"
}


With the addition of @ControllerAdvice and @ExceptionHandler, a different response is returned (stacktrace not found anymore).

[
    {
        "logref": "1",
        "message": "Person could not be found with id: 1",
        "links": []
    }
]


In this response, we have actually controlled what is returned to the client. Although the first one contains more information, some of it is not useful to the client and could technically be incorrect. Yes, an “Internal Server Error” occurred, but really a person did not exist with the passed id and the response could suggest something blew up.

One last thing before I wrap up this post: If you define more than one @ExceptionHandler for the same exception, you need to be on the lookout. When defined in the same class, Spring is kind enough to throw an exception and fail on startup. But when they appear in different classes, say two @ControllerAdvice classes, both with a handler for the PersonNotFoundException, the application would start — but will use the first handler it finds. This could cause unexpected behavior if you are not aware.

In conclusion, we have looked at how to use the @ControllerAdvice and @ExceptionHandler annotations to create global error handling. That allows you to keep your logic in a central place, thus removing possible duplication, and, when applied globally, it removes the need to worry about whether more general exceptions are being handled or not.

The code used in this post can be found on my GitHub.

Annotation POST (HTTP) Error message Property (programming) Spring Framework application Snippet (programming) Clutter (software) Strings

Published at DZone with permission of Dan Newton, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Java, Spring Boot, and MongoDB: Performance Analysis and Improvements
  • How To Build Web Service Using Spring Boot 2.x
  • How To Build Self-Hosted RSS Feed Reader Using Spring Boot and Redis
  • Visually Designing Views for Java Web Apps

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!