Backend Development 11 min read

Global Exception Handling in Spring MVC Using @ControllerAdvice and @ExceptionHandler

This article explains why global exception handling is needed in Spring MVC, outlines its application scenarios, compares AOP with the simpler @ControllerAdvice/@ExceptionHandler approach, and provides detailed code examples for custom response objects, validation handling, and transaction rollback strategies to improve code cleanliness and maintainability.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Global Exception Handling in Spring MVC Using @ControllerAdvice and @ExceptionHandler

In daily development, catching exceptions in each controller leads to tightly coupled and messy code; a global exception handler centralizes error processing and returns friendly responses.

Benefits include removing repetitive try‑catch blocks, handling validation errors uniformly, providing user‑friendly error pages, and preventing long stack traces from being printed.

There are two main methods: using Spring AOP (more complex) or the simpler combination of @ControllerAdvice and @ExceptionHandler .

Using @ControllerAdvice and @ExceptionHandler

Define a class annotated with @ControllerAdvice and methods annotated with @ExceptionHandler to handle specific exception types, returning either a view name or a JSON body.

@ControllerAdvice
public class ControllerHandlers {
    @ExceptionHandler
    public String errorHandler(Exception e) {
        return "error";
    }
}

For REST APIs, @RestControllerAdvice automatically adds @ResponseBody to the response.

@RestControllerAdvice
public class ControllerHandlers {
    @ExceptionHandler
    public String errorHandler(Exception e) {
        return "error";
    }
}

A custom AjaxResult class (extending HashMap ) is used to standardize success, warning, error, unauth, and unlogin responses, with static factory methods such as success() , error() , unauth() , etc.

public class AjaxResult extends HashMap
{
    public enum Type { SUCCESS(1), WARN(2), ERROR(0), UNAUTH(3), UNLOGIN(4); }
    private Type type; private int code; private String msg; private Object data;
    // constructors and static methods omitted for brevity
}

The GlobalExceptionHandler class demonstrates handling various exceptions:

AuthorizationException – returns JSON for Ajax requests or a view for normal requests.

HttpRequestMethodNotSupportedException – returns an error message.

RuntimeException – logs and returns a generic runtime error.

Exception – catches all other exceptions and returns a server‑error message.

MethodArgumentNotValidException, BindException, ConstraintViolationException – extracts validation error messages and returns them.

BusinessException and DemoModeException – logs and returns specific error messages.

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(AuthorizationException.class)
    public Object handleAuthorizationException(HttpServletRequest request, AuthorizationException e) {
        if (ServletUtils.isAjaxRequest(request)) {
            return AjaxResult.unauth(PermissionUtils.getMsg(e.getMessage()));
        } else {
            ModelAndView mv = new ModelAndView();
            mv.setViewName("error/unauth");
            return mv;
        }
    }
    // other handlers omitted for brevity
}

Validation can be performed with @Validated and @Valid annotations. Example Person class shows field constraints, and a controller method demonstrates automatic validation.

public class Person {
    @NotNull @PersonName(prefix = "song") private String name;
    @Min(18) @Max(value = 30, message = "超过30岁的不要!") private Integer age;
}

@RestController
public class PersonController {
    @PostMapping("/person")
    public Person savePerson(@Valid @RequestBody Person person) {
        return person;
    }
}

Custom exceptions (e.g., MyException ) should extend RuntimeException to trigger transaction rollback in service layers; catching generic Exception and re‑throwing a RuntimeException ensures rollback.

public class MyException extends RuntimeException { }

try {
    // business logic
} catch (Exception e) {
    logger.error("发生异常", e);
    throw new RuntimeException();
}

Finally blocks should avoid returning values that could mask exceptions, as a return in finally overrides any exception thrown in try or catch .

BackendJavaException HandlingSpringvalidationControllerAdvice
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.