🌱 Spring Boot Q55 / 69

How to write custom exceptions in spring boot?

AI-Powered Answer ✓ Answered

Custom exceptions in Spring Boot allow you to define domain-specific error types, making your application's error handling more structured, readable, and easier to manage. This guide walks through the process of defining a custom exception, handling it globally, and using it within your application.

1. Define Your Custom Exception Class

First, create a simple Java class that extends either RuntimeException (for unchecked exceptions) or Exception (for checked exceptions). Unchecked exceptions are generally preferred in Spring Boot for most application-level errors as they don't force boilerplate try-catch blocks everywhere.

java
package com.example.myproject.exception;

public class ResourceNotFoundException extends RuntimeException {

    public ResourceNotFoundException(String message) {
        super(message);
    }

    public ResourceNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}

2. Create a Global Exception Handler

To handle your custom exception gracefully across your entire application, create a global exception handler using @RestControllerAdvice (for REST APIs) or @ControllerAdvice (for MVC applications). This class centralizes exception handling logic.

java
package com.example.myproject.handler;

import com.example.myproject.exception.ResourceNotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<String> handleResourceNotFoundException(ResourceNotFoundException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
    }

    // You can add more exception handlers here for other custom or built-in exceptions
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGeneralException(Exception ex) {
        return new ResponseEntity<>("An unexpected error occurred: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

3. Use Your Custom Exception

Now you can throw your custom exception from any service layer, controller, or other component when a specific error condition is met.

java
package com.example.myproject.service;

import com.example.myproject.exception.ResourceNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class MyService {

    public String getResourceById(Long id) {
        if (id < 0) {
            throw new IllegalArgumentException("ID cannot be negative");
        }
        if (id == 123L) {
            return "Found resource with ID " + id;
        } else {
            throw new ResourceNotFoundException("Resource with ID " + id + " not found.");
        }
    }
}

4. Create a Controller to Test

Finally, create a simple REST controller to expose an endpoint that uses the service, allowing you to test your custom exception handling.

java
package com.example.myproject.controller;

import com.example.myproject.service.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/resources")
public class MyController {

    @Autowired
    private MyService myService;

    @GetMapping("/{id}")
    public String getResource(@PathVariable Long id) {
        return myService.getResourceById(id);
    }
}

When you access /api/resources/123, you will get a 200 OK response. When you access /api/resources/456, the ResourceNotFoundException will be caught by GlobalExceptionHandler, and you'll receive a 404 Not Found response with the message "Resource with ID 456 not found.". If you access /api/resources/-1, you will get a 400 Bad Request with the message "ID cannot be negative" due to the IllegalArgumentException handler.