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

    文章

    8447

    浏览量

    150722
  • 开源
    +关注

    关注

    3

    文章

    3215

    浏览量

    42329
  • 源码
    +关注

    关注

    8

    文章

    632

    浏览量

    29110
收藏 人收藏

    评论

    相关推荐

    ElfBoard开源项目|“智慧光伏”开源项目技术文档

    最大化地捕捉和利用。下面就和各位小伙伴详细介绍一下这一开源项目是怎样实现的。环境说明1.开发环境操作系统:Ubuntu18.0464位版2.交叉编译工具:arm-
    的头像 发表于 10-11 15:52 320次阅读
    ElfBoard<b class='flag-5'>开源</b><b class='flag-5'>项目</b>|“智慧光伏”<b class='flag-5'>开源</b><b class='flag-5'>项目</b>技术文档

    Matepad pro12.2 已上市半个月,但是还没有在开源网站看到该项目开源信息,违背开源精神

    Matepad pro12.2 已上市半个月,本人自己也购买了同款12+256的pad,想要同步学习下这款pad的一些体验还不错的功能点,但是目前为止还没有在开源网站看到该项目开源
    发表于 08-27 17:25

    开源项目!基于 Arduino DIY 漂亮的宏机械键盘

    。 接下来,我们将要制作的这款键盘拥有 12 个按键,可以根据自己的喜好和需求,将这些按键与所需的快捷键进行关联。尽管这个项目听起来可能有些复杂,但实际上无论是电子部分还是软件部分,
    发表于 08-19 17:02

    开源项目!用ESP8266 DIY会爬墙的无人机

    有点神奇,作者表示,下面介绍的这个无人机就能做到! 一个基于NodeMCU飞行控制器和安卓应用控制的爬墙项目。完整的细节和解释视频。 相信,会爱上它。 玩无人机需要一个飞行控制器,
    发表于 07-04 09:22

    华为车究竟成没成功,这个责任谁来担?

    华为车,一种很新的车方式。
    的头像 发表于 06-20 11:16 226次阅读

    嵌入式软件工程师如何提升自己

    、分享项目经验等方式,展示自己的专业能力和行业见解,树立自己在行业中的声誉。 6.寻找合适的导师 在职业生涯的发展过程中,有一个经验丰富的导师是非常宝贵的。他们可以给予指导和
    发表于 06-12 11:20

    开源项目】Arduino ESP32 彩色电子气象站

    展示是如何构建这个项目的,以及如何使用相同或相似的显示器来构建自己项目。 首先,让解释一
    发表于 01-16 14:01

    关于FPGA的开源项目介绍

    Hello,大家好,之前给大家分享了大约一百多个关于FPGA的开源项目,涉及PCIe、网络、RISC-V、视频编码等等,这次给大家带来的是不枯燥的娱乐项目,主要偏向老的游戏内核使用FP
    的头像 发表于 01-10 10:54 1312次阅读
    关于FPGA的<b class='flag-5'>开源</b><b class='flag-5'>项目</b>介绍

    五元左右?要的不是雪,而是开源的雪花灯

    佳作,愿也喜欢——用工程师的独有浪漫与神奇魔法,一起点亮不会融化的雪花灯。触摸无极调光雪花灯-开源分享-雪花灯原创开源项目采用SGL8022W触摸芯片+陶瓷灯丝
    的头像 发表于 01-06 08:04 454次阅读
    五元左右?<b class='flag-5'>我</b>要的不是雪,而是<b class='flag-5'>开源</b>的雪花灯

    开源FPGA项目有哪些

    请问开源FPGA项目有哪些?
    发表于 12-26 12:09

    OpenHarmony Meetup 2023南京站亮点抢先看

    点击蓝字 ╳ 关注我们 开源项目 OpenHarmony 是每个人的 OpenHarmony 原文标题:OpenHarmony Meetup 2023南京站亮点抢先看 文章出处:【微信公众号:OpenAtom OpenHarmo
    的头像 发表于 12-25 21:10 545次阅读
    OpenHarmony Meetup 2023南京站亮点抢<b class='flag-5'>先看</b>

    开源项目】基于ESP32制作的小小光立方,可以使用wifi连接到电脑

    ,光立方焊接室东西太多了,有点卡。 光立方AR室,使用光立方的地板进行AR识别,这样就可以实现在现实生活中出现一个虚拟的光立方在你的电脑中。这个虚拟的光立方用法跟上位机一样 4、电脑端喇叭fft
    发表于 12-19 13:51

    【飞腾派4G版免费试用】1.实战交叉编译环境搭建和手把手uboot编译

    因为突然对硬件产生了浓厚的兴趣,而是这款硬件和其背后的技术团队让感受到了技术与使命责任的完美结合。在这个过程中,也产生了一些思考和反思
    发表于 12-06 16:58

    CRA专题|面对法律,开源应负什么安全责任

    对Recital 第10条“开源豁免条款”的修正意见,更是激起了多家开源基金会及社区的担忧甚至是谴责。但直至11月30日,欧盟理事会与欧盟议会就CRA提案达成临时议定,也未就开源界的建议
    的头像 发表于 12-04 21:15 901次阅读
    CRA专题|面对法律,<b class='flag-5'>开源</b>应负什么安全<b class='flag-5'>责任</b>

    还在使用传统架构的DC-DC转换器吗?

    还在使用传统架构的DC-DC转换器吗?
    的头像 发表于 12-04 17:26 798次阅读
    <b class='flag-5'>你</b><b class='flag-5'>还在</b>使用传统架构的DC-DC转换器吗?