Spring Boot - 异常处理

    技术2025-05-24  53

    文章目录

    Spring Boot - 异常处理准备工作默认的错误页面404500 自定义错误页面使用 @ExceptionHandler 注解处理异常使用 @ControllerAdvice 和 @ExceptionHandler 注解处理全局异常返回视图页面返回 JSON 格式的数据 @SimpleMappingExceptionResolver 简单映射异常解析器通过实现 HandlerExceptionResolver 接口处理全局异常

    Spring Boot - 异常处理

    准备工作

    新建一个 Spring Starter Project 项目,版本 2.2.2.RELEASE,添加 Spring Web 与 Thymeleaf 的依赖:

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

    默认的错误页面

    404

    启动应用,访问 http://localhost:8080/,由于该链接指向的页面不存在,所以返回 404:

    500

    新建一个控制器,人为制造一个异常:

    import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class DemoController { @GetMapping("/ex") public String ex() { int i = 5 / 0; // 算术异常 return "hello"; } }

    重启应用,访问 http://localhost:8080/ex,提示服务器错误:

    自定义错误页面

    在项目的 src/main/resources/templates/ 目录下新建一个 error.html 页面:

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>错误</title> </head> <body> <h1>错误,请联系管理员!</h1> </body> </html>

    修改 application.yml 配置文件:

    spring: thymeleaf: cache: false

    重启应用,访问 http://localhost:8080/ 或 http://localhost:8080/ex,Spring Boot 返回我们自定义的错误页面:

    使用 @ExceptionHandler 注解处理异常

    修改 DemoController,添加一个针对算术异常的处理方法:

    import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; @RestController public class DemoController { @GetMapping("/ex") public String ex() { int i = 5 / 0; // 算术异常 return "hello"; } /** * <p> 针对算术异常的处理方法 * @param e * @return */ @ExceptionHandler(value = { java.lang.ArithmeticException.class }) public ModelAndView exceptionHandler(Exception e) { ModelAndView mv = new ModelAndView(); mv.addObject("error", e.toString()); mv.setViewName("error"); return mv; } }

    修改 src/main/resources/templates/error.html 页面:

    <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>错误</title> </head> <body> <h1>错误,请联系管理员!</h1> <p th:text="${error}"></p> </body> </html>

    在第 2 行中,通过 xmlns:th="http://www.thymeleaf.org" 引入 Thymeleaf 命名空间。

    在第 9 行中,通过 th:text="${error}" 获取 DemoController.exceptionHandler(Exception) 返回的异常信息。

    重启应用,访问 http://localhost:8080/ex,可以得到如下异常信息:

    如需处理空指针异常,可以修改 @ExceptionHandler 注解,向 value 数组中添加 java.lang.NullPointerException.class 值:

    import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; @RestController public class DemoController { @GetMapping("/ex") public String ex() { int i = 5 / 0; // 算术异常 return "hello"; } @GetMapping("/np") public String np() { String s = null; s.length(); // 空指针异常 return "空指针异常"; } /** * <p> 针对算术异常和空指针异常的处理方法 * @param e * @return */ @ExceptionHandler(value = { java.lang.ArithmeticException.class, java.lang.NullPointerException.class }) public ModelAndView exceptionHandler(Exception e) { ModelAndView mv = new ModelAndView(); mv.addObject("error", e.toString()); mv.setViewName("error"); return mv; } }

    以上 @ExceptionHandler(value = { java.lang.ArithmeticException.class, java.lang.NullPointerException.class }) 只针对特定的异常,如果想针对大多数异常,可以这样修改:

    @ExceptionHandler(value = { Exception.class }) public ModelAndView exceptionHandler(Exception e) { ModelAndView mv = new ModelAndView(); mv.addObject("error", e.toString()); mv.setViewName("error"); return mv; }

    这样,只要异常是继承自 Exception 类,都可以被捕获、处理。

    使用 @ControllerAdvice 和 @ExceptionHandler 注解处理全局异常

    返回视图页面

    在上面的例子中,@ExceptionHandler 注解的作用范围仅在当前控制器,其并不处理其他控制器的异常。

    如果要处理全局异常,需要结合 @ControllerAdvice 注解使用。

    新建一个 ArithmeticExceptionController 控制器类,人为制造一个算术异常:

    import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ArithmeticExceptionController { @GetMapping("/ae") public String ae() { int i = 5 / 0; // 算术异常 return "算术异常"; } }

    再建一个 NullPointerExceptionController 控制器类,人为制造一个空指针异常:

    import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class NullPointerExceptionController { @GetMapping("/npe") public String npe() { String s = null; s.length(); // 空指针异常 return "空指针异常"; } }

    最后,新建一个 GlobalExceptionHandler 全局异常处理类:

    import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.ModelAndView; @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(value = { Exception.class}) public ModelAndView exceptionHandler(Exception e) { ModelAndView mv = new ModelAndView(); mv.addObject("error", e.toString()); mv.setViewName("error"); return mv; } }

    重启应用,分别访问 http://localhost:8080/ae 和 http://localhost:8080/npe,这两个控制器类中的方法抛出的异常均可被全局异常处理类捕获并处理:

    返回 JSON 格式的数据

    如果要返回 JSON 格式的数据,可以这样修改 GlobalExceptionHandler 类:

    import java.util.HashMap; import java.util.Map; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @ControllerAdvice public class GlobalExceptionHandler { // @ExceptionHandler(value = { Exception.class}) // public ModelAndView exceptionHandler(Exception e) { // ModelAndView mv = new ModelAndView(); // mv.addObject("error", e.toString()); // mv.setViewName("error"); // return mv; // } @ExceptionHandler(value = { Exception.class }) @ResponseBody public Map<String, Object> exceptionHandler(Exception e) { Map<String, Object> result = new HashMap<>(); result.put("code", 0); result.put("msg", e.toString()); result.put("data", null); return result; } }

    重启应用,分别访问 http://localhost:8080/ae 或 http://localhost:8080/npe,可以看到返回的是 JSON 格式的数据而非视图页面:

    @SimpleMappingExceptionResolver 简单映射异常解析器

    新建一个 GlobalSimpleMappingExceptionResolver 类:

    import java.util.Properties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; @Configuration public class GlobalSimpleMappingExceptionResolver { @Bean public SimpleMappingExceptionResolver simpleMappingExceptionResolver() { Properties properties = new Properties(); properties.put("java.lang.NullPointerException", "error-npe"); // 指定异常与视图页面的映射关系 properties.put("java.lang.ArithmeticException", "error-ae"); SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); resolver.setExceptionMappings(properties); return resolver; } }

    在项目的 src/main/resources/templates/ 目录下分别新建 error-ae.html 与 error-npe.html 文件:

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>算术异常</title> </head> <body> <h1>错误,请联系管理员!</h1> <p>java.lang.ArithmeticException</p> </body> </html> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>空指针</title> </head> <body> <h1>错误,请联系管理员!</h1> <p>java.lang.NullPointerException</p> </body> </html>

    注意:如果你是在原有项目上进行修改,则需注释上一个例子中的全局异常处理器,以免发生干扰。

    重启应用,分别访问 http://localhost:8080/ae 和 http://localhost:8080/npe,可以看到 Spring Boot 返回了不同的页面:

    通过实现 HandlerExceptionResolver 接口处理全局异常

    新建一个 GlobalHandlerExceptionResolver 实现 GlobalHandlerExceptionResolver 接口,重写 resolveException(HttpServletRequest, HttpServletResponse, Object, Exception) 方法:

    import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; @Configuration public class GlobalHandlerExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView mv = new ModelAndView(); mv.setViewName("error"); // 视图页面名称 mv.addObject("error", ex.toString()); // 错误提示信息 return mv; } }

    注意:如果你是在原有项目上进行修改,则需注释上一个例子中的全局异常处理器,以免发生干扰。

    重启应用,分别访问 http://localhost:8080/ae 和 http://localhost:8080/npe,可以看到 Spring Boot 返回的页面:

    Processed: 0.012, SQL: 9