我的编程空间,编程开发者的网络收藏夹
学习永远不晚

注解@RestControllerAdvice用法途

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

注解@RestControllerAdvice用法途

文章目录

一、@RestControllerAdvice是什么

@RestControllerAdvice是一个组合注解,由@ControllerAdvice@ResponseBody组成,而@ControllerAdvice继承了@Component,因此@RestControllerAdvice本质上是个Component,用于定义@ExceptionHandler@InitBinder@ModelAttribute方法,适用于所有使用@RequestMapping方法。

二、@RestControllerAdvice的特点

  1. 通过@ControllerAdvice注解可以将对于控制器的全局配置放在同一个位置。
  2. 注解了@RestControllerAdvice的类的方法可以使用@ExceptionHandler@InitBinder@ModelAttribute注解到方法上。
  3. @RestControllerAdvice注解将作用在所有注解了@RequestMapping的控制器的方法上。
  4. @ExceptionHandler:用于指定异常处理方法。当与@RestControllerAdvice配合使用时,用于全局处理控制器里的异常。
  5. @InitBinder:用来设置WebDataBinder,用于自动绑定前台请求参数到Model中。
  6. @ModelAttribute:本来作用是绑定键值对到Model中,当与@ControllerAdvice配合使用时,可以让全局的@RequestMapping都能获得在此处设置的键值对
@ControllerAdvice  public class GlobalController{           //(1)全局数据绑定    //应用到所有@RequestMapping注解方法      //此处将键值对添加到全局,注解了@RequestMapping的方法都可以获得此键值对      @ModelAttribute     public void addUser(Model model) {           model.addAttribute("msg", "此处将键值对添加到全局,注解了@RequestMapping的方法都可以获得此键值对");      }        //(2)全局数据预处理    //应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器      //用来设置WebDataBinder      @InitBinder("user")    public void initBinder(WebDataBinder binder) {    }            // (3)全局异常处理    //应用到所有@RequestMapping注解的方法,在其抛出Exception异常时执行      //定义全局异常处理,value属性可以过滤拦截指定异常,此处拦截所有的Exception      @ExceptionHandler(Exception.class)        public String handleException(Exception e) {            return "error";    }    }  

@ControllerAdvice可以指定 Controller 范围

  • basePackages: 指定一个或多个包,这些包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理
@RestControllerAdvice(basePackages={"top.onething"})@Slf4jpublic class ExceptionHandlerAdvice {        @ExceptionHandler(Exception.class)        public String handleException(Exception e) {            return "error";    }   } 
  • basePackageClasses: 是 basePackages 的一种变形,指定一个或多个 Controller 类,这些类所属的包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理
@RestControllerAdvice(basePackageClasses={TestController.class})@Slf4jpublic class ExceptionHandlerAdvice {    @ExceptionHandler(Exception.class)        public String handleException(Exception e) {            return "error";    } }  
  • assignableTypes: 指定一个或多个 Controller 类,这些类被该 @ControllerAdvice 管理
@RestControllerAdvice(assignableTypes={TestController.class})@Slf4jpublic class ExceptionHandlerAdvice {    @ExceptionHandler(Exception.class)        public String handleException(Exception e) {            return "error";    } }  
  • annotations: 指定一个或多个注解,被这些注解所标记的 Controller 会被该 @ControllerAdvice 管理
@ControllerAdvice(annotations = {TestAnnotation.class})@Slf4jpublic class ExceptionHandlerAdvice {    @ExceptionHandler(Exception.class)        public String handleException(Exception e) {            return "error";    } } 

三、@ExceptionHandler

我们可以搭配@ResponseStatus:可以将某种异常映射为HTTP状态码

首先需要为自己的系统设计一个自定义的异常类,通过它来传递状态码。

public class SystemException extends RuntimeException{    private String code;//状态码    public SystemException(String message, String code) {        super(message);        this.code = code;    }    public String getCode() {        return code;    }}

第一种思路,设计一个基类

public class BaseController {        @ExceptionHandler    @ResponseBody    public Object expHandler(Exception e){        if(e instanceof SystemException){            SystemException ex= (SystemException) e;            return WebResult.buildResult().status(ex.getCode()).msg(ex.getMessage());        }else{            e.printStackTrace();            return WebResult.buildResult().status(Config.FAIL).msg("系统错误");        }    }}

之后所有需要异常处理的Controller都继承这个类,从而获取到异常处理的方法。
虽然这种方式可以解决问题,但是极其不灵活,因为动用了继承机制就只为获取一个默认的方法,这显然是不好的。

第二种思路,将这个基类变为接口,提供此方法的默认实现(也就是接口中的default方法,java8开始支持接口方法的默认实现)

public interface DataExceptionSolver {    @ExceptionHandler    @ResponseBody    default Object exceptionHandler(Exception e){        try {            throw e;        } catch (SystemException systemException) {            systemException.printStackTrace();            return WebResult.buildResult().status(systemException.getCode())                    .msg(systemException.getMessage());        } catch (Exception e1){            e1.printStackTrace();            return WebResult.buildResult().status(Config.FAIL)                    .msg("系统错误");        }    }}

这种方式虽然没有占用继承,但是也不是很优雅,因为几乎所有的Controller都需要进行异常处理,于是我每个Controller都需要去写implement DataExceptionSolver,这显然不是我真正想要的。况且这种方式依赖java8才有的语法,这是一个很大的局限。

第三种思路,使用加强Controller做全局异常处理。

来个案例

1、定义一个异常信息描述基础信息接口类

public interface BaseErrorInfoInterface {         String getResultCode();         String getResultMsg();}

2、定义一个枚举类实现上面的异常信息描述接口

public enum CommonEnum implements BaseErrorInfoInterface {    // 数据操作错误定义    SUCCESS("200", "成功!"),     BODY_NOT_MATCH("400","请求的数据格式不符!"),    SIGNATURE_NOT_MATCH("401","请求的数字签名不匹配!"),    NOT_FOUND("404", "未找到该资源!"),     INTERNAL_SERVER_ERROR("500", "服务器内部错误!"),    SERVER_BUSY("503","服务器正忙,请稍后再试!");        private String resultCode;        private String resultMsg;    CommonEnum(String resultCode, String resultMsg) {        this.resultCode = resultCode;        this.resultMsg = resultMsg;    }    @Override    public String getResultCode() {        return resultCode;    }    @Override    public String getResultMsg() {        return resultMsg;    }}

3、定义一个自定义异常类,标识业务系统出现的异常信息

public class BizException extends RuntimeException {    private static final long serialVersionUID = 1L;        protected String errorCode;        protected String errorMsg;    public BizException() {        super();    }    public BizException(BaseErrorInfoInterface errorInfoInterface) {        super(errorInfoInterface.getResultCode());        this.errorCode = errorInfoInterface.getResultCode();        this.errorMsg = errorInfoInterface.getResultMsg();    }    public BizException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) {        super(errorInfoInterface.getResultCode(), cause);        this.errorCode = errorInfoInterface.getResultCode();        this.errorMsg = errorInfoInterface.getResultMsg();    }    public BizException(String errorMsg) {        super(errorMsg);        this.errorMsg = errorMsg;    }    public BizException(String errorCode, String errorMsg) {        super(errorCode);        this.errorCode = errorCode;        this.errorMsg = errorMsg;    }    public BizException(String errorCode, String errorMsg, Throwable cause) {        super(errorCode, cause);        this.errorCode = errorCode;        this.errorMsg = errorMsg;    }    public String getErrorCode() {        return errorCode;    }    public void setErrorCode(String errorCode) {        this.errorCode = errorCode;    }    public String getErrorMsg() {        return errorMsg;    }    public void setErrorMsg(String errorMsg) {        this.errorMsg = errorMsg;    }    public String getMessage() {        return errorMsg;    }    @Override    public Throwable fillInStackTrace() {        return this;    }}

4、定义一个统一结果返回数据封装类

public class ResultBody {        private String code;        private String message;        private Object result;    public ResultBody() {    }    public ResultBody(BaseErrorInfoInterface errorInfo) {        this.code = errorInfo.getResultCode();        this.message = errorInfo.getResultMsg();    }    public String getCode() {        return code;    }    public void setCode(String code) {        this.code = code;    }    public String getMessage() {        return message;    }    public void setMessage(String message) {        this.message = message;    }    public Object getResult() {        return result;    }    public void setResult(Object result) {        this.result = result;    }        public static ResultBody success() {        return success(null);    }        public static ResultBody success(Object data) {        ResultBody rb = new ResultBody();        rb.setCode(CommonEnum.SUCCESS.getResultCode());        rb.setMessage(CommonEnum.SUCCESS.getResultMsg());        rb.setResult(data);        return rb;    }        public static ResultBody error(BaseErrorInfoInterface errorInfo) {        ResultBody rb = new ResultBody();        rb.setCode(errorInfo.getResultCode());        rb.setMessage(errorInfo.getResultMsg());        rb.setResult(null);        return rb;    }        public static ResultBody error(String code, String message) {        ResultBody rb = new ResultBody();        rb.setCode(code);        rb.setMessage(message);        rb.setResult(null);        return rb;    }        public static ResultBody error( String message) {        ResultBody rb = new ResultBody();        rb.setCode("-1");        rb.setMessage(message);        rb.setResult(null);        return rb;    }    @Override    public String toString() {        return JSONObject.toJSONString(this);    }}

5、定义一个全局异常处理类

@ControllerAdvicepublic class GlobalExceptionHandler {    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);        @ExceptionHandler(value = BizException.class)      @ResponseBody      public  ResultBody bizExceptionHandler(HttpServletRequest req, BizException e){        logger.error("发生业务异常!原因是:{}",e.getErrorMsg());        return ResultBody.error(e.getErrorCode(),e.getErrorMsg());    }        @ExceptionHandler(value =NullPointerException.class)    @ResponseBody    public ResultBody exceptionHandler(HttpServletRequest req, NullPointerException e){        logger.error("发生空指针异常!原因是:",e);        return ResultBody.error(CommonEnum.BODY_NOT_MATCH);    }        @ExceptionHandler(value =Exception.class)    @ResponseBody    public ResultBody exceptionHandler(HttpServletRequest req, Exception e){        logger.error("未知异常!原因是:",e);        return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR);    }}

说明:上面的代码,使用了@ControllerAdvice@ExceptionHandler注解。其中@ControllerAdvice的作用是开启对全局异常的捕获,这个注解还可以通过assignableTypes参数指定特定的Controller类,让异常处理类只处理特定类抛出的异常。@ExceptionHandler注解,标明了该处理方法体处理的异常类型。

四、@InitBinder

SpringMVC并不是能对所有类型的参数进行绑定的,如果对日期Date类型参数进行绑定,就会报错IllegalStateException错误。所以需要注册一些类型绑定器用于对参数进行绑定。InitBinder注解就有这个作用。

    @InitBinder    public void dateTypeBinder(WebDataBinder webDataBinder){        //往数据绑定器中添加一个DateFormatter日期转化器。        webDataBinder.addCustomFormatter(new DateFormatter("yyyy-mm-dd"));    }

使用@InitBinder 注册的绑定器只有在当前Controller中才有效,不会作用于其他Controller。

如果觉得在每个Controller里面写太复杂,你可以写个BaseController,让其他Controller继承该类

我们可以自定义格式转化器,实现Formatter接口就可。还可以添加验证器等等。

public class StringFormatter implements Formatter<String> {    private static final String PREFIX = "prefix- ";    @Override    public String parse(String text, Locale locale) throws ParseException {    //所以String类型参数都加上一个前缀。        String result = PREFIX + text;        return result;    }    @Override    public String print(String object, Locale locale) {        return object;    }}

然后添加到数据绑定器中

    @InitBinder    public void dateTypeBinder(WebDataBinder webDataBinder){        //往数据绑定器中添加一个DateFormatter日期转化器。        webDataBinder.addCustomFormatter(new DateFormatter("yyyy-mm-dd"));        // 添加一个String类型数据绑定器,作用是添加一个前缀        webDataBinder.addCustomFormatter(new StringFormatter());    }

当然,你也可以自己注册自定义的编辑器

自定义的编辑器类需要继承org.springframework.beans.propertyeditors.PropertiesEditor;并重写其setAsText和getAsText两个方法就行了

然后在InitBinder方法中注册就行。

这里提供给大家一个Demo

public class BaseController {    @InitBinder    protected void initBinder(WebDataBinder binder) {        binder.registerCustomEditor(Date.class, new MyDateEditor());        binder.registerCustomEditor(Double.class, new DoubleEditor());         binder.registerCustomEditor(Integer.class, new IntegerEditor());    }    private class MyDateEditor extends PropertyEditorSupport {        @Override        public void setAsText(String text) throws IllegalArgumentException {            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");            Date date = null;            try {                date = format.parse(text);            } catch (ParseException e) {                format = new SimpleDateFormat("yyyy-MM-dd");                try {                    date = format.parse(text);                } catch (ParseException e1) {                }            }            setValue(date);        }    }        public class DoubleEditor extends PropertiesEditor  {            @Override            public void setAsText(String text) throws IllegalArgumentException {                if (text == null || text.equals("")) {                    text = "0";                }                setValue(Double.parseDouble(text));            }                    @Override            public String getAsText() {                return getValue().toString();            }        }          public class IntegerEditor extends PropertiesEditor {            @Override            public void setAsText(String text) throws IllegalArgumentException {                if (text == null || text.equals("")) {                    text = "0";                }                setValue(Integer.parseInt(text));            }                    @Override            public String getAsText() {                return getValue().toString();            }        }  }

利用@InitBinder实现表单多对象传递小技巧

Student对象和Course对象:

public class Student implements Serializable{    String id;    String note;    //get..set....  }  public class Course implements Serializable{    String id;    String note;  //set..get...  }  

HTML页面:

<form action="/test/test" method="get">     <input type="text" name="student.id" value="student_id">     <input type="text" name="student.name" value="student_name">     <input type="text" name="course.id" value="course_id">     <input type="text" name="course.name" value="course_name">     <input type="submit" value="提交">  form> 

Controller:

@Controller  @RequestMapping("/classtest")  public class TestController {      // 绑定变量名字和属性,参数封装进类      @InitBinder("student")      public void initBinderUser(WebDataBinder binder) {          // 这里的”.”千万别忘记了    // 表示去掉前缀 student.        binder.setFieldDefaultPrefix("student.");      }      // 绑定变量名字和属性,参数封装进类      @InitBinder("course")      public void initBinderAddr(WebDataBinder binder) {          binder.setFieldDefaultPrefix("course.");      }      @RequestMapping("/methodtest")      @ResponseBody     public Map<String,Object> test(Student student,@ModelAttribute("course") Course course){          Map<String,Object> map=new HashMap<String,Object>();          map.put("student", student);          map.put("course", course);          return map;      }

@InitBinder() 中间的value值,用于指定表单属性或请求参数的名字,符合该名字的将使用此处的DataBinder。比如:student.id和student.note。student就得是中间的value值,这样才能接收得到。而且student会填充进WebDataBinder,这里binder对象就是student了。也可以用@ModelAttribute("student")做限定。

来源地址:https://blog.csdn.net/m0_51620667/article/details/127013347

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

注解@RestControllerAdvice用法途

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Attributes.Add用途与用法

Attributes.Add用于向对象添加新的属性或修改现有属性的值。用法如下:1. 添加属性:```Attributes.Add("属性名", "属性值");```2. 修改属性值:```Attributes["属性名"] = "新的属性
2023-09-15

plsql注册码的用途是什么

PL/SQL注册码是用于注册和激活PL/SQL开发工具的授权证书。它的主要用途是验证用户是否有权使用PL/SQL开发工具,并限制非授权用户的访问和使用。使用PL/SQL注册码可以实现以下功能:1. 许可证管理:注册码可以跟踪和管理许可证的使
2023-09-14

not null注解用法详解

用法是通过设定字段的约束,可以限制字段的取值范围,避免数据错误或者不符合业务规则的情况发生。not null注解是一种用于约束数据库表字段的注解,它用于指示该字段不允许为空。在数据库设计中,字段的约束是保证数据完整性和一致性的重要手段之一。
not null注解用法详解
2023-11-07

注册云服务器的用途有哪些

云服务器是一种虚拟计算环境,可按需提供可扩展的计算能力。其优点包括弹性、可扩展性、按需付费和高可靠性。云服务器广泛用于网站和应用程序托管、数据存储和管理、计算密集型应用程序、云原生应用程序开发以及远程桌面、游戏服务器、物联网、数据库托管和备份/灾难恢复等其他用途。
注册云服务器的用途有哪些
2024-04-09

注册云服务器的用途有哪些

注册云服务器的用途包括:1. 网站托管:可以将网站的文件、数据库等托管在云服务器上,提供更稳定的服务和更快的访问速度。2. 应用程序部署:可以将应用程序部署在云服务器上,提供灵活的计算资源和可靠的运行环境。3. 数据备份与存储:可以将重要数
2023-09-27

Python中@注解的用法

在java中,@注解的用处各位应该都了解一些。在python中,@可以将python定义的函数“当做”属性访问,从而提供更加友好访问方式。在之前的文章中提到过静态方法,也是使用@来实现的。是@staticmethod
2023-01-31

spring的@Transactional注解用法解读

概述事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。Spring Framework对事务管理提供了一致的抽象,其特点如下:为不同的事务API提供一致的编程模型,比如JTA(Java Transaction
2023-05-30

云服务器的用法和用途

云服务器(CloudStorage)是一种使用虚拟化技术将计算资源(例如数据库、文件系统和应用程序)集中存储、管理和访问的系统。以下是几种常见的用法和用途:存储:云服务器通常用于存储在多个云存储平台(例如AWS、GoogleS3和微软的Azure)中的数据。通过使用云存储,企业可以将数据存储在多个地方,从而更好地保护数据的安全性和隐私性。应用程序部署:企业往往需要使用云服务来将其应用程序部
2023-10-26

关于@EnableGlobalMethodSecurity注解的用法解读

这篇文章主要介绍了关于@EnableGlobalMethodSecurity注解的用法解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-03-19

Java中注解@JsonFormat的用法详解

这篇文章主要给大家介绍了关于Java中注解@JsonFormat用法的相关资料,以及分享了@JsonFormat将枚举序列化为对象的方法,文中给出了详细的代码实例,需要的朋友可以参考下
2023-01-06

JUnit5常用注解的使用方法

这篇文章主要介绍“JUnit5常用注解的使用方法”,在日常操作中,相信很多人在JUnit5常用注解的使用方法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JUnit5常用注解的使用方法”的疑惑有所帮助!接下来
2023-06-20

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录