ExceptionHandling in Spring Boot Applications
In this article, we’ll discuss how to properly handle exceptions in Spring Boot applications. We’ll start by looking at the different types of exceptions that can be thrown and the best way to catch them. We’ll then take a look at some specific examples of how to handle exceptions in a Spring Boot application. Finally, we’ll discuss how to log exception information so that you can troubleshoot problems more effectively.
Using @ResponseStatus annotation
Earlier versions of Spring Boot applications used @ResponseStatus
annotation to send the exception/error code and messages in the response when ever exception happend.
You can define exception class like below and set the error code and message to be sent in the response when ever
@ResponseStatus(code = HttpStatus.NOT_FOUND,reason = "Department not found" ) public class DeptNotFoundException extends RuntimeException { public DeptNotFoundException() { } public DeptNotFoundException(String message) { super(message); } public DeptNotFoundException(String message, Throwable cause) { super(message, cause); } }
In controller class we can throw DeptNotFoundException
if we do not find department
@RestController @RequestMapping("/department") public class DepartmentController { @Autowired DepartmentService departmentService; @GetMapping(value="/{id}") public Department getDepartmentById(@PathVariable Integer id) { Optional<Department> dept = departmentService.getDepartmentById(id); return dept.orElseThrow(DeptNotFoundException::new); } }
If we try to get Department information which is not existing we will receive error response like below

{ "timestamp": "2022-04-08T14:00:53.642+00:00", "status": 404, "error": "Not Found", "message": "Department not found", "path": "/department/1000" }
A major drawback of this approach is tight coupling of exception and messages. If we want to throw another exception with different message we have to define another message
We can overcome this problem to some extent by declaring a GenericException without any reason
attribute and providing information in the message
@ResponseStatus(code = HttpStatus.NOT_FOUND )
public class GenericException extends RuntimeException {
public GenericException() {
}
public GenericException(String message) {
super(message);
}
public GenericException(String message, Throwable cause) {
super(message, cause);
}
}
@RestController @RequestMapping("/department") public class DepartmentController { @Autowired DepartmentService departmentService; @GetMapping(value="/{id}") public Department getDepartmentById(@PathVariable Integer id) { Optional<Department> dept = departmentService.getDepartmentById(id); return dept.orElseThrow( () -> new GenericException("Department not found")); } }
With above approach , we can send different message but still we can’t send different Http status code.
Note
If you are using Spring Boot 2.3 and above, you should include following property in application.properties
to view the full error/exception
server.error.include-message=always
Using ResponseStatusException class
Spring 5 introduced ResponseStatusException class which simplifies exception/error handling in SpringBoot REST API’s
We can create ResponseStatusException instance with status
code .We can optionally provide reason
and cause
also.
Following are the constructions provided by ResponseStatusException class.
public ResponseStatusException(HttpStatus status) public ResponseStatusException(HttpStatus status, @Nullable String reason) public ResponseStatusException(HttpStatus status, @Nullable String reason, @Nullable Throwable cause)
@RestController @RequestMapping("/employee") public class EmployeeController { @Autowired EmployeeService employeeService; @GetMapping("/{id}") public Employee getEmployee(@PathVariable Integer id) { return employeeService.getEmployeeById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,"Employee not found with id : "+ id)); }
Pros
- Since it is inbuilt class , error handling can be implemented quiet fast.
- There is no need for creating custom exception classes unless there is specific requirement as it provides option for sending reason and cause along with HTTP status code.
Using @ExceptionHandler
We can add methods annotated with @ExceptionHandler to any controller to specifically handle exceptions thrown by Request mapping methods. We can use these methods for send error response using
1. Handle predefined exceptions
2. Build a custom error message
Handle predefined exceptions
The following code demonstrates handling predefined exception.
Let’s see the response received by the rest client if DataIntegrityViolationException
occurs in EmployeeController
class.

Instead of sending Internal Server error response, Using @ExceptionHandler and @ResponseStatus annotation we can convert a predefined exception to an HTTP Status code
@RestController @RequestMapping("/employee") public class EmployeeController { @Autowired EmployeeService employeeService; @ResponseStatus(value=HttpStatus.CONFLICT, reason="Data integrity violation") // 409 @ExceptionHandler(DataIntegrityViolationException.class) public void handleConstraintViolated2() { // Nothing to do System.out.println("in const violation"); } }

Build a custom error message
Instead of sending HTTP response code, you can also send error response string or or as an object in the response.
Following code sends error message as string
@ExceptionHandler(DataIntegrityViolationException.class) public String handleDataIntegrityViolationException() { return "Database error occurred"; }
Following code sends error message as custom object.
@ExceptionHandler(DataIntegrityViolationException.class) public ErrorResponse handleDataIntegrityViolationException() { return new ErrorResponse("Database error occurred",HttpStatus.CONFLICT.value()); }
Global Exception Handling
Using @ControllerAdvice
@ExceptionHandler allows to you handle exception inside controller, @ControllerAdvice allows you to handle exception across the application.
@ControllerAdvice allows you to handle exceptions in centralized way. As the name indicates the annotated class assists a “Controller”. By default the @ControllerAdvice
annotated class will assist all known Controllers. You can configure it to assist only subset of of Controller classes.
Exception handlers you saw above can be defined on a controller-advice class – but now they apply to exceptions thrown from any controller. Here is a simple example:
@ControllerAdvice public class GlobalExceptionHandler { @ResponseStatus(value= HttpStatus.CONFLICT, reason="Data integrity violation") // 409 @ExceptionHandler(DataIntegrityViolationException.class) public void handleDataIntegrityViolationException() { } }
You can also send custom error response message in response. You have to use @ResponseBody annotation to send the object in response
@ExceptionHandler(DataIntegrityViolationException.class) @ResponseBody public ErrorDetails handleDataIntegrityViolationException() { ErrorDetails errorDetails = new ErrorDetails(LocalDate.now(),"DataIntegrityViolationException exception", ""); return errorDetails; }
Using @RestControllerAdvice
You can further simplify the ControllerAdvice classes using @RestControllerAdvice annotation.
It is a convenience annotation that is itself annotated with @ControllerAdvice
and @ResponseBody
.
When using @RestControllerAdvice annotation, you omit the @ResponseBody annotation when sending back custom error response.
@RestControllerAdvice public class RestControllerGlobalExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(DataIntegrityViolationException.class) public ErrorDetails handleDataIntegrityViolationException() { ErrorDetails errorDetails = new ErrorDetails(LocalDate.now(),"DataIntegrityViolationException exception", ""); return errorDetails; }
Using ResponseEntityExceptionHandler class
Spring framework has a ResponseEntityExceptionHandler convenient base class for @ControllerAdvice
classes that wish to provide centralized exception handling across all @RequestMapping
methods through @ExceptionHandler
methods.
This base class provides an @ExceptionHandler
method for handling internal Spring MVC exceptions. This method returns a ResponseEntity
for writing to the response with a message converter
.
This base class provides default implementation for following exceptions.You can override the coreesponding methos and provide your own custom response message to client. This class very useful if you are validating the Request paths and models.
HttpRequestMethodNotSupportedException HttpMediaTypeNotSupportedException HttpMediaTypeNotAcceptableException MissingPathVariableException MissingServletRequestParameterException ServletRequestBindingException ConversionNotSupportedException TypeMismatchException HttpMessageNotReadableException HttpMessageNotWritableException MethodArgumentNotValidException MissingServletRequestPartException NoHandlerFoundException AsyncRequestTimeoutException
Here is a simple example using ResponseEntityExceptionHandler
@ControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { @Override protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { ErrorDetails errorDetails = new ErrorDetails(LocalDate.now(),"HttpRequestMethodNotSupported exception", ex.getMessage()); return new ResponseEntity<>(errorDetails, HttpStatus.METHOD_NOT_ALLOWED); } }
When you invoke a PATCH method with GET option, The application will raise HttpRequestMethodNotSupportedException .
The above code will catch the exception and send the custom response back to client.

What to Use When?
- For user defined exceptions consider adding @ResponseStatus to them
- For all other exceptions implement an @ExceptionHandler method on a @ControllerAdvice or @RestControllerAdvice class
- For Controller specific exception handling, add @ExceptionHandler methods to your controller.