SpringBoot中@ControllerAdvice的三种使用场景

这篇具有很好参考价值的文章主要介绍了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上进行测试

SpringBoot中@ControllerAdvice的三种使用场景

 控制台打印返回文章来源地址https://www.toymoban.com/news/detail-485166.html

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测试

SpringBoot中@ControllerAdvice的三种使用场景

 控制台打印返回

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

到了这里,关于SpringBoot中@ControllerAdvice的三种使用场景的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringBoot 增强Controller方法,@ControllerAdvice注解的使用

    参考资料 @ControllerAdvice 用法 SpringBoot使用@ControllerAdvice注解 @ControllerAdvice ,是Spring3.2提供的新注解,它是一个Controller增强器,可对controller进行增强处理。 配合 @ExceptionHandler 注解,进行全局异常处理。 配合 @InitBinder 注解,用来设置 WebDataBinder ,用于自动绑定前台请求参数到

    2024年02月08日
    浏览(31)
  • 金融级低代码的三种应用场景和六个特色能力建设

    低代码平台在企业数字化转型中发挥着重要的作用,助力降低成本、提升效率。尤其对于金融行业而言,其规模庞大、复杂多变,各级分行、业务线以及科技部门都有使用低代码平台来增强效能的需求。 对于领导层 而言,要实现数字化转型在一线分支机构的全面推进,不能

    2024年01月18日
    浏览(26)
  • springboot依赖注入的三种方式

    springboot依赖注入的三种方式 在 Spring Boot 中,使用 XML 配置依赖注入(DI)时,需要使用 bean 元素来定义 bean,并使用 property 元素来为 bean 的属性注入值或依赖对象。 以下是一个简单的示例: 在 src/main/resources 目录下创建 applicationContext.xml 文件。 在该文件中定义一个 testBean

    2023年04月23日
    浏览(36)
  • 【SpringBoot】获取HttpServletRequest的三种方式

    线程安全 缺点: 每个方法都需要写一遍 线程安全 在 Spring 中, DemoRequestController 的 scope 是 singleton (单例),也就是说在整个 web 系统中,只有一个 DemoRequestController ;但是其中注入的 request 却是线程安全的,原因在于:使用这种方式,当 Bean (本例的 DemoRequestController )初始化

    2024年02月12日
    浏览(35)
  • 【SpringBoot18】SpringBoot 调用外部接口的三种方式

    SpringBoot不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化了Spring应用的整个搭建和开发过程。在Spring-Boot项目开发中,存在着本模块的代码需要访问外面模块接口,或外部url链接的需求, 比如在apaas开发过程中需要封装接口在接口中调用apaas提供的接口(

    2023年04月11日
    浏览(61)
  • SpringBoot实现分页的三种方式

    一 自己封装Page对象实现 博客链接 二 使用sql实现分页 2.1 场景分析 前段传递给给后台什么参数? 当前页码 currentPage 每页显示条数 pageSize 后台给前端返回什么数据? 当前页数据 List 总记录数 totalCount 2.2 前段代码 2.3 后端代码 PageBean mapper service impl controller 三 使用PageHelper插件

    2024年02月10日
    浏览(36)
  • SpringBoot导出Word文档的三种方式

    1、直接在Java代码里创建Word文档,设置格式样式等,然后导出。(略) 需要的见:https://blog.csdn.net/qq_42682745/article/details/120867432 2、富文本转换后的HTML下载为Word文档。相当于把HTML转为Word导出 3、使用模板技术导出。固定格式、可以写入不同数据 其他: springboot版本:2.7.11 导

    2024年02月02日
    浏览(41)
  • SpringBoot获取Request请求的三种方式

    Request对象包含了请求的各种信息,比如请求方法、请求URL、请求参数、请求内容等等,这些信息可以供服务器进行处理和响应。那么在SpringBoot中,怎么才能获取到Request对象? 本文将介绍三种方法,并提示例参考。 一、直接在Controller方法参数上注入HttpServletRequest 这是最常用

    2024年02月04日
    浏览(47)
  • SpringBoot获取HttpServletRequest、HttpServletResponse的三种方式

    仅仅适用在controller方法上。当Spring接收到HTTP请求时,会寻找一个合适的方法来处理该请求。如果该方法参数上标注了@RequestMapping或@Get、@Post等注解,Spring就会将HttpServletRequest对象注入到该参数中。 适用于所有的bean 通过调试可以看到,注入的Reques是一个代理类,而这个被代

    2024年02月19日
    浏览(34)
  • SpringBoot+MyBatis批量插入数据的三种方式

    最近导入表格数据时需要同时插入修改大量数据,研究了一下有三种实现方式 1、用for循环调用sql插入数据 这种方式插入大量数据时,效率非常底下,不推荐 2、利用mybatis的foreach来实现循环插入 这种方式插入大量数据时,好处是不用频繁访问数据库,一条sql搞定,效率比较

    2024年02月16日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包