【SpringBoot】统一功能处理详解


🎬 那我掉的头发算什么个人主页
🔥 个人专栏 : 《javaSE》《数据结构》《数据库》《javaEE》

⛺️待到苦尽甘来日


文章目录

统一数据返回格式

快速入门

统一的数据返回格式使用 @ControllerAdvice 和 ResponseBodyAdvice 的方式实现@ControllerAdvice 表示控制器通知类添加类 ResponseAdvice , 实现 ResponseBodyAdvice 接口,并在类上添加@ControllerAdvice 注解。

java 复制代码
package com.hbu.book.responseAdvice;

import org.jspecify.annotations.Nullable;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

public class ResponseAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return false;
    }

    @Override
    public @Nullable Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return null;
    }
}

supports 方法:判断是否要执行 beforeBodyWrite 方法. true 为执行,false 不执行。通过该方法可以选择哪些类或哪些方法的 response 要进行处理,其他的不进行处理。

beforeBodyWrite方法: 对response方法进行具体操作处理。

此时还没启动统一处理,这时访问接口返回结果是这样的:

java 复制代码
package com.hbu.book.model;



import com.hbu.book.enums.ResultCodeEnum;
import lombok.Data;

@Data
public class Result<T> {
    private ResultCodeEnum code;  //-1 未登录    200 正常  -2 出错
    private String errMsg;
    private T data;

    public static <T> Result success(T data){
        Result result = new Result();
        result.setCode(ResultCodeEnum.SUCCESS);
        result.setErrMsg("");
        result.setData(data);
        return result;
    }

    public static <T> Result fail(String errMsg){
        Result result = new Result();
        result.setCode(ResultCodeEnum.FAIL);
        result.setErrMsg(errMsg);
        result.setData(null);
        return result;
    }

    public static <T> Result fail(String errMsg, T data){
        Result result = new Result();
        result.setCode(ResultCodeEnum.FAIL);
        result.setErrMsg(errMsg);
        result.setData(data);
        return result;
    }

    public static <T> Result unlogin(){
        Result result = new Result();
        result.setCode(ResultCodeEnum.UNLOGIN);
        result.setErrMsg("用户未登录");
        return result;
    }
}
java 复制代码
package com.hbu.book.responseAdvice;

import com.hbu.book.model.Result;
import org.jspecify.annotations.Nullable;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

public class ResponseAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public @Nullable Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return Result.success(body);
    }
}

启动了之后再次访问接口:

存在问题



但是数据库中确实是插入了这个数据。

其他的操作都是正常的。

java 复制代码
package com.hbu.book.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/test")
@RestController
public class TestController {
    @RequestMapping("/t1")
    public String t1(){
        return "t1";
    }
    @RequestMapping("/t2")
    public boolean t2(){
        return true;
    }
    @RequestMapping("/t3")
    public Integer t3(){
        return 200;
    }
}

多方测试下,只有返回类型是String的接口才会发生这种错误。并且报错方式都是一样的。

核心原因是 Spring 处理 String 类型返回值的消息转换器优先级问题:当控制器方法返回 String 时,Spring 会优先使用 StringHttpMessageConverter 处理响应,但这个转换器只能处理 String 类型,而 ResponseAdvice 把 String 包装成了 Result 对象,导致转换器尝试将 Result 强制转为 String 时抛出类型转换异常。

代码优化

java 复制代码
package com.hbu.book.config;

import com.hbu.book.model.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import tools.jackson.databind.ObjectMapper;

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Autowired
    private ObjectMapper objectMapper;
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public  Object beforeBodyWrite( Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if(body instanceof Result){
            return body;
        }
        if(body instanceof String){
            return objectMapper.writeValueAsString(Result.success(body));
        }
        return Result.success(body);
    }
}

优点

1.方便前端程序员更好的接收和解析后端数据接口返回的数据

  1. 降低前端程序员和后端程序员的沟通成本,按照某个格式实现就可以了,因为所有接口都是这样返回的.

  2. 有利于项目统一数据的维护和修改.

  3. 有利于后端技术部门的统一规范的标准制定,不会出现稀奇古怪的返回内容.

统一异常处理

统一异常处理使用的是 @ControllerAdvice + @ExceptionHandler 来实现的,@ControllerAdvice 表示控制器通知类, @ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件。

java 复制代码
package com.hbu.book.config;

import com.hbu.book.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
@ResponseBody
@Slf4j
public class ExceptionAdvice {
    
    @ExceptionHandler
    public Object handler(Exception e){
        log.error("出现异常:",e);
        return Result.fail(e.getMessage());
    }
}

测试一下:

java 复制代码
package com.hbu.book.controller;

import org.apache.ibatis.jdbc.Null;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/test")
@RestController
public class TestController {
    @RequestMapping("/t1")
    public String t1(){
        Integer x = 7/0;
        return "t1";
    }
    @RequestMapping("/t2")
    public boolean t2(){
        String a = null;
        a.contains("a");
        return true;
    }
    @RequestMapping("/t3")
    public Integer t3(){
        int[] arr = new int[10];
        System.out.println(arr[100]);
        return 200;
    }
}




对于不同的异常,我们其实可以设置不同的方法:

java 复制代码
package com.hbu.book.config;

import com.hbu.book.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
@ResponseBody
@Slf4j
public class ExceptionAdvice {

    @ExceptionHandler
    public Object handler(Exception e){
        log.error("出现异常:",e);
        return Result.fail(e.getMessage());
    }
    @ExceptionHandler
    public Object handler(NullPointerException e){
        log.error("出现空指针异常:",e);
        return Result.fail(e.getMessage());
    }
    @ExceptionHandler
    public Object handler(ArithmeticException e){
        log.error("出现除0异常:",e);
        return Result.fail(e.getMessage());
    }
    @ExceptionHandler
    public Object handler(ArrayIndexOutOfBoundsException e){
        log.error("出现数组越界异常:",e);
        return Result.fail(e.getMessage());
    }
}

或者也可以:

java 复制代码
package com.hbu.book.config;

import com.hbu.book.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
@ResponseBody
@Slf4j
public class ExceptionAdvice {

    @ExceptionHandler
    public Object handler(Exception e){
        log.error("出现异常:",e);
        return Result.fail(e.getMessage());
    }
//    @ExceptionHandler
//    public Object handler(NullPointerException e){
//        log.error("出现空指针异常:",e);
//        return Result.fail(e.getMessage());
//    }
//    @ExceptionHandler
//    public Object handler(ArithmeticException e){
//        log.error("出现除0异常:",e);
//        return Result.fail(e.getMessage());
//    }
//    @ExceptionHandler
//    public Object handler(ArrayIndexOutOfBoundsException e){
//        log.error("出现数组越界异常:",e);
//        return Result.fail(e.getMessage());
//    }

    @ExceptionHandler(NullPointerException.class)
    public Object handler1(Exception e){
        log.error("出现异常:",e);
        return Result.fail(e.getMessage());
    }
    @ExceptionHandler(ArrayIndexOutOfBoundsException.class)
    public Object handler2(Exception e){
        log.error("出现异常:",e);
        return Result.fail(e.getMessage());
    }
}

我们一般不把异常的具体内容返回给前端,因此可以这样处理:


这里我们没有针对t1做特别的异常处理,最后打印出来的是内部异常。

所以说,当没有匹配的异常时,会自动去寻找有没有报的异常的父类的处理方法,然后执行处理方法。

相关推荐
逍遥德2 小时前
编程技能点小记之if-else条件分支合理用法
java·开发语言·代码规范·代码复审·极限编程·代码覆盖率
瞎某某Blinder2 小时前
DFT学习记录[3]:material project api使用方法 mp_api调取与pymatgen保存
java·笔记·python·学习
_周游2 小时前
Java8 API文档搜索引擎_7.项目优化之权重合并
java·开发语言·前端·搜索引擎·intellij-idea
IT19952 小时前
Java文档阅读笔记-AI LangChain4j - Agent Multiple Tools Calling Example
java·笔记·文档阅读
rlpp2 小时前
spring.profiles.active和spring.profiles.include的使用及区别说明
java·后端·spring
Pluchon2 小时前
硅基计划4.0 算法 简单实现B树
java·数据结构·b树·算法·链表
小小张说故事3 小时前
Python图像处理利器:Pillow (PIL)入门指南
后端·python·图像识别
whatever who cares3 小时前
Java Web 架构全组件详解
java·前端·架构
好家伙VCC3 小时前
**标题:发散创新|用Python构建GAN图像生成器:从理论到实战全流程解析**---在深度学习飞速发展的今天,**生成对抗
java·python·深度学习·生成对抗网络