首页 > 其他分享 >还在自己实现责任链?我建议你造轮子之前先看看这个开源项目

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

时间:2024-09-20 15:02:27浏览次数:1  
标签:你造 Object 开源 Handler 轮子 logger 异常 public out

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

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

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

4. 总结

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

标签:你造,Object,开源,Handler,轮子,logger,异常,public,out
From: https://www.cnblogs.com/Jcloud/p/18422538

相关文章

  • MyFlash使用示例——美团点评的开源MySQL闪回工具安装及使用示例
    下载及安装我已经将二进制安装包上传至网盘,点击如下链接进行下载——链接:https://pan.baidu.com/s/1RzuHv6nDiAVMP7yk03bdNg?pwd=jtua提取码:jtua将如下两个二进制文件拷贝至Linux系统PATH路径下将mysqlbinlong20160408重命名为mysqlbinlog1.Howtousecdbinary......
  • .NET 开源高性能 MQTT 类库
    目录前言项目介绍功能说明客户端功能服务器功能功能特点应用场景使用方法1、安装MQTTnet2、服务端代码3、客户端代码项目地址总结最后前言随着物联网(IoT)技术的迅猛发展,MQTT(消息队列遥测传输)协议凭借其轻量级和高效性,已成为众多物联网应用的首选通信标准。......
  • 阿里史上最大规模开源发布,超GPT-4o 、Llama-3.1!
    今天凌晨,阿里巴巴官宣了史上最大规模的开源发布,推出了基础模型Qwen2.5、专用于编码Qwen2.5-Coder和数学的Qwen2.5-Math。这三大类模型一共有10多个版本,包括0.5B、1.5B、3B、7B、14B、32B和72B,适用于个人、企业以及移动端、PC等不同人群不同业务场景的模型。如果不想进行繁琐......
  • day4[大模型全链路开源开放体系学习小结]
    书生·浦语大模型全链路开源开放体系涵盖数据收集、标注、训练、微调、评测、部署等全链路,学习了解其技术发展、性能提升、模型架构、开源生态等。书生·浦语大模型(英文名INTurnLLM)多次开源,性能不断提升,达到国际先进水平,在推理能力、上下文记忆、自主规划等方面表现优异,可应用......
  • 分享3款开源、免费的Avalonia UI控件库
    Avalonia介绍Avalonia是一个强大的框架,使开发人员能够使用.NET创建跨平台应用程序。它使用自己的渲染引擎绘制UI控件,确保在Windows、macOS、Linux、Android、iOS和WebAssembly等不同平台上具有一致的外观和行为。这意味着开发人员可以共享他们的UI代码,并在不同的目标平台上保持统......
  • 基于51单片机开发多功能菜单系统项目介绍(开源)
    51单片机多功能菜单系统一、项目介绍基于51单片机开发的多功能菜单系统是一种集成多种功能的嵌入式系统,广泛应用于电子产品中,该系统的核心是AT89C52芯片,其强大的处理能力和丰富的外设接口使其成为许多项目的理想选择。这是一个有趣且实用的项目,可以帮助用户实现多种功能,我......
  • FastReport.OpenSource .Net下开源免费报表打印组件
    解决了这个问题:《winForm下,fastReport.net从.netframework升级到.net5遇到的错误“Operationisnotsupportedonthisplatform.”》本文内容转载自:https://www.fcnsoft.com/Home/ShowArticleNews/473最近被fastreport.net搞得有点烦躁,网上有很多破解版本下载可以下载使用,......
  • TICUP_ALL 开源项目教程
    TICUP_ALL开源项目教程引言在当今的软件开发领域,开源项目已经成为推动技术进步和创新的重要力量。TICUP_ALL是一个新兴的开源项目,旨在为开发者提供一个全面的工具包,帮助他们更高效地构建和管理复杂的软件系统。本文将详细介绍TICUP_ALL开源项目的背景、功能、安装步骤、使用方......
  • whiz学习资料汇总 - 终端命令行助手工具Whiz安装使用方法隐私贡献Github开源项目
    whiz学习资料汇总-终端命令行助手工具whiz是一个基于人工智能的终端命令行助手工具,可以帮助用户更高效地使用命令行。本文汇总了whiz的相关学习资料,帮助读者快速上手和深入了解这个强大的工具。1.安装方法可以通过npm安装whiz:npminstall-gwhiz_cli安装完成后,......
  • 供方软件供应链安全保障要求及开源场景对照自评表(下)
    软件供应链安全管理是保护软件开发和交付过程中所有组件的安全性和完整性的重要环节,软件供应链安全国家标准及政策的发布,为企业软件供应链安全管理提供了依据。本文摘选自软件供应链安全推进工作组指导、苏州棱镜七彩信息科技有限公司主笔的《2023软件供应链安全研究报告》中第八章......