SpringBoot中@ControllerAdvice的三种使用场景

一、全局异常处理

代码示例如下:

/**
 * @author qinxun
 * @date 2023-06-14
 * @Descripion: 业务层异常枚举
 */
public enum ServiceExceptionEnum {
    SUCCESS(0, "成功"),
    ERROR(1, "失败"),
    SYS_ERROR(1000, "服务端发生异常"),
    MISSING_REQUEST_PARAM_ERROR(1001, "参数缺失"),
    INVALID_REQUEST_PARAM_ERROR(1002, "请求参数不合法");

    private final String message;

    private final int code;

    ServiceExceptionEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public int getCode() {
        return code;
    }
}
import com.example.quartzdemo.enums.ServiceExceptionEnum;

import java.io.Serializable;

/**
 * @author qinxun
 * @date 2023-06-14
 * @Descripion: 统一返回结果实体类
 */
public class CommonResult<T> implements Serializable {


    /**
     * 错误码
     */
    private Integer code;
    /**
     * 错误提示
     */
    private String message;
    /**
     * 返回数据
     */
    private T data;

    /**
     * 成功
     *
     * @param data
     * @param <T>
     * @return
     */
    public static <T> CommonResult<T> success(T data) {
        CommonResult<T> commonResult = new CommonResult<>();
        commonResult.setCode(ServiceExceptionEnum.SUCCESS.getCode());
        commonResult.setMessage(ServiceExceptionEnum.SUCCESS.getMessage());
        commonResult.setData(data);
        return commonResult;
    }

    /**
     * 失败
     *
     * @param message
     * @param <T>
     * @return
     */
    public static <T> CommonResult<T> error(String message) {
        CommonResult<T> commonResult = new CommonResult<>();
        commonResult.setCode(ServiceExceptionEnum.ERROR.getCode());
        commonResult.setMessage(message);
        return commonResult;
    }

    /**
     * 失败
     *
     * @param message
     * @param <T>
     * @return
     */
    public static <T> CommonResult<T> error(int code, String message) {
        CommonResult<T> commonResult = new CommonResult<>();
        commonResult.setCode(code);
        commonResult.setMessage(message);
        return commonResult;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "CommonResult{" +
                "code=" + code +
                ", message='" + message + '\'' +
                ", data=" + data +
                '}';
    }
}

全局异常处理类:

import com.example.quartzdemo.common.CommonResult;
import com.example.quartzdemo.enums.ServiceExceptionEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

/**
 * @author qinxun
 * @date 2023-06-14
 * @Descripion: 全局异常处理
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理 MissingServletRequestParameterException 异常
     * <p>
     * SpringMVC 参数不正确
     */
    @ResponseBody
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public CommonResult<String> missingServletRequestParameterExceptionHandler(HttpServletRequest req, MissingServletRequestParameterException ex) {
        log.error("[missingServletRequestParameterExceptionHandler]", ex);
        // 包装 CommonResult 结果
        return CommonResult.error(ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getCode(),
                ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = ConstraintViolationException.class)
    public CommonResult<String> constraintViolationExceptionHandler(HttpServletRequest req, ConstraintViolationException ex) {
        log.error("[constraintViolationExceptionHandler]", ex);
        // 拼接错误
        StringBuilder detailMessage = new StringBuilder();
        for (ConstraintViolation<?> constraintViolation : ex.getConstraintViolations()) {
            // 使用 ; 分隔多个错误
            if (detailMessage.length() > 0) {
                detailMessage.append(";");
            }
            // 拼接内容到其中
            detailMessage.append(constraintViolation.getMessage());
        }
        // 包装 CommonResult 结果
        return CommonResult.error(
                ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString());
    }

    /**
     * 处理参数校验异常
     *
     * @param req
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = BindException.class)
    public CommonResult<String> bindExceptionHandler(HttpServletRequest req, BindException ex) {
        log.info("========进入了 bindException======");
        log.error("[bindExceptionHandler]", ex);
        // 拼接错误
        StringBuilder detailMessage = new StringBuilder();
        for (ObjectError objectError : ex.getAllErrors()) {
            // 使用 ; 分隔多个错误
            if (detailMessage.length() > 0) {
                detailMessage.append(";");
            }
            // 拼接内容到其中
            detailMessage.append(objectError.getDefaultMessage());
        }
        // 包装 CommonResult 结果
        return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(),
                ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString());
    }

    /**
     * 处理参数校验异常
     *
     * @param req
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public CommonResult<String> MethodArgumentNotValidExceptionHandler(HttpServletRequest req, MethodArgumentNotValidException ex) {
        log.info("-----------------进入了 MethodArgumentNotValidException-----------------");
        log.error("[MethodArgumentNotValidException]", ex);
        // 拼接错误
        StringBuilder detailMessage = new StringBuilder();
        for (ObjectError objectError : ex.getBindingResult().getAllErrors()) {
            // 使用 ; 分隔多个错误
            if (detailMessage.length() > 0) {
                detailMessage.append(";");
            }
            // 拼接内容到其中
            detailMessage.append(objectError.getDefaultMessage());
        }
        // 包装 CommonResult 结果
        return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(),
                ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString());
    }


    /**
     * 处理其它 Exception 异常
     *
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public CommonResult<String> exceptionHandler(HttpServletRequest req, Exception e) {
        // 记录异常日志
        log.error("[exceptionHandler]", e);
        // 返回 ERROR CommonResult
        return CommonResult.error(ServiceExceptionEnum.SYS_ERROR.getCode(),
                ServiceExceptionEnum.SYS_ERROR.getMessage());
    }
}

二、全局数据绑定

全局数据绑定可以用来做一些初始化数据的操作,我们可以将一些公共的数据定义到添加了@ControllerAdvice注解的类中,这样我们就可以在每一个Controller中可以访问这些数据。

1.定义全局数据绑定类

import java.util.HashMap;
import java.util.Map;

/**
 * @author qinxun
 * @date 2023-06-15
 * @Descripion: 全局数据绑定
 */
@ControllerAdvice
public class GlobalDataBindHandler {

    @ModelAttribute(name = "gd")
    public Map<String, Object> globalData() {
        Map<String, Object> map = new HashMap<>();
        map.put("website", "https://www.xx.com/");
        map.put("email", "xxxx@qq.com");
        return map;
    }
}

使用@ModelAttribute注解标记该方法返回的是一个全局数据。

2.我们在Controller类中使用这个全局数据

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**
 * @author qinxun
 * @date 2023-06-15
 * @Descripion: 全局数据绑定测试
 */
@RestController
public class HelloController {

    @GetMapping("/hello")
    public String toHello(Model model) {
        Map<String, Object> map = model.asMap();
        // 输出 {map={website=https://www.xx.com/, email=xxxx@qq.com}}
        System.out.println(map);
        return "hello";
    }
}

三、全局数据预处理

1.我们准备两个实体类

/**
 * @author qinxun
 * @date 2023-06-15
 * @Descripion: Author实体
 */
public class Author {

    private String name;

    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Author{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
/**
 * @author qinxun
 * @date 2023-06-15
 * @Descripion: Book实体
 */
public class Book {

    private String name;

    private Integer price;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

我们定义一个访问接口

import com.example.quartzdemo.bean.Author;
import com.example.quartzdemo.bean.Book;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author qinxun
 * @date 2023-06-15
 * @Descripion: 测试
 */
@RestController
public class TestController {


    /**
     * 测试
     *
     * @param book   Book对象
     * @param author Author对象
     */
    @PostMapping("/book")
    public void addBook(Book book, Author author) {
        System.out.println(book);
        System.out.println(author);
    }
}

这个时候,我们的添加操作就会有问题,因为这两个实体类中都有一个name的属性,前端传递数据的时候,无法区分是哪个实体的name属性,这种情况我们可以通过@ControllerAdvice的全局数据预处理来解决这个问题。

我们在postman上进行测试

 控制台打印返回

Book{name='书名,作者', price=20}
Author{name='书名,作者', age=20}

发现数据混乱了,这不是我们需要的结果。

如何解决?

1.给接口中的变量取别名

import com.example.quartzdemo.bean.Author;
import com.example.quartzdemo.bean.Book;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author qinxun
 * @date 2023-06-02
 * @Descripion: 测试
 */
@RestController
public class TestController {


    /**
     * 测试
     *
     * @param book   Book对象
     * @param author Author对象
     */
    @PostMapping("/book")
    public void addBook(@ModelAttribute("b") Book book, @ModelAttribute("a") Author author) {
        System.out.println(book);
        System.out.println(author);
    }
}

2.全局数据处理类

import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;

import java.util.HashMap;
import java.util.Map;

/**
 * @author qinxun
 * @date 2023-06-15
 * @Descripion: 全局数据处理
 */
@ControllerAdvice
public class GlobalDataBindHandler {

    // 全局数据绑定
    @ModelAttribute
    public Map<String, Object> globalData() {
        Map<String, Object> map = new HashMap<>();
        map.put("website", "https://www.xx.com/");
        map.put("email", "xxxx@qq.com");
        return map;
    }

    // 对命名为b的全局数据预处理
    @InitBinder("b")
    public void b(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("b.");
    }

    // 对命名为a的全局数据预处理
    @InitBinder("a")
    public void a(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("a.");
    }
}

@InitBinder("b")表示该方法用来处理和Book相关的参数,给参数添加一个b前缀,要求参数必须有b前缀。

3.postman测试

 控制台打印返回

Book{name='书名', price=20}
Author{name='作者', age=20}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/30774.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序自定义模块

自定义wxs并引入 新建一个tools.wxs 创建一些function,并使用moule.exports {}导出 使用 <wxs>标签 并填写正确src 书写module名称 之后在其他标签内&#xff0c;使用 {{自定的module名称.自定义的一个function并传入对应参数}}就可以实现参数在自定义function中的导入…

用docker搭建selenium grid分布式环境实践

目录 前言&#xff1a; selenium jar包直接启动节点 用docker命令直接启动 docker-compose 启动 Hub和node在一台机器上 Hub和node不在一台机器上 遗留问题 总结 前言&#xff1a; Selenium是一个流行的自动化测试工具&#xff0c;支持多种编程语言和多种浏览器。Sele…

SpringCloudAlibaba之Sentinel源码分析--protoc-3.17.3-win64

Sentinel源码分析 文章目录 Sentinel源码分析1.Sentinel的基本概念1.1.ProcessorSlotChain1.2.Node1.3.Entry1.3.1.自定义资源1.3.2.基于注解标记资源 1.4.Context1.4.1.什么是Context1.4.2.Context的初始化1.4.2.1.自动装配1.4.2.2.AbstractSentinelInterceptor1.4.2.3.Contex…

【linux kernel】linux media子系统分析之media控制器设备

文章目录 一、抽象媒体设备模型二、抽象媒体设备三、Entity四、Interfaces五、Pad六、Link七、Media图遍历八、使用计数和电源处理九、link设置十、Pipeline和Media流十一、链接验证十二、媒体控制器设备的分配器API 本文基于linux内核 4.19.4&#xff0c;抽象媒体设备模型框架…

chatgpt赋能python:Python如何查找特定名称文件

Python如何查找特定名称文件 在计算机文件管理和互联网网络应用程序中&#xff0c;查找特定文件往往是一项必要的任务。在使用Python编程时&#xff0c;我们可以使用Python内置的os模块来查找特定名称的文件。本文将介绍如何使用Python查找特定名称的文件&#xff0c;并提供实…

013:解决vue中不能加载.geojson的问题

第013个 查看专栏目录: VUE — element UI 本文章目录 问题状态造成这个结果的原因&#xff1a;解决办法Vue Loader 其他特性&#xff1a;专栏目标 问题状态 在做vue项目的时候&#xff0c;碰到这样一个问题&#xff0c;vue页面中引用一个.geojson文件&#xff0c;提示如下错误…

【C++篇】字符串:标准库string类

友情链接&#xff1a;C/C系列系统学习目录 知识总结顺序参考C Primer Plus&#xff08;第六版&#xff09;和谭浩强老师的C程序设计&#xff08;第五版&#xff09;等&#xff0c;内容以书中为标准&#xff0c;同时参考其它各类书籍以及优质文章&#xff0c;以至减少知识点上的…

面试篇:Java基础

目录 一、HashMap 的底层结构和原理 1、JDK7 2、JDK8 3、扩容问题 二、讲一下你对动态代理的理解 1、JDK动态代理 2、CGLIB动态代理 三、Java 集合体系的划分、List、Set、Map 的区别 四、ArrayList 和 LinkedList 的区别 1、数据结构实现&#xff1a; 2、随机访问&a…

Python-Selenium-定位详解

目录 前言&#xff1a; 一、id定位 二、name定位 三、class_name定位 四、xpath定位 五、css_selector定位 六、tag_name定位 七、link_text 定位 八、Xpath&Css定位方法速查表 九、By定位 十、elements复数定位 十一、JS的定位 前言&#xff1a; Python是一种…

pikachu靶场-PHP反序列化

在理解这个漏洞前,你需要先搞清楚php中serialize()&#xff0c;unserialize()这两个函数。 序列化serialize() 序列化说通俗点就是把一个对象变成可以传输的字符串,比如下面是一个对象: class S{public $test"pikachu";}$snew S(); //创建一个对象serialize($s); //…

eclipse中创建一个maven父工程和几个模块(子工程)

示例&#xff1a;创建一个父工程和几个模块&#xff08;子工程&#xff09; 1&#xff09;、先创建一个父工程 注意&#xff1a;下面的Packaging选择pom&#xff1a; 点击Finish&#xff0c;父工程就创建好了&#xff1a; 2&#xff09;、再创建模块&#xff08;module&am…

RuntimeError: launcher ‘pdsh‘ not installed解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

麒麟系统在线安装docker(x86/arm)

文章目录 一、查看系统版本&#xff0c;确认版本二、查看系统架构三、下载安装docker-ceX86架构安装aarch64架构 一、查看系统版本&#xff0c;确认版本 [rootlocalhost ~]# cat /etc/kylin-release Kylin Linux Advanced Server release V10 (Sword)二、查看系统架构 [root…

基于STM32的四旋翼无人机项目(二):MPU6050姿态解算(含上位机3D姿态显示教学)

前言&#xff1a;本文为手把手教学飞控核心知识点之一的姿态解算——MPU6050 姿态解算&#xff08;飞控专栏第2篇&#xff09;。项目中飞行器使用 MPU6050 传感器对飞行器的姿态进行解算&#xff08;四元数方法&#xff09;&#xff0c;搭配设计的卡尔曼滤波器与一阶低通滤波器…

软件设计模式之原型模式

一.定义 原型模式(Prototype Pattern)的简单程度仅次于单例模式和迭代器模式。正是由于简单&#xff0c;使用的场景才非常地多&#xff0c;其定义如下: Specify the kinds of objects to create using a prototypical instance, and create new objects by copyingthis protot…

第七章 测试

文章目录 第七章 测试7.1 编码7.1.1 选择程序设计语言1. 计算机程序设计语言基本上可以分为汇编语言和高级语言2. 从应用特点看&#xff0c;高级语言可分为基础语言、结构化语言、专用语言 7.1.2 编码风格 7.2 软件测试基础7.2.1 软件测试的目标7.2.2 软件测试准则7.2.3 测试方…

边缘智能:边缘计算驱动实时深度学习

边缘智能 作为人工智能领域的当红炸子鸡&#xff0c;深度学习技术近年来得到了学术界与产业界的大力追捧。目前&#xff0c;深度学习技术已在计算机视觉、自然语言处理以及语音识别等领域大放异彩&#xff0c;相关产品正如雨后春笋般涌现。由于深度学习模型需要进行大量的计算…

Delta 一个新的 git diff 对比显示工具

目录 介绍git diff 介绍delta介绍 一、安装1.下载 Git2.下载 delta3.解压4.修改配置文件5. 修改主题6.其他配置和说明 二、对比命令1.在项目中 git diff 常用命令2.对比电脑上两个文件3.对比电脑上的两个文件夹 三、在Git 命令行中使用效果四、在idea 的Terminal命令行中使用效…

linux 内核版本和发行版本

当要明确自己的Linux系统的版本号时&#xff0c;大多数情况是用命令确定Linux内核版本的。不过这个还是要与CentOS的版本号&#xff08;就是你使用的Linux系统的发行版本&#xff09;区分开来&#xff0c;这两个不是一个东西。 一、发行版本号 比如当时安装CentOS时&#x…

MySQL是什么,如何整合SpringBoot,以及使用优势

目录 一、MySQL是什么 二、如何整合SpringBoot 三、MySQL使用优势 一、MySQL是什么 MySQL是一种开源的关系型数据库管理系统&#xff0c;采用客户机/服务器模式实现数据存储和管理。其最初由瑞典的MySQL AB公司开发&#xff0c;后来被Sun Microsystems收购&#xff0c;最终…