【java】实际项目中责任链模式的应用

责任链模式

责任链模式的作用和适用场景这里不做详细描述,因为该模式的使用场景比较广泛,且在实际开发中,该模式的使用方式也千差万别,这里是我个人整理的具有通用性的模板代码。

首先需要定义一些基础性的标识和管理类,然后依托于标识类进行业务逻辑的实现,后面我会以审批业务和付款业务为示例进行业务代码的模拟,可能不一定符合真实业务场景,但可以作为参考供后续者使用。

抛开业务意外,如果有不合理的地方,或者各位大佬有更加合理或更加优秀的代码请不吝赐教。
抛开业务意外,如果有不合理的地方,或者各位大佬有更加合理或更加优秀的代码请不吝赐教。
抛开业务意外,如果有不合理的地方,或者各位大佬有更加合理或更加优秀的代码请不吝赐教。

1、首先创建请求参数和处理结果类


  • 请求参数
package com.neo.design.chain;

/**
 * 链式业务请求参数标识类,该标识类用于标识业务请求参数,业务请求参数必须实现该接口<p/>
 * 在整个链式业务管理中心中,请求参数都会以该类为基类进行封装,以便于业务请求参数的传递,也便于代码的解耦<p/>
 * 使用方式:<p/>
 * 1. 实现ChainRequest接口,实现该接口的类推荐同时实现Serializable接口<p/>
 * 2. 添加自己的业务请求参数<p/>
 * 3. 在自己的业务处理流程中,依托于实现的请求参数类进行业务处理<p/>
 * 具体可参考:{@link com.neo.design.chain.approve.ApproveRequest}
 *
 * @author kvein
 */
public interface ChainRequest {
}


  • 处理结果
package com.neo.design.chain;

/**
 * 链式业务处理结果标识类,该标识类用于标识业务处理结果<p/>
 * 在整个链式业务管理中心中,业务处理结果都会以该类为基类进行封装,就算处理结果为空,也推荐实现该接口<p/>
 * 使用方式:<p/>
 * 1. 实现ChainResult接口,实现该接口的类推荐同时实现Serializable接口<p/>
 * 2. 根据业务类型添加需要返回的数据内容<p/>
 * 3. 在自己的业务处理流程中,将处理后的数据封装到该类中,返回给业务处理中心<p/>
 * 具体可参考:{@link com.neo.design.chain.approve.ApproveResult}
 *
 * @author kvein
 */
public interface ChainResult {
}


2、定义业务节点标识

package com.neo.design.chain;

/**
 * 链式业务处理节点标识<p/>
 * 该标识仅用于业务上下文组装的识别,故每个业务推荐都基于此标识类继承一个自己的业务标识基类
 * 比如{@link com.neo.design.chain.approve.ApproveHandler}<p/>
 * 然后根据业务的拆分,依托于基类实现每个链路节点的业务处理,比如:{@link com.neo.design.chain.approve.DirectorApproveHandler}<p>
 * 如果该业务有新的链路节点,则实现自己的业务标识基类,不用更改其他代码,彻底的进行代码解耦
 *
 * @param <T> 业务请求参数 {@link ChainRequest}
 * @param <R> 业务处理结果 {@link ChainResult}
 * @author kevin
 */
public interface ChainHandler<T extends ChainRequest, R extends ChainResult> {

    /**
     * 每一层业务代码执行逻辑
     *
     * @param request 业务请求参数 {@link ChainRequest}
     * @return 处理结果 {@link ChainResult}
     */
    R handleRequest(T request);

    /**
     * 设置下一层业务处理节点
     *
     * @param next 下一层业务处理节点 {@link ChainHandler}
     */
    void setNext(ChainHandler<T, R> next);

}


3、定义业务封装中心标识

package com.neo.design.chain;

/**
 * 业务链封装中心标识<p/>
 * 在项目中如果有业务需要使用责任链进行代码拆分解耦,则首先需要实现该标识类<p/>
 * 例如:在项目中有一个审批流程需要用到责任链模式,则首先需要实现该接口,比如:{@link com.neo.design.chain.approve.ApproveContext},
 * 然后在实现类中进行业务上下文的定义和组装<p/>
 * <p/>
 * 注意:尽量避免长链业务的存在,因为长链业务对性能会产生影响<p/>
 * 注意:尽量避免长链业务的存在,因为长链业务对性能会产生影响<p/>
 * 注意:尽量避免长链业务的存在,因为长链业务对性能会产生影响<p/>
 * 重要的事情说三遍,如果有长链业务(具体限制多少,根据自己项目的情况而定),请优化业务逻辑,或改用其他设计模式
 * <p/>
 * 需要注意的是,每个业务都需要继承实现自己的请求参数类{@link ChainRequest}和返回结果类{@link ChainResult},
 * 在责任链管理中心{@link ChainContextManager}里,根据请求参数的类型会自动进行业务处理中心的匹配{@link ChainContext#match(ChainRequest)}
 * 然后进行业务处理
 *
 * @author Kevin
 */
public interface ChainContext<T extends ChainRequest, R extends ChainResult> {

    /**
     * 初始化业务上下文,实现该接口的时候,可以基于此方法进行业务组装,也可以自定义初始化方法<p/>
     * 这里不做强制要求,看自己的编码习惯而定,具体可参考{@link com.neo.design.chain.approve.ApproveContext#init()}
     */
    @SuppressWarnings("unused")
    default void init() {

    }

    /**
     * 处理业务请求
     *
     * @param request 业务请求参数 {@link ChainRequest}
     * @return 处理结果 {@link ChainResult}
     */
    R handleRequest(T request);

    /**
     * 判断当前业务上下文是否可以处理该请求<p/>
     * 每个业务中心都需要实现该方法,实现方式为根据请求参数类型判断是否符合本业务的处理<p/>
     * 比如:{@link com.neo.design.chain.approve.ApproveContext#match(ChainRequest)}
     * <p/>
     * 判断方式: {@code request instanceof ChainRequest}, 这里的ChainRequest每个业务根据自己的业务进行实现
     *
     * @param request 业务请求参数 {@link ChainRequest}
     * @return 业务是否匹配 {@code true}:匹配,{@code false}:不匹配
     */
    boolean match(ChainRequest request);
}


4、定义全局业务管理中心

package com.neo.design.chain;

import com.neo.design.chain.approve.ApproveRequest;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.util.LinkedList;
import java.util.List;

/**
 * 全局业务链管理中心<p/>
 * 系统中定义和注册的业务链,以每个业务链为一个链对象,业务链管理器负责集中进行维护和处理,
 * 并且提供统一的处理入口,处理入口: {@link ChainContextManager#handleRequest(ChainRequest)} 。<p/>
 * 当然,也可以直接通过调用业务链对象进行处理,如: {@link com.neo.design.chain.approve.ApproveContext#handleRequest(ApproveRequest)}
 * <p/>
 * 这里维护的管理中心,可以理解为全局的业务链管理器,通过请求参数的类型{@link ChainRequest}进行业务链的分发,
 * 分发逻辑是通过 {@link ChainContext#match(ChainRequest)} 方法进行匹配,匹配成功后,
 * 通过具体的 {@link ChainContext#handleRequest(ChainRequest)} 实现类方法进行处理,也就是具体的业务链。<p/>
 * <p>
 * 调用方式示例:<pre>
 *  // 注入全局业务链管理中心
 *  private ChainContextManager chainContextManager;
 *
 *  public Result<ChainResult> handle***(ApproveRequest request) {
 *      return Result.ok(chainContextManager.handleRequest(request));
 *  }</pre>
 * <p>
 * <p/>
 * 关于 {@link ApplicationContextAware} ,这里根据自己的习惯进行实现,本人习惯了这种用法
 *
 * @author kevin
 */
@Component
public class ChainContextManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    /**
     * 业务链集合:
     */
    private final List<ChainContext<ChainRequest, ChainResult>> chains = new LinkedList<>();

    /**
     * 初始化业务链集合<p/>
     * <p>
     * <p>
     * {@link PostConstruct} 注解的作用以及用法,这里不做解释,自行百度或B站上找教程<p/>
     * 还有一种初始化方法为:实现{@link org.springframework.beans.factory.SmartInitializingSingleton#afterSingletonsInstantiated()}方法
     */
    @PostConstruct
    @SuppressWarnings("unchecked")
    private void init() {
        applicationContext.getBeansOfType(ChainContext.class).forEach((key, value) -> chains.add(value));
    }

    /**
     * 业务处理请求入口<p/>
     * 当管理中初始化完成后,系统内注册的业务链会注入到当前对象中的业务链集合中,当有请求进来时,
     * 会根据请求参数的类型进行匹配,匹配成功后,会调用对应的业务链进行处理。
     *
     * @param request 业务请求对象
     * @param <T>     具体的业务请求对象类型,必须是{@link ChainRequest}的子类
     * @param <R>     业务处理返回对象,必须是{@link ChainResult}以及其子类的类型
     * @return 处理结果
     */
    @SuppressWarnings("unchecked")
    public <T extends ChainRequest, R extends ChainResult> R handleRequest(T request) {
        Assert.notEmpty(chains, "初始化失败");
        return chains.stream().filter(chain -> chain.match(request))
                .findFirst()
                .map(chain -> (R) chain.handleRequest(request)).orElse(null);
    }

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

4、示例业务代码

4.1、审批业务

4.1.1、业务请求和处理结果

  • 请求参数
package com.neo.design.chain.approve;

import com.neo.design.chain.ChainRequest;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * 审批业务请求参数<p/>
 * 这里只是示例代码,根据具体的业务需求,添加具体的请求参数
 *
 * @see com.neo.design.chain.ChainRequest
 * @author kevin
 *
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ApproveRequest implements ChainRequest, Serializable {

    private Integer status;
}

  • 处理结果
package com.neo.design.chain.approve;

import com.neo.design.chain.ChainResult;
import lombok.*;

import java.io.Serializable;

/**
 * 审批业务处理结果<p/>
 * 这里只是示例代码,根据具体的业务需求,添加具体的返回数据,也可以什么都不返回
 *
 * @see com.neo.design.chain.ChainResult
 * @author kevin
 *
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApproveResult implements ChainResult, Serializable {
    private String message;
}
4.1.2、审批业务标识
package com.neo.design.chain.approve;

import com.neo.design.chain.ChainHandler;

/**
 * 审批流程业务链标识<p/>
 * <p/>
 * 业务流程:<pre>
 *  1. 先由主管审批
 *  2. 主管审批完成后由经理审批
 *  3. 经理审批完成后由老板审批</pre>
 *
 * 这里只是示例代码,不一定符合真实的业务逻辑,实际业务逻辑请自行实现
 *
 * @see com.neo.design.chain.ChainHandler
 * @author kevin
 */
public interface ApproveHandler extends ChainHandler<ApproveRequest, ApproveResult> {
}
4.1.3、审批业务链中心
package com.neo.design.chain.approve;

import com.neo.design.chain.ChainContext;
import com.neo.design.chain.ChainRequest;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.util.LinkedList;
import java.util.List;

/**
 * 审批流程业务链中心<p/>
 * <p>
 * 当定义完成业务链后,由该类进行业务链的初始化和组装<p/>
 * 这里只是示例代码,但模板代码基本通用,参照:{@link com.neo.design.chain.payment.PaymentContext},
 * 比较而言,业务链中心的初始化和组装逻辑基本一致
 * <p/>
 * 需要注意的是:如果在项目中需要使用到多个业务链,可以使用注解{@link Order}来定义业务链的顺序,调用频繁或者业务比较重要的可以进行优先加载。
 *
 * @author kevin
 * @see com.neo.design.chain.ChainContext
 */
@Order(1)
@Component
@Slf4j
public class ApproveContext implements ChainContext<ApproveRequest, ApproveResult> {

    /**
     * 业务链节点处理器集合
     */
    @Resource
    private List<ApproveHandler> handlers;

    /**
     * 业务链处理节点入口,可以使用@Order注解来定义业务链的顺序,数字越小,优先执行
     */
    private ApproveHandler handler;

    /**
     * 审批链初始化,这里需要注意的是,不要形成环链了。
     *
     * @see com.neo.design.chain.ChainContext#init()
     */
    @Override
    @PostConstruct
    public void init() {
        LinkedList<ApproveHandler> contexts = new LinkedList<>();
        handlers.forEach(hd -> {
            if (!contexts.isEmpty()) {
                contexts.getLast().setNext(hd);
            }
            contexts.add(hd);
            // 可以查看顺序链是否正确
            log.info("初始化审批业务链:{}", hd.getClass().getSimpleName());
        });
        handler = contexts.getFirst();
    }

    /**
     * 处理审批业务
     *
     * @param request 审批业务请求参数
     * @return 审批业务处理结果
     * @see com.neo.design.chain.ChainContext#handleRequest(ChainRequest)
     */
    @Override
    public ApproveResult handleRequest(ApproveRequest request) {
        Assert.notNull(handler, "容器初始化失败");
        return handler.handleRequest(request);
    }

    /**
     * 判断是否是审批业务
     *
     * @param request 业务请求参数 {@link ChainRequest}
     * @return true:是审批业务,false:不是审批业务
     * @see com.neo.design.chain.ChainContext#match(ChainRequest)
     */
    @Override
    public boolean match(ChainRequest request) {
        return request instanceof ApproveRequest;
    }
}
4.1.4、审批业务处理器

  • 主管审批
package com.neo.design.chain.approve;

import com.neo.design.chain.ChainHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

/**
 * 主管审批处理
 *
 * @see com.neo.design.chain.approve.ApproveHandler
 * @author kevin
 */
@Order(1)
@Component
@Slf4j
public class DirectorApproveHandler implements ApproveHandler {

    /**
     * 下一处理节点,可以为空,表示该节点为审批流程的结束节点
     */
    private ChainHandler<ApproveRequest, ApproveResult> next;

    @Override
    public ApproveResult handleRequest(ApproveRequest request) {
        if (request.getStatus() == 1) {
            return ApproveResult.builder().message("主管审批").build();
        }
        log.info("主管已经审批过了,交由下一节点审批");
        Assert.notNull(next, "审批流程已结束,或不支持该操作。");
        return next.handleRequest(request);
    }

    @Override
    public void setNext(ChainHandler<ApproveRequest, ApproveResult> next) {
        this.next = next;
    }
}

  • 经理审批
package com.neo.design.chain.approve;

import com.neo.design.chain.ChainHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

/**
 * 经理审批处理<p/>
 *
 * 需要注意的是,使用{@code Order}注解的作用是定义该处理器的执行顺序,而{@code DependsOn}注解的作用
 * 是校验处理器是否完整,比如如果代码中需要使用的本处理器,但本处理器需要依赖于{@code DependsOn}标注的处理器,真实情况可以删除该注解,
 * 这里是为了安心,避免代码写漏了
 *
 * @author kevin
 * @see com.neo.design.chain.approve.ApproveHandler
 */
@Order(2)
@Component
@Slf4j
@DependsOn("directorApproveHandler")
public class ManagerApproveHandler implements ApproveHandler {
    private ChainHandler<ApproveRequest, ApproveResult> next;

    @Override
    public ApproveResult handleRequest(ApproveRequest request) {
        if (request.getStatus() == 2) {
            return ApproveResult.builder().message("经理审批").build();
        }
        log.info("经理已经审批过了,交由下一节点审批");
        Assert.notNull(next, "审批流程已结束,或不支持该操作。");
        return next.handleRequest(request);
    }

    @Override
    public void setNext(ChainHandler<ApproveRequest, ApproveResult> next) {
        this.next = next;
    }
}

  • 老板审批
package com.neo.design.chain.approve;

import com.neo.design.chain.ChainHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

/**
 * 老板审批处理
 *
 * @see com.neo.design.chain.approve.ApproveHandler
 * @author Kevin
 */
@Order(3)
@Component
@Slf4j
@DependsOn("managerApproveHandler")
public class BossApproveHandler implements ApproveHandler {

    private ChainHandler<ApproveRequest, ApproveResult> next;

    @Override
    public ApproveResult handleRequest(ApproveRequest request) {
        if (request.getStatus() == 3) {
            return ApproveResult.builder().message("老板审批").build();
        }
        log.info("老板已经审批过了,交由下一节点审批");
        Assert.notNull(next, "审批流程已结束,或不支持该操作。");
        return next.handleRequest(request);
    }

    @Override
    public void setNext(ChainHandler<ApproveRequest, ApproveResult> next) {
        this.next = next;
    }
}

5、接口返回结果封装

package com.neo.web.beans;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serial;
import java.io.Serializable;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    private int code;

    private String message;

    private T data;

    public static <T> Result<T> ok(T data) {
        ResultBuilder<T> builder = Result.builder();
        builder.code(200).message("成功");
        if (data != null) {
            builder.data(data);
        }
        return builder.build();
    }

    public static <T> Result<T> okWithoutData() {
        return ok(null);
    }
}

6、接口定义

package com.neo.design.chain;

import com.neo.design.chain.approve.ApproveRequest;
import com.neo.design.chain.payment.PaymentRequest;
import com.neo.web.beans.Result;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ChainController {

    @Resource
    private ChainContextManager chainContextManager;

    @PostMapping("/chain/approve")
    public Result<ChainResult> handleApprove(ApproveRequest request) {
        ChainResult chainResult = chainContextManager.handleRequest(request);
        return Result.ok(chainResult);
    }
}

7、接口测试

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

8、代码地址

https://gitee.com/leehouzhu/java-legacy-code

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值