0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

还在自己实现责任链?我建议你造轮子之前先看看这个开源项目

京东云 来源:jf_75140285 作者:jf_75140285 2024-09-20 14:38 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

1. 前言

设计模式在软件开发中被广泛使用。通过使用设计模式,开发人员可以更加高效地开发出高质量的软件系统,提高代码的可读性、可维护性和可扩展性。

责任链模式是一种常用的行为型设计模式,它将请求沿着处理链进行发送,直到其中一个处理者对请求进行处理为止。在责任链模式中,通常会有多个处理者,每个处理者都有一个处理请求的方法。当一个请求到达处理链的起点时,会依次传递给每个处理者进行处理,直到某个处理者能够处理该请求。这样可以保证每个请求都能被处理,并且可以根据实际情况动态地添加或删除处理者,以满足不同的需求。

责任链模式可以帮助降低系统的耦合度,增加系统的灵活性和可扩展性,其在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 坐标将其引入到项目中。

< dependency >
    < groupId >com.feiniaojin.ddd.ecosystem< /groupId >
    < artifactId >pie< /artifactId >
    < version >1.0< /version >
< /dependency >

目前最新的版本是 1.0

2.2 实现出参工厂

出参也就是执行结果,一般的执行过程都要求有执行结果返回。实现 OutboundFactory 接口,用于产生接口默认返回值。

例如:

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 的代码如下:

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 的代码如下:

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 的代码如下:

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 拼装并执行

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 被逐个执行了。

wKgaombtGFuAFlFjAAMfA3tGG9c190.png

3. 异常处理

3.1 Handler 异常处理

当某个Handler执行发生异常时,我们可将其异常处理逻辑实现在当前 Handler 的 exceptionCaught 方法中。

在 pie 案例工程( https://github.com/feiniaojin/pie-example.git )的 example2 包中,展示了某个 Handler 抛出异常时的处理方式。

假设 ArticleModifyTitleHandler 的业务逻辑会抛出异常,实例代码如下:

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 方法,输出如下:

wKgaombtGGeAdo9GAAHqAaCzzZs111.png

3.2 全局异常处理

有时候,我们不想每个 handler 都处理一遍异常,我们希望在执行链的最后统一进行处理。
在 ArticleModifyExample3 中,我们展示了通过一个全局异常进行最后的异常处理,其实现主要分为以下几步:

3.2.1 业务 Handler 传递异常

如果业务 Handler 实现了 ChannelHandler 接口,那么需要手工调用 ctx.fireExceptionCaught 方法向下传递异常。
例如 CheckParameterHandler 捕获到异常时的示例如下:


@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
方法。
示例代码如下:

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 加入到执行链最后即可,示例代码如下:


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 中进行处理。

wKgZombtGGmAT1MbAAKoayl5eCE437.png

4. 总结

本文通过简单的例子,向读者介绍了如何使用pie框架快速进行责任链模式开发,包括责任链初始化和异常处理等日常开发中常见的场景。读者可以参考这些案例,并将pie框架应用于实际开发中,以快速实现通用的责任链模式,最终降低代码的耦合度、增加代码的可扩展性和提高代码的可读性。

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接口
    +关注

    关注

    33

    文章

    9449

    浏览量

    156153
  • 开源
    +关注

    关注

    3

    文章

    4037

    浏览量

    45578
  • 源码
    +关注

    关注

    8

    文章

    682

    浏览量

    31094
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    【社区之星】欧小龙——保护的好奇心:它是所有创造力的源泉

    的时候会看重些什么呢?应届生和有两、三年左右工作经验人有什么侧重点呢? @ouxiaolong: 从的经历来看,一个项目通常不是按部就班地完成的,需要灵光一闪的瞬间,抓住脑海里蹦出的小火花
    发表于 11-26 16:31

    开源鸿蒙项目达成开源孵化目标顺利毕业

    11月21日,2025开放原子开发者大会在北京隆重启幕。作为本次大会的重磅环节和核心焦点之一,在大会开幕式上,开源鸿蒙项目达成开源孵化目标、顺利毕业,这是项目
    的头像 发表于 11-25 17:36 890次阅读

    仅花1块乐鑫科技ESP32-S3开发板,竟在家实现了鱼缸水质监测自由?!代码开源!启明云端乐鑫代理

    上期我们的智能鱼缸已经成功实现了自动水循环功能,水“活”起来了:偷懒神器?用乐鑫科技ESP32-S3开发板做了个会自己养鱼的智能鱼缸!低成本开源!那本期我们就来完成上期留下的“作业”
    的头像 发表于 11-20 18:02 1562次阅读
    仅花1块乐鑫科技ESP32-S3开发板,<b class='flag-5'>我</b>竟在家<b class='flag-5'>实现</b>了鱼缸水质监测自由?!代码<b class='flag-5'>开源</b>!启明云端乐鑫代理

    了一台‘迷你 Switch’,还能自己写游戏!

    问题来了——能不能用极低的成本,整出一台性能强劲、画质清晰、手感流畅的掌上游戏机?可以很负责任地告诉:能,而且做出来了!今天的主角就是——启明云端ESP32-
    的头像 发表于 08-12 18:05 482次阅读
    <b class='flag-5'>我</b><b class='flag-5'>造</b>了一台‘迷你 Switch’,还能<b class='flag-5'>自己</b>写游戏!

    开放原子开源基金会与新一批开源项目完成捐赠签约

    近日,在2025开放原子开源生态大会开幕式上,开放原子开源基金会与新一批开源项目完成捐赠签约,涵盖人工智能、具身智能、基础软件、区块等多个
    的头像 发表于 07-28 17:04 810次阅读

    2025开放原子开源生态大会亮点抢先看

    2025年,从“开源VS闭源”的激烈交锋到软件供应安全危机,再到企业数字化转型浪潮中开源的挑战,“开源”始终占据C位,开源产业也呈现出一系
    的头像 发表于 07-23 10:01 882次阅读

    这个夏天,用代码定义的硬核实力!RT-Thread开源之夏重磅来袭

    还在为简历项目发愁?想和业界大牛零距离交流?渴望让自己的代码跑在千万级设备上?今年,RT-Thread再次加入开源之夏,带来了四个有趣又有挑战的项目
    的头像 发表于 05-23 16:06 655次阅读
    <b class='flag-5'>这个</b>夏天,用代码定义<b class='flag-5'>你</b>的硬核实力!RT-Thread<b class='flag-5'>开源</b>之夏重磅来袭

    开源力量!树莓派与 AI 联手构建智能城市监控!

    用树莓派规划的早晨通勤。如果打算出门,看看你所在地区的天气和交通报告也无妨。但是如果能获取更具体的数据呢?这就是开发者Glossyio开发这个很酷的RaspberryPi
    的头像 发表于 05-10 08:35 389次阅读
    <b class='flag-5'>开源</b>力量!树莓派与 AI 联手构建智能城市监控!

    在构建自动布线工具之前我会告诉自己的13件事

    在为 tscircuit(一款用TypeScript编写的开源电子CAD内核)开发自动布线工具上耗费了约一年时间。如果能回到一年前,以下是我会告诉自己的13件事: 一个键盘项目自动布
    的头像 发表于 05-08 11:20 1034次阅读
    在构建自动布线工具<b class='flag-5'>之前</b>我会告诉<b class='flag-5'>自己</b>的13件事

    开源项目!Open Echo:一个开源的声纳项目

    “ 这是一个还在迭代中的项目开源的回声测深仪/水深测量仪/声呐系统,适用于水文测绘及科研用途。基于Arduino平台开发并具备良好兼容性 ” Open Echo 概览 作为持续迭代的开源
    发表于 03-20 13:37

    Open Echo:一个开源的声纳项目

    “  这是一个还在迭代中的项目开源的回声测深仪/水深测量仪/声呐系统,适用于水文测绘及科研用途。基于Arduino平台开发并具备良好兼容性  ”   Open Echo 概览 作为持续迭代
    的头像 发表于 03-20 11:14 2034次阅读
    Open Echo:一个<b class='flag-5'>开源</b>的声纳<b class='flag-5'>项目</b>

    开源项目!教你如何制作一个开源教育机械臂

    和适应性强的机器人平台。 作为一个开源项目,构建Pedro所需的所有文件都可以在Pedro Github页面上找到: 用于3D打印和定制的STL文件。 Gerber文件来制造您自己的Pedro板
    发表于 03-10 11:22

    还在以为智能家居是玩?我家是真能用!

    以前听到“智能家居”,总觉得是科技发烧友的玩具,离普通人的生活还很远。直到我自己入手了三翼鸟,才发现智能家居根本不是“玩”,而是实实在在能提升生活品质的利器!今天就从智慧管理和主动服务两个层面
    的头像 发表于 03-06 11:17 775次阅读
    <b class='flag-5'>你</b><b class='flag-5'>还在</b>以为智能家居是玩?我家是真能用!

    ElfBoard开源项目|智能消防车项目

    项目——利用ELF 1开发板打造的智慧消防车。该项目展现了物联网、数据处理等前沿技术在消防领域的应用潜力,下面就和各位小伙伴展示一下这个开源项目
    的头像 发表于 01-04 16:43 1053次阅读
    ElfBoard<b class='flag-5'>开源</b><b class='flag-5'>项目</b>|智能消防车<b class='flag-5'>项目</b>

    开源项目!OpenCat—— 一个全能的平价四足机器人

    的消费市场。可以把它想象成一个有腿的安卓手机或Alexa,带有一个可供第三方扩展的应用商店。它可以以约每秒 2.6 个身体长度的速度持续运行 60分钟,或者坐着播放视频几个小时。还在脊柱下方预留了一些
    发表于 12-16 11:44