前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >【SpringBoot】Spring 一站式解决方案:融合统一返回结果、异常处理与适配器模式

【SpringBoot】Spring 一站式解决方案:融合统一返回结果、异常处理与适配器模式

作者头像
用户11288949
发布2025-01-17 14:02:15
发布2025-01-17 14:02:15
14000
代码可运行
举报
文章被收录于专栏:学习学习
运行总次数:0
代码可运行

📚️1.适配器模式

🚀1.1适配器模式定义

适配器模式, 也叫包装器模式. 将⼀个类的接⼝,转换成客⼾期望的另⼀个接⼝, 适配器让原本接⼝不兼容的类可以合作⽆间.

简单来说就是⽬标类不能直接使⽤, 通过⼀个新类进⾏包装⼀下, 适配调⽤⽅使⽤. 把两个不兼容的接⼝通过⼀定的⽅式使之兼容.

⽐如下⾯两个接⼝, 本⾝是不兼容的(参数类型不⼀样, 参数个数不⼀样等等)

但是此时我们可以通过适配器进行两者的兼容,具体的图示如下所示:

🚀1.2适配器模式角色

• Target: ⽬标接⼝ (可以是抽象类或接⼝), 客⼾希望直接⽤的接⼝

• Adaptee: 适配者, 但是与Target不兼容

• Adapter: 适配器类, 此模式的核⼼. 通过继承或者引⽤适配者的对象, 把适配者转为⽬标接⼝

• client: 需要使⽤适配器的对象

大致就是:两个不相容的接口,通过适配器进行了连接,使得使用适配器的对象能够操作目标接口;

🚀1.3适配器模式实现

slf4j 就使⽤了适配器模式,slf4j底层调用了这里的log4j,我们作为调用者,只需要调用slf4j的api就可以了

具体的代码如下所示:

第一步:创建slf4j的api接口

代码语言:javascript
代码运行次数:0
复制
interface Slf4jApi{
 void log(String message);
}

第二步:创建一个log4j,实现打印的功能

代码语言:javascript
代码运行次数:0
复制
class Log4j{
 void log4jLog(String message){
 System.out.println("Log4j打印:"+message);
 }
}

此时可以看到,这两个类并没有关联,那么此时我们就要进行两个接口的连接了

第三步:创建适配器,进行两个接口的连接

代码语言:javascript
代码运行次数:0
复制
class Slf4jLog4JAdapter implements Slf4jApi{
   private Log4j log4j;
   public Slf4jLog4JAdapter(Log4j log4j) {
      this.log4j = log4j;
   }
   @Override
   public void log(String message) {
      log4j.log4jLog(message);
   }
}

此时就是进行方法的重写,这里重写的方法调用我们目标打印的类里的方法,此时就将两个不相关的接口进行连接;

第四步:调用api实现打印

代码语言:javascript
代码运行次数:0
复制
public class Slf4jDemo {
   public static void main(String[] args) {
      Slf4jApi slf4jApi = new Slf4jLog4JAdapter(new Log4j());
         slf4jApi.log("使⽤slf4j打印⽇志");
      }
}

所以可以发现,作为调用者,真正使用的就是适配器帮我们进行操作,不需要改变log4j的api,只需要通过适配器转换下, 就可以更换⽇志框架, 保障系统的平稳运⾏

适配器使用场景:

⼀般来说,适配器模式可以看作⼀种"补偿模式",⽤来补救设计上的缺陷. 应⽤这种模式算是"⽆奈之举", 如果在设计初期,我们就能协调规避接⼝不兼容的问题, 就不需要使⽤适配器模式了

📚️2.统一数据返回格式

🚀2.1快速入门

统⼀的数据返回格式使⽤ @ControllerAdvice 和 ResponseBodyAdvice 的⽅式实现 @ControllerAdvice 表⽰控制器通知类 添加类 ResponseAdvice , 实现 ResponseBodyAdvice 接⼝, 并在类上添加 @ControllerAdvice 注解

代码如下所示:

代码语言:javascript
代码运行次数:0
复制
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {   
    @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) {
        
         return null
    }
}

解释:

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

下面的方法就是表示同意返回的格式是什么,默认重写的就是null;

🚀2.2存在问题
1.作用路径

假如我们需要作用指定的类上,那么代码如下所示:

代码语言:javascript
代码运行次数:0
复制
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
    // 假设你想针对某个具体业务的返回值类型进行处理,这里以 com.example.demo.entity.User 为例
    return returnType.getParameterType().getName().equals("com.example.demo.entity.User");
}

解释:

过获取MethodParameter中的返回值类型的全限定名,并与指定的类全限定名(这里是com.example.demo.entity.User)进行比较,如果相等,就表明当前处理的方法返回值类型正是我们期望操作的那个类,supports方法就返回true

2.异常情况

这里在对于返回类型为string类型的数据会发生,数据不匹配的原因,这里涉及到原码的问题了,出现问题如下所示:

代码语言:javascript
代码运行次数:0
复制
@Controller
@RequestMapping("/test")
public class TestController {
    @RequestMapping("t1")
    public String t1(){
        return "t1";
    }
}

首先我们先创建一个string类,通过运行代码,然后进行同意结果的访问,那么出现的结果如下所示:

主要还是应为原码的问题:

所以此时,我们规定的返回格式不符合这里的string类型,所以发生类型不匹配的问题,那么此时我们就需要进行操作修改;

代码如下:

代码语言:javascript
代码运行次数:0
复制
@Autowired
    private ObjectMapper mapper;

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

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
         //如果返回的值本来就是result类型,那么就不必再次进行包装了
         if(body instanceof String){
              return mapper.writeValueAsString(Result.success(body));
         }
      
         return Result.success(body);
    }

解释:

这里就是判断类型是否是一个string类型,如果是string类型,在包装后我们要进行转化为string类,那么这里的objectmapping的使用是什么呢?

ObjectMapper是 Jackson 库(一个常用的 Java 处理 JSON 数据的库)中的一个核心类,主要用于在 Java 对象和 JSON 格式数据之间进行相互转换,在这里主要用于将对象转换为json格式的字符串;

🚀2.3统一返回格式优点

1. ⽅便前端程序员更好的接收和解析后端数据接⼝返回的数据 2. 降低前端程序员和后端程序员的沟通成本, 按照某个格式实现就可以了, 因为所有接⼝都是这样返回的. 3. 有利于项⽬统⼀数据的维护和修改. 4. 有利于后端技术部⻔的统⼀规范的标准制定, 不会出现稀奇古怪的返回内容

📚️3.统一异常处理

🚀3.1快速入门

统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实现的,@ControllerAdvice 表⽰控制器通知类, @ExceptionHandler 是异常处理器,两个结合表⽰当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件

代码如下所示:

代码语言:javascript
代码运行次数:0
复制
@Slf4j
@ResponseBody
@ControllerAdvice
public class ExceptionAdvice {
    @ExceptionHandler
    public Result handlerException(Exception e){
        log.error("发生不知名异常, e: {}", e);
        return Result.fail("内部错误");
    }

注意:小编这里使用统一返回类型来进行演示的,这里添加了@slf4j来进行错误日志的打印;此时我们自己手动构造一个错误;

代码语言:javascript
代码运行次数:0
复制
@Controller
@RequestMapping("/test")
public class TestController {
    @RequestMapping("t1")
    public String t1(){
        int a=10/0;
        return "t1";
    }
}

很明显这是一种算数异常的出现,那么此时我们进行运行后,在postman中可以看到此时的情况是如何的;

可以看到这里就是出现了内部错误;

🚀3.2多种异常

代码如下:

代码语言:javascript
代码运行次数:0
复制
 @ExceptionHandler
    public Result handlerException(Exception e){
        log.error("发生不知名异常, e: {}", e);
        return Result.fail("内部错误");
    }
    @ExceptionHandler
    public Result handlerException(NullPointerException e){
        log.error("发生空指针异常, e: {}", e);
        return Result.fail("发生空指针异常");
    }
    @ExceptionHandler
    public Result handlerException(ArithmeticException e){
        log.error("发生算数异常, e: {}", e);
        return Result.fail("发生算数异常");
    }

那么此时可以看到,我们列举了不止一种异常,一个父类异常,两个子类异常,那么此时我们再次进行运行,并试一下controller中的算数异常;

可以看到此时得到的就是算数异常;

总结:

在出现一个父类异常时,和出现一个对应的子类异常时,优先就是使用子类异常处理,若没有对应的子类异常,那么就是使用父类的异常处理;这里涉及到原码小编就不再过多赘述了;

🚀3.3统一异常处理优点

1.保障代码质量:确保异常处理方式统一规范,避免因开发人员差异导致的不一致,使代码遵循相同规则,增强可读性与可维护性。 2.强化系统性能:全面捕获各类异常,采取诸如重试、降级等应对策略,有效防止系统因异常而崩溃,维持稳定运行,提升健壮性。 3.助力问题排查:详细记录异常信息,包括类型、发生位置及堆栈详情,方便开发人员依据日志迅速追溯根源,实现高效调试。 4.优化用户感受:向客户端反馈简洁友好的错误消息,屏蔽复杂技术细节,使用户能直观了解问题,提升交互体验。

📚️4.总结

本期接着上回,讲解了关于适配器模式,以及Spring统一功能处理的统一返回格式,以及统一异常处理,当然这里涉及原码,大家可以去看看,翻一翻;

🌅🌅🌅~~~~最后希望与诸君共勉,共同进步!!!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-01-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 📚️1.适配器模式
    • 🚀1.1适配器模式定义
    • 🚀1.2适配器模式角色
    • 🚀1.3适配器模式实现
  • 📚️2.统一数据返回格式
    • 🚀2.1快速入门
    • 🚀2.2存在问题
      • 1.作用路径
      • 2.异常情况
    • 🚀2.3统一返回格式优点
  • 📚️3.统一异常处理
    • 🚀3.1快速入门
    • 🚀3.2多种异常
    • 🚀3.3统一异常处理优点
  • 📚️4.总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档