您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
还在自己实现责任链?我建议你造轮子之前先看看这个开源项目
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
还在自己实现责任链?我建议你造轮子之前先看看这个开源项目
教室****
2023-05-04
IP归属:北京
19840浏览
# 1. pie 简介 责任链模式是开发过程中常用的一种设计模式,在SpringMVC、Netty等许多框架中均有实现。我们日常的开发中如果要使用责任链模式,通常需要自己来实现,但自己临时实现的责任链既不通用,也很容易产生框架与业务代码耦合不清的问题,增加Code Review 的成本。 Netty的代码向来以优雅著称,早年我在阅读Netty的源码时,萌生出将其责任链的实现应用到业务开发中的想法,之后花了点时间将Netty中责任链的实现代码抽取出来,形成了本项目,也就是pie。pie的核心代码均来自Netty,绝大部分的 API 与 Netty 是一致的。 pie 是一个可快速上手的责任链框架,开发者只需要专注业务,开发相应的业务Handler,即可完成业务的责任链落地。 一分钟学会、三分钟上手、五分钟应用,欢迎 star。 pie 源码地址:https://github.com/feiniaojin/pie.git pie 案例工程源码地址:https://github.com/feiniaojin/pie-example.git # 2. 快速入门 ## 2.1 引入 maven 依赖 pie 目前已打包发布到 maven 中央仓库,开发者可以直接通过 maven 坐标将其引入到项目中。 ```xml <dependency> <groupId>com.feiniaojin.ddd.ecosystem</groupId> <artifactId>pie</artifactId> <version>1.0</version> </dependency> ``` 目前最新的版本是 1.0 ## 2.2 实现出参工厂 出参也就是执行结果,一般的执行过程都要求有执行结果返回。实现 OutboundFactory 接口,用于产生接口默认返回值。 例如: ```java public class OutFactoryImpl implements OutboundFactory { @Override public Object newInstance() { Result result = new Result(); result.setCode(0); result.setMsg("ok"); return result; } } ``` ## 2.3 实现 handler 接口完成业务逻辑 在 pie 案例工程( https://github.com/feiniaojin/pie-example.git )的 Example1 中,为了展示 pie 的使用方法,实现了一个虚拟的业务逻辑:CMS类项目修改文章标题、正文,大家不要关注修改操作放到两个 handler 中是否合理,仅作为讲解案例。 三个 Handler 功能如下: CheckParameterHandler:用于参数校验。 ArticleModifyTitleHandler:用于修改文章的标题。 ArticleModifyContentHandler:用于修改文章的正文。 CheckParameterHandler 的代码如下: ```java public class CheckParameterHandler implements ChannelHandler { private Logger logger = LoggerFactory.getLogger(CheckParameterHandler.class); @Override public void channelProcess(ChannelHandlerContext ctx, Object in, Object out) throws Exception { logger.info("参数校验:开始执行"); if (in instanceof ArticleTitleModifyCmd) { ArticleTitleModifyCmd cmd = (ArticleTitleModifyCmd) in; String articleId = cmd.getArticleId(); Objects.requireNonNull(articleId, "articleId不能为空"); String title = cmd.getTitle(); Objects.requireNonNull(title, "title不能为空"); String content = cmd.getContent(); Objects.requireNonNull(content, "content不能为空"); } logger.info("参数校验:校验通过,即将进入下一个Handler"); ctx.fireChannelProcess(in, out); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause, Object in, Object out) throws Exception { logger.error("参数校验:异常处理逻辑", cause); Result re = (Result) out; re.setCode(400); re.setMsg("参数异常"); } } ``` ArticleModifyTitleHandler 的代码如下: ```java public class ArticleModifyTitleHandler implements ChannelHandler { private Logger logger = LoggerFactory.getLogger(ArticleModifyTitleHandler.class); @Override public void channelProcess(ChannelHandlerContext ctx, Object in, Object out) throws Exception { logger.info("修改标题:进入修改标题的Handler"); ArticleTitleModifyCmd cmd = (ArticleTitleModifyCmd) in; String title = cmd.getTitle(); //修改标题的业务逻辑 logger.info("修改标题:title={}", title); logger.info("修改标题:执行完成,即将进入下一个Handler"); ctx.fireChannelProcess(in, out); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause, Object in, Object out) throws Exception { logger.error("修改标题:异常处理逻辑"); Result re = (Result) out; re.setCode(1501); re.setMsg("修改标题发生异常"); } } ``` ArticleModifyContentHandler 的代码如下: ```java public class ArticleModifyContentHandler implements ChannelHandler { private Logger logger = LoggerFactory.getLogger(ArticleModifyContentHandler.class); @Override public void channelProcess(ChannelHandlerContext ctx, Object in, Object out) throws Exception { logger.info("修改正文:进入修改正文的Handler"); ArticleTitleModifyCmd cmd = (ArticleTitleModifyCmd) in; logger.info("修改正文,content={}", cmd.getContent()); logger.info("修改正文:执行完成,即将进入下一个Handler"); ctx.fireChannelProcess(in, out); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause, Object in, Object out) throws Exception { logger.error("修改标题:异常处理逻辑"); Result re = (Result) out; re.setCode(1502); re.setMsg("修改正文发生异常"); } } ``` 2.4 通过 BootStrap 拼装并执行 ```java public class ArticleModifyExample1 { private final static Logger logger = LoggerFactory.getLogger(ArticleModifyExample1.class); public static void main(String[] args) { //构造入参 ArticleTitleModifyCmd dto = new ArticleTitleModifyCmd(); dto.setArticleId("articleId_001"); dto.setTitle("articleId_001_title"); dto.setContent("articleId_001_content"); //创建引导类 BootStrap bootStrap = new BootStrap(); //拼装并执行 Result result = (Result) bootStrap .inboundParameter(dto)//入参 .outboundFactory(new ResultFactory())//出参工厂 .channel(new ArticleModifyChannel())//自定义channel .addChannelHandlerAtLast("checkParameter", new CheckParameterHandler())//第一个handler .addChannelHandlerAtLast("modifyTitle", new ArticleModifyTitleHandler())//第二个handler .addChannelHandlerAtLast("modifyContent", new ArticleModifyContentHandler())//第三个handler .process();//执行 //result为执行结果 logger.info("result:code={},msg={}", result.getCode(), result.getMsg()); } } ``` ## 2.5 执行结果 以下是运行 ArticleModifyExample1 的 main 方法打出的日志,可以看到我们定义的 handler 被逐个执行了。 ![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2023-04-27-14-350AIvwsjYggLl66D.png) # 3. 异常处理 ## 3.1 Handler 异常处理 当某个Handler执行发生异常时,我们可将其异常处理逻辑实现在当前 Handler 的 exceptionCaught 方法中。 在 pie 案例工程( https://github.com/feiniaojin/pie-example.git )的 example2 包中,展示了某个 Handler 抛出异常时的处理方式。 假设 ArticleModifyTitleHandler 的业务逻辑会抛出异常,实例代码如下: ```java public class ArticleModifyTitleHandler implements ChannelHandler { private Logger logger = LoggerFactory.getLogger(ArticleModifyTitleHandler.class); @Override public void channelProcess(ChannelHandlerContext ctx, Object in, Object out) throws Exception { logger.info("修改标题:进入修改标题的Handler"); ArticleTitleModifyCmd cmd = (ArticleTitleModifyCmd) in; String title = cmd.getTitle(); //此处的异常用于模拟执行过程中出现异常的场景 throw new RuntimeException("修改title发生异常"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause, Object in, Object out) throws Exception { logger.error("修改标题:异常处理逻辑"); Result re = (Result) out; re.setCode(1501); re.setMsg("修改标题发生异常"); } } ``` 此时 ArticleModifyTitleHandler 的 channelProcess 方法一定会抛出异常, 在当前 Handler 的 exceptionCaught 方法中对异常进行了处理。 运行 ArticleModifyExample2 的 main 方法,输出如下: ![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2023-04-27-14-366Tpi27GQqYHeGU8L.png) ## 3.2 全局异常处理 有时候,我们不想每个 handler 都处理一遍异常,我们希望在执行链的最后统一进行处理。 在 ArticleModifyExample3 中,我们展示了通过一个全局异常进行最后的异常处理,其实现主要分为以下几步: ### 3.2.1 业务 Handler 传递异常 如果业务 Handler 实现了 ChannelHandler 接口,那么需要手工调用 ctx.fireExceptionCaught 方法向下传递异常。 例如 CheckParameterHandler 捕获到异常时的示例如下: ```java @Override public class XXXHandler implements ChannelHandler { //省略其他逻辑 //异常处理 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause, Object in, Object out) throws Exception { logger.info("参数校验的异常处理逻辑:不处理直接向后传递"); ctx.fireExceptionCaught(cause, in, out); } } ``` 如果业务 Handler 继承了 ChannelHandlerAdapter,如果没有重写 fireExceptionCaught 方法,则默认将异常向后传递。 ### 3.2.2 实现全局异常处理的 Handler 我们把业务异常处理逻辑放到最后的 Handler 中进行处理,该 Handler 继承了ChannelHandlerAdapter,只需要重写异常处理的exceptionCaught 方法。 示例代码如下: ```java public class ExceptionHandler extends ChannelHandlerAdapter { private Logger logger = LoggerFactory.getLogger(ExceptionHandler.class); @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause, Object in, Object out) throws Exception { logger.error("异常处理器中的异常处理逻辑"); Result re = (Result) out; re.setCode(500); re.setMsg("系统异常"); } } ``` ### 3.2.3 将 ExceptionHandler 加入到执行链中 直接通过 BootStrap 加入到执行链最后即可,示例代码如下: ```java public class ArticleModifyExample3 { private final static Logger logger = LoggerFactory.getLogger(ArticleModifyExample3.class); public static void main(String[] args) { //入参 ArticleTitleModifyCmd dto = new ArticleTitleModifyCmd(); dto.setArticleId("articleId_001"); dto.setTitle("articleId_001_title"); dto.setContent("articleId_001_content"); //创建引导类 BootStrap bootStrap = new BootStrap(); Result result = (Result) bootStrap .inboundParameter(dto)//入参 .outboundFactory(new ResultFactory())//出参工厂 .channel(new ArticleModifyChannel())//自定义channel .addChannelHandlerAtLast("checkParameter", new CheckParameterHandler())//第一个handler .addChannelHandlerAtLast("modifyTitle", new ArticleModifyTitleHandler())//第二个handler .addChannelHandlerAtLast("modifyContent", new ArticleModifyContentHandler())//第三个handler .addChannelHandlerAtLast("exception", new ExceptionHandler())//异常处理handler .process();//执行 //result为执行结果 logger.info("result:code={},msg={}", result.getCode(), result.getMsg()); } } ``` 3.2.4 运行 ArticleModifyExample3 运行 ArticleModifyExample3 的 main 方法,控制台输出如下,可以看到异常被传递到最后的 ExceptionHandler 中进行处理。 ![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2023-04-27-14-38dcbdP38eNGLC4aPD.png)
上一篇:一起单测引起的项目加载失败惨案
下一篇:限流算法及各类常用方案介绍
教室****
文章数
5
阅读量
2434
作者其他文章
01
一站式统一返回值封装、异常处理、异常错误码解决方案—最强的Sping Boot接口优雅响应处理器
1. 简介Graceful Response是一个Spring Boot体系下的优雅响应处理器,提供一站式统一返回值封装、异常处理、异常错误码等功能。使用Graceful Response进行web接口开发不仅可以节省大量的时间,还可以提高代码质量,使代码逻辑更清晰。强烈推荐你花3分钟学会它!Graceful Response的Github地址: https://github.com/feinia
01
还在自己实现责任链?我建议你造轮子之前先看看这个开源项目
1. pie 简介责任链模式是开发过程中常用的一种设计模式,在SpringMVC、Netty等许多框架中均有实现。我们日常的开发中如果要使用责任链模式,通常需要自己来实现,但自己临时实现的责任链既不通用,也很容易产生框架与业务代码耦合不清的问题,增加Code Review 的成本。Netty的代码向来以优雅著称,早年我在阅读Netty的源码时,萌生出将其责任链的实现应用到业务开发中的想法,之后花了
01
手把手教你落地DDD
1. 前言常见的DDD实现架构有很多种,如经典四层架构、六边形(适配器端口)架构、整洁架构(Clean Architecture)、CQRS架构等。架构无优劣高下之分,只要熟练掌握就都是合适的架构。本文不会逐个去讲解这些架构,感兴趣的读者可以自行去了解。本文将带领大家从日常的三层架构出发,精炼推导出我们自己的应用架构,并且将这个应用架构实现为Maven Archetype,最后使用我们Arche
01
手把手教你实战TDD
1. 前言领域驱动设计,测试驱动开发。我们在《手把手教你落地DDD》一文中介绍了领域驱动设计(DDD)的落地实战,本文将对测试驱动开发(TDD)进行探讨,主要内容有:TDD基本理解、TDD常见误区、TDD技术选型,以及案例实战。希望通过本文,读者能够理解掌握TDD并将其应用于实际开发中。2. TDD基本理解测试驱动开发(TDD)是一种软件开发方法,要求开发者在编写代码之前先编写测试用例,然后编写代
教室****
文章数
5
阅读量
2434
作者其他文章
01
一站式统一返回值封装、异常处理、异常错误码解决方案—最强的Sping Boot接口优雅响应处理器
01
手把手教你落地DDD
01
手把手教你实战TDD
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号