Backend Development 7 min read

Mastering Global Exception Handling in Spring MVC with @RestControllerAdvice

This guide explains how Spring MVC's @ControllerAdvice, @RestControllerAdvice, and @ExceptionHandler annotations enable both local and global exception handling, including selective handling by custom annotations, specific controllers, or packages, with practical code examples for Java developers.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Global Exception Handling in Spring MVC with @RestControllerAdvice

Environment: Spring 5.3.23

1. Introduction

Spring MVC provides a flexible exception‑handling mechanism based on the @ControllerAdvice and @ExceptionHandler annotations. @ControllerAdvice defines a global exception‑handling class, while @ExceptionHandler specifies the exception types to handle. In RESTful projects the preferred annotation is @RestControllerAdvice , which combines @ControllerAdvice with @ResponseBody so that returned objects are automatically serialized to JSON.

2. Application Cases

Controller‑level Exception Handling

<code>@RestController
public class TestController {
  @GetMapping("/test/{id}")
  public Object test(@PathVariable Integer id) {
    if (id < 5) {
      throw new RuntimeException("运行时异常");
    }
    return "测试异常处理";
  }

  @ExceptionHandler
  public Object handle(Exception e) {
    return e.getMessage();
  }
}
</code>

This method handles exceptions only for the TestController . It is convenient for a single controller but becomes cumbersome when many controllers exist.

Global Exception Handling

<code>@RestControllerAdvice
public class TestControllerAdvice {
  @ExceptionHandler
  public Object handle(Exception e) {
    return "我是全局异常:" + e.getMessage();
  }
}
</code>

The global handler works when the controller does not define its own @ExceptionHandler . Local handlers have higher priority than global ones.

Handling Only Specific Annotations

<code>@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AppAnnotation {}
</code>

Controllers annotated with @AppAnnotation :

<code>@AppAnnotation
@RestController
public class AnnotationController {
  @GetMapping("/an/get/{id}")
  public Object an(@PathVariable Integer id) {
    if (id < 10) {
      throw new RuntimeException("发生错误了");
    }
    return "自定义Annotation注解: " + id;
  }
}
</code>

Controllers without the annotation:

<code>@RestController
public class AnnotationController2 {
  @GetMapping("/an/get2/{id}")
  public Object an(@PathVariable Integer id) {
    if (id < 10) {
      throw new RuntimeException("2发生错误了");
    }
    return "自定义Annotation注解2: " + id;
  }
}
</code>
<code>@RestControllerAdvice(annotations = {AppAnnotation.class})
public class AnnotationControllerAdvice {
  @ExceptionHandler
  public Object handle(Exception e) {
    return "特定注解全局异常:" + e.getMessage();
  }
}
</code>

Only the controller with @AppAnnotation is intercepted by the advice.

Handling Specific Controllers

<code>@RestController
public class UserController {
  @GetMapping("/user/{id}")
  public Object get(@PathVariable Integer id) {
    if (id < 10) {
      throw new RuntimeException("用户ID错误");
    }
    return "Users";
  }
}
</code>
<code>@RestController
public class PersonController {
  @GetMapping("/person/{id}")
  public Object get(@PathVariable Integer id) {
    if (id < 10) {
      throw new RuntimeException("Person ID错误");
    }
    return "Person";
  }
}
</code>
<code>@RestControllerAdvice(assignableTypes = {UserController.class})
public class SpecificControllerAdvice {
  @ExceptionHandler
  public Object handle(Exception e) {
    return "指定Controller全局异常:" + e.getMessage();
  }
}
</code>

The advice applies only to UserController ; exceptions from PersonController are not handled.

Handling Controllers in Specific Packages

<code>@RestControllerAdvice(basePackages = {"com.pack.pkg1"})
public class PackageControllerAdvice {
  @ExceptionHandler
  public Object handle(Exception e) {
    return "指定包下的全局异常:" + e.getMessage();
  }
}
</code>

When a controller in com.pack.pkg1 throws an exception, the advice handles it; controllers in other packages are unaffected.

Conclusion: Local (controller‑level) exception handling takes precedence over global exception handling.

Additional reference images about the parameters and return types accepted by @ExceptionHandler methods:

ExceptionHandler parameter types
ExceptionHandler parameter types
ExceptionHandler return types
ExceptionHandler return types
Javabackend developmentexception handlingSpring MVCRestControllerAdvice
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

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.