Stream异常处理wrapper function与functional interface定义及包结构最佳实践
Stream异常处理组件存放规则
不要非黑即白选“全放业务类”或者“全抽全局公共类”,按组件的复用边界判断即可:
- 无业务属性、纯通用能力的自定义函数式接口、wrapper方法,必须抽离到公共位置统一存放,绝对不要在各个使用Stream的业务类里重复定义。比如支持抛出受检异常的
ThrowingFunction/ThrowingConsumer类、把受检异常统一转成非受检异常的通用包装方法,这类逻辑和具体业务无关,散落在业务类里只会造成大量重复代码,后续调整逻辑时很容易漏改。 - 绑定特定业务规则的异常处理wrapper,不要放到全局公共类里,就近放在所属业务域的公共工具位置即可。比如专门处理订单流中查询异常、打订单域特定错误日志、转成订单自定义业务异常的包装逻辑,本身不具备跨业务域复用的价值,塞到全局公共类只会让通用组件越来越臃肿,职责混乱。
- 禁止把通用wrapper定义成某个业务类的私有方法/内部类,这是重复代码的重灾区。
推荐包结构
按复用粒度分层组织即可,不要过度设计:
- 全局通用的流处理异常组件,统一放在项目根路径下的
common.util.stream包内,再按职责拆分子包:- 自定义的通用函数式接口放在
common.util.stream.function子包下,统一用Throwing作为前缀命名,和JDK原生函数式接口做区分,比如ThrowingFunction、ThrowingBiFunction、ThrowingConsumer、ThrowingSupplier。 - 通用的异常包装静态方法,直接放在
common.util.stream包下的StreamUtils或者StreamExceptionWrapper工具类中,所有方法设为静态,使用时直接静态导入即可,不要做不必要的实例化。
通用wrapper的参考实现如下:
// common.util.stream.function.ThrowingFunction @FunctionalInterface public interface ThrowingFunction<T, R, E extends Exception> { R apply(T arg) throws E; } // common.util.stream.StreamExceptionWrapper public static <T, R> Function<T, R> wrap(ThrowingFunction<T, R, Exception> throwingFunc) { return arg -> { try { return throwingFunc.apply(arg); } catch (Exception e) { // 全局通用逻辑只做最基础的异常转译,不要嵌入业务逻辑 throw new RuntimeException(e); } }; } - 自定义的通用函数式接口放在
- 特定业务域专属的流异常wrapper,放在对应业务域的工具包下即可,比如订单域的相关组件放在
order.util.stream包下,只允许本业务域的代码引用,不要跨域暴露,也不要上浮到全局公共包。
注意:全局通用wrapper只做无业务属性的通用逻辑,不要在里面加特定业务的日志打印、默认值返回、自定义业务异常转译逻辑,这类逻辑要么写在业务域专属的wrapper里,要么在流操作的具体位置单独处理,保持通用组件的职责单一。
内容的提问来源于stack exchange,提问作者sjpark
相关产品推荐
相关产品推荐

