3.2.3 Add ExceptionHandler to the execution chain

0 28
Author: Yan Yujie, JD Logistics1. Introduction to pieThe responsibility chain pa...

Author: Yan Yujie, JD Logistics

1. Introduction to pie

The responsibility chain pattern is a commonly used design pattern in the development process, which is implemented in many frameworks such as SpringMVC and Netty. In our daily development, if we want to use the responsibility chain pattern, we usually need to implement it ourselves. However, the responsibility chain implemented temporarily is not general and is also prone to unclear coupling between framework and business code, which increases the cost of Code Review.

The code of Netty has always been known for its elegance. Early on, when I was reading the source code of Netty, I had the idea of applying the implementation of the responsibility chain to business development. After spending some time extracting the implementation code of the responsibility chain from Netty, I formed this project, which is 'pie'. The core code of 'pie' comes from Netty, and the vast majority of the API is consistent with Netty.

'pie' is a responsibility chain framework that can be quickly learned. Developers only need to focus on business and develop corresponding business Handlers to complete the implementation of the business responsibility chain.

Learn in one minute, get started in three minutes, and apply in five minutes, welcome to star.

The source code address of 'pie':https://github.com/feiniaojin/pie.git

The source code address of the 'pie' case project:https://github.com/feiniaojin/pie-example.git

2. Quick Start

2.1 Introduce Maven dependency

The 'pie' has been packaged and released to the Maven Central Repository, and developers can directly introduce it into their projects via Maven coordinates.

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

The latest version is 1.0

2.2 Implement the output parameter factory

The output parameter, also known as the execution result, requires a return value in general execution processes. Implement the OutboundFactory interface to generate the default return value for the interface.

For example:

public class OutFactoryImpl implements OutboundFactory {
    @Override
    public Object newInstance() {
        Result result = new Result();
        result.setCode(0);
        result.setMsg("ok");
        return result;
    }
}

2.3 Implement the handler interface to complete the business logic

In the pie example project ( https://github.com/feiniaojin/pie-example.gitIn the Example1, in order to demonstrate the usage of pie, a virtual business logic was implemented: the CMS project modifies the article title and content. Please do not pay attention to whether the modification operation is reasonable to be placed in two handlers. It is only used as an example for explanation.

The functions of the three Handlers are as follows:

CheckParameterHandler: used for parameter validation.

ArticleModifyTitleHandler: used to modify the article title.

ArticleModifyContentHandler: used to modify the article content.

The code for CheckParameterHandler is as follows:

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("Parameter validation: start execution");

        if (in instanceof ArticleTitleModifyCmd) {
            ArticleTitleModifyCmd cmd = (ArticleTitleModifyCmd) in;
            String articleId = cmd.getArticleId();
            Objects.requireNonNull(articleId, "articleId cannot be null");
            String title = cmd.getTitle();
            Objects.requireNonNull(title, "title cannot be null");
            String content = cmd.getContent();
            Objects.requireNonNull(content, "content cannot be null");
        }
        logger.info("Parameter validation: validation passed, will enter the next Handler");
        ctx.fireChannelProcess(in, out);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,
                                Throwable cause,
                                Object in,
                                Object out) throws Exception {
        logger.error("Parameter validation: exception handling logic", cause);
        Result re = (Result) out;
        re.setCode(400);
        re.setMsg("Parameter exception");
    }
}

The code for ArticleModifyTitleHandler is as follows:

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("Title Modification: Entering the Title Modification Handler");

        ArticleTitleModifyCmd cmd = (ArticleTitleModifyCmd) in;

        String title = cmd.getTitle();
        //Business logic for modifying the title
        logger.info("Title modification, title={}", title);

        logger.info("Title modification: Execution completed, about to enter the next Handler");
        ctx.fireChannelProcess(in, out);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,
                                Throwable cause,
                                Object in,
                                Object out) throws Exception {
        logger.error("Title Modification: Exception Handling Logic");
        Result re = (Result) out;
        re.setCode(1501);
        re.setMsg("An exception occurred while modifying the title");
    }
}

The code for ArticleModifyContentHandler is as follows:

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("Content modification: Entering the content modification Handler");
        ArticleTitleModifyCmd cmd = (ArticleTitleModifyCmd) in;
        logger.info("Content modification, content={}", cmd.getContent());
        logger.info("Content modification: Execution completed, about to enter the next Handler");
        ctx.fireChannelProcess(in, out);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,
                                Throwable cause,
                                Object in,
                                Object out) throws Exception {

        logger.error("Title Modification: Exception Handling Logic");

        Result re = (Result) out;
        re.setCode(1502);
        re.setMsg("An exception occurred while modifying the content");
    }
}

2.4 Assemble and execute through BootStrap

public class ArticleModifyExample1 {

    private final static Logger logger = LoggerFactory.getLogger(ArticleModifyExample1.class);

    public static void main(String[] args) {
        //Construct input parameters
        ArticleTitleModifyCmd dto = new ArticleTitleModifyCmd()
        dto.setArticleId("articleId_001");
        dto.setTitle("articleId_001_title");
        dto.setContent("articleId_001_content");

        // Create the guide class
        BootStrap bootStrap = new BootStrap();

        //Assemble and execute
        Result result = (Result) bootStrap
                .inboundParameter(dto)// input parameter
                .outboundFactory(new ResultFactory())// output parameter factory
                .channel(new ArticleModifyChannel())// custom channel
                .addChannelHandlerAtLast("checkParameter", new CheckParameterHandler())// the first handler
                .addChannelHandlerAtLast("modifyTitle", new ArticleModifyTitleHandler())// the second handler
                .addChannelHandlerAtLast("modifyContent", new ArticleModifyContentHandler())// The third handler
                .process();// execute
        // The variable 'result' represents the execution result
        logger.info("result:code={},msg={}", result.getCode(), result.getMsg());
    }
}

2.5 Execution Result

The following is the log printed by the main method of running ArticleModifyExample1, which shows that the handlers we defined are executed one by one.
1683340054_6455bb16ad7a35b3dfb99.png!small?1683340055421

3. Exception Handling

3.1 Handler Exception Handling

When an exception occurs during the execution of a Handler, we can implement its exception handling logic in the current Handler's exceptionCaught method.

In the pie example project ( https://github.com/feiniaojin/pie-example.gitThe example2 package in the ) directory shows the way to handle exceptions thrown by a Handler.

Assuming that the business logic of ArticleModifyTitleHandler will throw an exception, the instance code is as follows:

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("Title Modification: Entering the Title Modification Handler");
        ArticleTitleModifyCmd cmd = (ArticleTitleModifyCmd) in;
        String title = cmd.getTitle();
        //The exception here is used to simulate the scenario where an exception occurs during execution
        throw new RuntimeException("An exception occurred while modifying the title");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,
                                Throwable cause,
                                Object in,
                                Object out) throws Exception {
        logger.error("Title Modification: Exception Handling Logic");
        Result re = (Result) out;
        re.setCode(1501);
        re.setMsg("An exception occurred while modifying the title");
    }
}

At this time, the channelProcess method of ArticleModifyTitleHandler will definitely throw an exception, and the exception is handled in the exceptionCaught method of the current Handler.

Running the main method of ArticleModifyExample2, the output is as follows:

3.2 Global Exception Handling

Sometimes, we don't want each handler to process the exception one by one, and we hope to handle it uniformly at the end of the execution chain.
In ArticleModifyExample3, we demonstrate the final exception handling through a global exception, which is mainly implemented in the following steps:

3.2.1 Business Handler Passes Exception

If the business Handler implements the ChannelHandler interface, then it is necessary to manually call the ctx.fireExceptionCaught method to pass the exception down.
For example, the following is an example of how CheckParameterHandler handles exceptions:

@Override
public class XXXHandler implements ChannelHandler {}

    //Omit other logic

    //Exception handling
    public void exceptionCaught(ChannelHandlerContext ctx,
                                Throwable cause,
                                Object in,
                                Object out) throws Exception {

        logger.info("Exception handling logic for parameter verification: pass on directly without processing");
        ctx.fireExceptionCaught(cause, in, out);
    }
}

If the business Handler inherits ChannelHandlerAdapter and has not overridden the fireExceptionCaught method, the exception will be passed on by default.

3.2.2 Implement a global exception handling Handler

We put the business exception handling logic in the last Handler for processing, which inherits ChannelHandlerAdapter, and only needs to override the exception handling exceptionCaught
method.
Example code is as follows:

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("Exception handling logic in the exception handler");
        Result re = (Result) out;
        re.setCode(500);
        re.setMsg("System Exception");
    }
}

3.2.3 Add ExceptionHandler to the execution chain

It can be directly added to the end of the execution chain via BootStrap, as shown in the following example code:

public class ArticleModifyExample3 {

    private final static Logger logger = LoggerFactory.getLogger(ArticleModifyExample3.class);

    public static void main(String[] args) {
        //Parameter
        ArticleTitleModifyCmd dto = new ArticleTitleModifyCmd()
        dto.setArticleId("articleId_001");
        dto.setTitle("articleId_001_title");
        dto.setContent("articleId_001_content");
        // Create the guide class
        BootStrap bootStrap = new BootStrap();

        Result result = (Result) bootStrap
                .inboundParameter(dto)// input parameter
                .outboundFactory(new ResultFactory())// output parameter factory
                .channel(new ArticleModifyChannel())// custom channel
                .addChannelHandlerAtLast("checkParameter", new CheckParameterHandler())// the first handler
                .addChannelHandlerAtLast("modifyTitle", new ArticleModifyTitleHandler())// the second handler
                .addChannelHandlerAtLast("modifyContent", new ArticleModifyContentHandler())// The third handler
                .addChannelHandlerAtLast("exception", new ExceptionHandler())// exception handling handler
                .process();// execute
        // The variable 'result' represents the execution result
        logger.info("result:code={},msg={}", result.getCode(), result.getMsg());
    }
}

3.2.4 Running ArticleModifyExample3

Running the main method of ArticleModifyExample3, the console output is as follows, and it can be seen that the exception is passed to the final ExceptionHandler for processing.

你可能想看:
最后修改时间:
admin
上一篇 2025年03月25日 23:10
下一篇 2025年03月25日 23:32

评论已关闭