当前位置: 首页 > news >正文

Function + 异常策略链:构建可组合的异常封装工具类

在这里插入图片描述

引言:异常到底该谁处理?

在日常 Java 业务开发中,我们常常写出这样的代码:

String result = someRemoteCall();
if (result == null) {throw new RuntimeException("接口返回为空");
}

看起来没问题,但一旦这种模式重复几十次,并在不同的团队成员手中以不同的形式出现(Optionalif-null-throw、try-catch 包装、日志输出混杂),你就会发现:异常的处理并不统一,也很难组合。

有没有办法像处理数据一样“组合”异常策略?答案是:有 —— 结合 Function<T, R> + 策略链思想,我们可以优雅地构建一套“异常装饰器”工具类。


核心思想:Function 的包装链 + 异常策略分离

我们想要实现的目标是:

  • 可以对任何 Function 进行统一的异常封装(比如异常转换、日志打点、返回默认值等)
  • 保持函数式接口的组合能力(支持 .andThen() / .compose()
  • 封装后的 Function 可被透明使用,不需要改业务代码

本质上是一种责任链模式 + 装饰器模式 + 函数式编程的融合。


工具类设计:FunctionChainWrapper

@FunctionalInterface
public interface CheckedFunction<T, R> {R apply(T t) throws Exception;
}

我们定义了一个能抛异常的函数接口,然后封装一个 FunctionWrapper 工具:

public class FunctionChainWrapper<T, R> {private final CheckedFunction<T, R> originalFunction;private final List<Function<Throwable, R>> exceptionHandlers = new ArrayList<>();private Function<Exception, RuntimeException> exceptionTranslator = RuntimeException::new;private Consumer<Throwable> exceptionLogger = e -> {}; // 默认无操作private R fallbackValue = null;private boolean hasFallback = false;public FunctionChainWrapper(CheckedFunction<T, R> originalFunction) {this.originalFunction = originalFunction;}public FunctionChainWrapper<T, R> onException(Function<Throwable, R> handler) {this.exceptionHandlers.add(handler);return this;}public FunctionChainWrapper<T, R> logException(Consumer<Throwable> logger) {this.exceptionLogger = logger;return this;}public FunctionChainWrapper<T, R> translateException(Function<Exception, RuntimeException> translator) {this.exceptionTranslator = translator;return this;}public FunctionChainWrapper<T, R> fallback(R value) {this.fallbackValue = value;this.hasFallback = true;return this;}public Function<T, R> build() {return input -> {try {return originalFunction.apply(input);} catch (Throwable ex) {exceptionLogger.accept(ex);for (Function<Throwable, R> handler : exceptionHandlers) {try {return handler.apply(ex);} catch (Throwable ignore) {}}if (hasFallback) return fallbackValue;if (ex instanceof RuntimeException) throw (RuntimeException) ex;throw exceptionTranslator.apply(new Exception(ex));}};}
}

使用示例:封装远程调用函数

FunctionChainWrapper<String, String> wrapper = new FunctionChainWrapper<>(input -> {if ("bad".equals(input)) throw new IOException("网络失败");return "Result: " + input;
});Function<String, String> safeFunc = wrapper.logException(e -> log.error("调用失败: {}", e.getMessage())).fallback("默认值").build();String val = safeFunc.apply("bad"); // 输出:默认值

可以继续组合使用:

Function<String, String> func = safeFunc.andThen(res -> res.toUpperCase()).andThen(res -> res + " ✅");String result = func.apply("bad"); // "默认值 ✅"

与 BiFunction 的扩展

只需简单改造:

@FunctionalInterface
public interface CheckedBiFunction<T, U, R> {R apply(T t, U u) throws Exception;
}public class BiFunctionChainWrapper<T, U, R> {// 与 FunctionChainWrapper 类似,只是换成 BiFunction
}

你就可以封装两个参数的函数,如数据库查询、组合业务等:

BiFunctionChainWrapper<String, Integer, String> wrapper = new BiFunctionChainWrapper<>((name, age) -> {if (age < 0) throw new IllegalArgumentException("非法年龄");return name + " is " + age + " years old.";}
);BiFunction<String, Integer, String> safeFunc = wrapper.translateException(e -> new BusinessException("年龄不合法", e)).fallback("默认用户").build();

单元测试建议(Function 单测思路)

  1. 输入正常、输出正常
  2. 输入异常(会触发 fallback / log)
  3. 异常链中的处理函数也抛异常(验证链的容错)
  4. 验证 log 是否触发(可 mock logger
  5. 组合测试(andThen)是否正常传递
@Test
void testFallbackTriggered() {FunctionChainWrapper<String, String> wrapper = new FunctionChainWrapper<>(s -> {throw new RuntimeException("fail");}).fallback("safe");Function<String, String> safeFunc = wrapper.build();Assertions.assertEquals("safe", safeFunc.apply("anything"));
}

避坑提示

  • 不要用 try-catch 把所有逻辑包死,应该聚焦于“异常感知点”来封装(I/O、反序列化、远程调用等)
  • 日志与 fallback 分离,不要耦合:打日志是一种副作用,fallback 是行为策略
  • 组合时注意状态性函数(如带缓存)之间的顺序

延伸思考:函数式异常封装的更大价值

  • 能让你的业务逻辑表达更加清晰(只关注 happy path)
  • 异常处理逻辑被拆分并复用(通用 fallback、打点策略可组合)
  • 可以在策略链中插入熔断器、缓存装饰器等 —— 用法无限扩展

总结

Function + 异常策略链 是一个十分实用且优雅的模式。它不仅解决了代码冗余和异常处理不一致的问题,还让我们重新认识了函数式接口的扩展能力。

推荐阅读文章

  • 由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)

  • 如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系

  • HTTP、HTTPS、Cookie 和 Session 之间的关系

  • 什么是 Cookie?简单介绍与使用方法

  • 什么是 Session?如何应用?

  • 使用 Spring 框架构建 MVC 应用程序:初学者教程

  • 有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误

  • 如何理解应用 Java 多线程与并发编程?

  • 把握Java泛型的艺术:协变、逆变与不可变性一网打尽

  • Java Spring 中常用的 @PostConstruct 注解使用总结

  • 如何理解线程安全这个概念?

  • 理解 Java 桥接方法

  • Spring 整合嵌入式 Tomcat 容器

  • Tomcat 如何加载 SpringMVC 组件

  • “在什么情况下类需要实现 Serializable,什么情况下又不需要(一)?”

  • “避免序列化灾难:掌握实现 Serializable 的真相!(二)”

  • 如何自定义一个自己的 Spring Boot Starter 组件(从入门到实践)

  • 解密 Redis:如何通过 IO 多路复用征服高并发挑战!

  • 线程 vs 虚拟线程:深入理解及区别

  • 深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别

  • 10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!

  • “打破重复代码的魔咒:使用 Function 接口在 Java 8 中实现优雅重构!”

  • Java 中消除 If-else 技巧总结

  • 线程池的核心参数配置(仅供参考)

  • 【人工智能】聊聊Transformer,深度学习的一股清流(13)

  • Java 枚举的几个常用技巧,你可以试着用用

  • 由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)

  • 如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系

  • HTTP、HTTPS、Cookie 和 Session 之间的关系

  • 使用 Spring 框架构建 MVC 应用程序:初学者教程

  • 有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误

  • Java Spring 中常用的 @PostConstruct 注解使用总结

  • 线程 vs 虚拟线程:深入理解及区别

  • 深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别

  • 10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!

  • 探索 Lombok 的 @Builder 和 @SuperBuilder:避坑指南(一)

  • 为什么用了 @Builder 反而报错?深入理解 Lombok 的“暗坑”与解决方案(二)

http://www.dtcms.com/a/320747.html

相关文章:

  • 机械学习--SVM 算法
  • 【Leetcode Hot 100 题目精华解析2025】python自用 --128.最长连续序列
  • 腾讯前端面试真题
  • Kafka生产者事务机制原理
  • Java集合中的链表
  • 解耦主库负载,赋能数据流转:MySQL Binlog Server 核心指南
  • Web 图像捕获革命:ImageCapture API 全面解析与实战指南
  • mt6897 scp a+g sh5201 porting记录
  • 数据结构:哈希表、排序和查找
  • 光子精密3D工业相机的应用与优势解析
  • CS231n2017 Assignment3 PyTorch部分
  • 代理模式在C++中的实现及面向对象设计原则的满足
  • 利用哥斯拉(Godzilla)进行文件上传漏洞渗透实战分析
  • ​「解决方案」Linux 无法在 NTFS 硬盘上创建文件/文件夹的问题
  • C++多态与虚函数的原理解析
  • MySQL的触发器:
  • 虹科技术分享 | LIN总线译码功能与LIN控制交流发电机(二)
  • 灌区信息化智能管理系统解决方案
  • 计算机视觉CS231n学习(5)
  • AI开发平台行业全景分析与战略方向建议
  • C++归并排序
  • 使用 Python GUI 工具创建安全的密码短语
  • tmi8150b在VM=3.3v电压下,如何提高转速,记录
  • 高性能 Vue 应用运行时策略
  • 仓颉编程语言的match表达式
  • 《算法导论》第 12 章 - 二叉搜索树
  • 【量子计算】量子计算驱动AI跃迁:2025年算法革命的曙光
  • conda pip uv与pixi
  • SpringCloud(4)-多机部署,负载均衡-LoadBalance
  • ASP.NET三层架构成绩管理系统源码