目录:
@ControllerAdvice是Spring提供的注释,使您可以编写可应用于各种控制器的全局代码-从所有控制器到选定的包,甚至是特定的注释。在这个简短的教程中,我们将专注于处理异常使用@ControllerAdvice和@ExceptionHandler(@InitBinder和@ModalAttribute也可以使用@ControllerAdvice)。
默认情况下, @ControllerAdvice将应用于使用@Controller注释的所有类(扩展到使用的类@RestController)。如果您想更具体一点,可以使用一些属性来实现这一点。
要按包减少适用的类,您只需要将包的名称添加到注释中。选择一个程序包后,将为该程序包内的子类以及子程序包启用该程序包。也可以按照相同的过程选择多个包,但是使用数组而不是单个字符串(其中的所有属性都@ControllerAdvice可以是单个或多个)。
@ControllerAdvice("my.chosen.package")
@ControllerAdvice(value = "my.chosen.package")
@ControllerAdvice(basePackages = "my.chosen.package")
如果要指定特定的包,可以通过basePackageClasses属性来指定包,该属性将使@ControllerAdvice类(或接口)所在的包中的所有控制器都可用。
如果要指定特定的类,可以通过assignableTypes = MyController.class来指定。
如果要应用于某些带注释的控制器,可以使用 @ControllerAdvice(annotations = RestController.class) ,此处仅对RestController起作用,而不会对带controller的起作用,尽管RestControlle注解相当于@ResponseBody + @Controller。
@ExceptionHandler顾名思义,您可以定义一个处理异常的方法。如果您不使用@ControllerAdvice ,则处理这些异常的代码将在控制器本身中,这可能会给类增加很多重复和混乱,并导致其不那么“干净”。您可以将这些@ExceptionHandler方法移到控制器扩展以分离代码的基类中。此方法并不完美,并且存在一个问题,即您需要此全局异常处理的每个控制器现在都需要扩展基本控制器。因此,当您创建一个新的控制器而忘记扩展此基类时,您现在不再处理某些异常,以后可能会陷入困境。使用@ControllerAdvice随@ExceptionHandler 通过提供全局(更具体的)错误处理来防止这种情况的发生,因此您无需记住自己实现它们或每次扩展另一个类。
示例:
- import java.text.DateFormat;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.Map;
-
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.propertyeditors.CustomDateEditor;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.WebDataBinder;
- import org.springframework.web.bind.annotation.ControllerAdvice;
- import org.springframework.web.bind.annotation.ExceptionHandler;
- import org.springframework.web.bind.annotation.InitBinder;
- import org.springframework.web.bind.annotation.ModelAttribute;
-
- //@ControllerAdvice(basePackages = {"springboot.controller"}) //指定一个或多个包,这些包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理。
- //@ControllerAdvice(basePackageClasses = {HelloController.class}) //指定一个或多个 Controller 类,这些类所属的包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理。
- //@ControllerAdvice(assignableTypes = {HelloController.class}) //指定一个或多个 Controller 类,这些类被该 @ControllerAdvice 管理
- //@ControllerAdvice(annotations = {HelloController.class}) //指定一个或多个注解,被这些注解所标记的 Controller 会被该 @ControllerAdvice 管理。
- //指定特定的类生效(assignableTypes = HelloController.class)) 只对HelloController文件做异常处理
- //指定所在的包生效(basePackageClasses={HelloController.class})
- @ControllerAdvice
- public class GlobalExceptionHandler {
-
- private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
-
- // 全局异常处理
- @ExceptionHandler(ArithmeticException.class)
- // 或者 public ResponseEntity<Object> costomException(ArithmeticException e) {
- public ResponseEntity<Object> costomException1(Exception e) {
- logger.info("被除数不能为0", e);
- Map<String, Object> result = new HashMap();
- result.put("code", 1);
- result.put("msg", "被除数不能为0");
- return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
- }
-
- @ExceptionHandler(ArrayIndexOutOfBoundsException.class)
- // 或者 public ResponseEntity<Object> costomException(ArrayIndexOutOfBoundsException e) {
- public ResponseEntity<Object> costomException2(Exception e) {
- logger.info("数组索引越界异常", e);
- Map<String, Object> result = new HashMap();
- result.put("code", 1);
- result.put("msg", "数组索引超出异常");
- return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
- }
-
- /**
- * @Description: 没有指定的异常走这里
- */
- @ExceptionHandler
- public ResponseEntity<Object> costomException(Exception e) {
- logger.info("未知错误", e);
- Map<String, Object> result = new HashMap();
- result.put("code", 1);
- result.put("msg", "未知错误");
- return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
- }
-
- // 全局数据绑定
- @ModelAttribute(name = "initData")
- public Map<String,Object> mydata() {
- HashMap<String, Object> map = new HashMap<>();
- map.put("age", 99);
- map.put("gender", "男");
- return map;
- }
-
- // 全局数据预处理
- @InitBinder
- public void InitBinder(WebDataBinder binder) {
- //前端传入的时间格式必须是"yyyy-MM-dd"效果!
- DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
- CustomDateEditor dateEditor = new CustomDateEditor(df, true);
- binder.registerCustomEditor(Date.class, dateEditor);
- }
- }
此类@ExceptionHandler为所有控制器全局提供了方法,因为(单独从此代码中看不到)有多个throw的控制器PersonNotFoundException需要处理。RequestMapping此处的注释用于设置所返回的内容类型ResponseEntity。这些可以添加到方法本身,而不是需要返回的不同类型。每个@ExceptionHandler标记实例都负责处理一个异常。本示例中的方法仅捕获异常并获取其错误消息,然后将其与适当的响应代码组合在一起。
@ExceptionHandler为同一异常定义了多个对象 ,需要进行监视。当在同一类中定义时,Spring足以引发异常并在启动时失败。但是,当它们出现在不同的类中时,例如说两个@ControllerAdvice类,都带有的处理程序PersonNotFoundException,应用程序将启动-但将使用找到的第一个处理程序。如果您不知道,这可能会导致意外的行为。
@ControllerAdvice ,很多初学者可能都没有听说过这个注解,实际上,这是一个非常有用的注解,顾名思义,这是一个增强的 Controller。使用这个 Controller ,可以实现三个方面的功能:
灵活使用这三个功能,可以帮助我们简化很多工作,需要注意的是,这是 SpringMVC 提供的功能,在 Spring Boot 中可以直接使用,下面分别来看。
springboot项目全局异常处理@ControllerAdvice(方式二) - 探歌 - 博客园
示例:
GlobalExceptionHandler.class
- package springboot.controller;
-
- import java.text.DateFormat;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.Map;
-
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.propertyeditors.CustomDateEditor;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.WebDataBinder;
- import org.springframework.web.bind.annotation.ControllerAdvice;
- import org.springframework.web.bind.annotation.ExceptionHandler;
- import org.springframework.web.bind.annotation.InitBinder;
- import org.springframework.web.bind.annotation.ModelAttribute;
-
- //@ControllerAdvice(basePackages = {"springboot.controller"}) //指定一个或多个包,这些包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理。
- //@ControllerAdvice(basePackageClasses = {HelloController.class}) //指定一个或多个 Controller 类,这些类所属的包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理。
- //@ControllerAdvice(assignableTypes = {HelloController.class}) //指定一个或多个 Controller 类,这些类被该 @ControllerAdvice 管理
- //@ControllerAdvice(annotations = {HelloController.class}) //指定一个或多个注解,被这些注解所标记的 Controller 会被该 @ControllerAdvice 管理。
- //指定特定的类生效(assignableTypes = HelloController.class)) 只对HelloController文件做异常处理
- //指定所在的包生效(basePackageClasses={HelloController.class})
- @ControllerAdvice
- public class GlobalExceptionHandler {
-
- private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
-
- // 全局异常处理
- @ExceptionHandler(ArithmeticException.class)
- // 或者 public ResponseEntity<Object> costomException(ArithmeticException e) {
- public ResponseEntity<Object> costomException1(Exception e) {
- logger.info("被除数不能为0", e);
- Map<String, Object> result = new HashMap();
- result.put("code", 1);
- result.put("msg", "被除数不能为0");
- return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
- }
-
- @ExceptionHandler(ArrayIndexOutOfBoundsException.class)
- // 或者 public ResponseEntity<Object> costomException(ArrayIndexOutOfBoundsException e) {
- public ResponseEntity<Object> costomException2(Exception e) {
- logger.info("数组索引越界异常", e);
- Map<String, Object> result = new HashMap();
- result.put("code", 1);
- result.put("msg", "数组索引超出异常");
- return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
- }
-
- /**
- * @Description: 没有指定的异常走这里
- */
- @ExceptionHandler
- public ResponseEntity<Object> costomException(Exception e) {
- logger.info("未知错误", e);
- Map<String, Object> result = new HashMap();
- result.put("code", 1);
- result.put("msg", "未知错误");
- return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
- }
-
- // 全局数据绑定
- @ModelAttribute(name = "initData")
- public Map<String,Object> mydata() {
- HashMap<String, Object> map = new HashMap<>();
- map.put("age", 99);
- map.put("gender", "男");
- return map;
- }
-
- // 全局数据预处理
- @InitBinder
- public void InitBinder(WebDataBinder binder) {
- //前端传入的时间格式必须是"yyyy-MM-dd"效果!
- DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
- CustomDateEditor dateEditor = new CustomDateEditor(df, true);
- binder.registerCustomEditor(Date.class, dateEditor);
- }
- }
HelloController。
- package springboot.controller;
-
- import java.text.DateFormat;
- import java.text.SimpleDateFormat;
- import java.util.Date;
-
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.propertyeditors.CustomDateEditor;
- import org.springframework.boot.context.properties.EnableConfigurationProperties;
- import org.springframework.stereotype.Controller;
- import org.springframework.ui.Model;
- import org.springframework.ui.ModelMap;
- import org.springframework.web.bind.WebDataBinder;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.ResponseBody;
- import org.springframework.web.bind.annotation.RestController;
-
-
- @RestController
- @RequestMapping("/hello")
- public class HelloController {
-
- @RequestMapping("/hello1")
- public String hello1(Date date) { // http://localhost:8080/hello/hello1?date=2022-05-16
- System.out.println(date); // Mon May 16 00:00:00 CST 2022
- System.out.println(1/0);
- return "Hello Spring Boot!";
- }
-
- @RequestMapping("/hello2")
- public String hello2(Date date) {
- System.out.println(date);
- int[] aa = {2,34};
- System.out.println(aa[3]);
- return "Hello Spring Boot!";
- }
-
- @RequestMapping("/hello3")
- public String hello3(ModelMap param) {
- System.out.println(param); // {initData={gender=男, age=99}}
- return "Hello Spring Boot!";
- }
-
-
- }