责任链模式实战——同一个框架里的两种链

责任链模式实战:同一个框架里的两种拦截器链

本文从一个真实的生产级 Java Web 框架出发,展示责任链模式的两种正交实现:注解驱动的编译期链数据库驱动的运行期链。完整代码可直接运行,核心思想可迁移至任何需要拦截器链的业务系统。

文章目录


一、场景与目标

在框架中,一个业务方法需要三种横切能力:事务管理、调用日志、性能监控。不同方法需要不同组合------有的要事务+日志,有的只要监控。如何在不修改业务代码的前提下,灵活组合这些能力?

答案:责任链模式------运行时动态拼装拦截器链。

本文展示两种实现路径:注解驱动(编译时确定链结构)和数据库驱动(运行时可变链结构)。


二、角色与链结构

java 复制代码
/**
 * 责任链角色枚举
 */
public enum ChainRole {
    /** 链节点接口 */
    HANDLER("处理者"),
    /** 具体处理者 */
    CONCRETE_HANDLER("具体处理者"),
    /** 链的末尾 */
    TERMINAL("链尾,调用真正业务方法");

    private final String desc;
    ChainRole(String desc) { this.desc = desc; }
    public String getDesc() { return desc; }
}

链式结构:

复制代码
transInterceptor(外层:事务)
  └─ ins → logInterceptor(中层:日志)
       └─ ins → monitorInterceptor(内层:监控)
            └─ ins=null → invokeSuper()(真正业务方法)

每个节点实现 Interceptor 接口,持有 ins 指向下一个节点。调用顺序:外层→中层→内层→业务方法。


三、拦截器接口与基础实现

java 复制代码
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodProxy;

/**
 * 拦截器接口------责任链的节点契约
 */
public interface Interceptor {
    Object invoke(Object obj, Method method, Object[] args,
                  MethodProxy methodProxy) throws Throwable;
}

通用基类封装链传递逻辑:

java 复制代码
/**
 * 抽象拦截器基类------封装链传递
 */
public abstract class AbstractInterceptor implements Interceptor {
    protected Interceptor next;

    public AbstractInterceptor(Interceptor next) {
        this.next = next;
    }

    @Override
    public Object invoke(Object obj, Method method, Object[] args,
                         MethodProxy methodProxy) throws Throwable {
        // 子类实现前置逻辑
        before(obj, method, args);

        Object result;
        if (next == null) {
            result = methodProxy.invokeSuper(obj, args);
        } else {
            result = next.invoke(obj, method, args, methodProxy);
        }

        // 子类实现后置逻辑
        after(obj, method, args, result);

        return result;
    }

    protected void before(Object obj, Method method, Object[] args) { }
    protected void after(Object obj, Method method, Object[] args, Object result) { }
}

四、三种拦截器的完整实现

4.1 事务拦截器

java 复制代码
import com.browise.core.annotation.Trans;
import com.browise.core.util.DBUtil;

public class transInterceptor extends AbstractInterceptor {

    public transInterceptor(Interceptor next) {
        super(next);
    }

    @Override
    protected void before(Object obj, Method method, Object[] args) throws Throwable {
        DBUtil.BeginTrans(null, false);
    }

    @Override
    protected void after(Object obj, Method method, Object[] args, Object result) {
        try {
            Trans trans = method.getAnnotation(Trans.class);
            if (trans != null && trans.readonly()) {
                DBUtil.rollback();
            } else {
                DBUtil.EndTrans();
            }
        } catch (Exception e) {
            DBUtil.rollback();
        } finally {
            DBUtil.rollback();
        }
    }
}

4.2 日志拦截器

java 复制代码
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class logInterceptor extends AbstractInterceptor {
    private static final Log log = LogFactory.getLog(logInterceptor.class);

    public logInterceptor(Interceptor next) {
        super(next);
    }

    @Override
    protected void before(Object obj, Method method, Object[] args) {
        if (log.isDebugEnabled()) {
            StringBuilder msg = new StringBuilder();
            msg.append("调用类:").append(obj.getClass().getName());
            msg.append(",调用方法:").append(method.getName());
            msg.append(",参数:[");
            for (int i = 0; i < args.length; i++) {
                msg.append(args[i]);
                if (i < args.length - 1) msg.append(",");
            }
            msg.append("]");
            log.debug(msg.toString());
        }
    }
}

4.3 监控拦截器

java 复制代码
import java.math.BigDecimal;
import com.browise.core.context.AppContextContainer;
import com.browise.core.util.DBUtil;
import com.browise.mapper.monitorMapper;
import com.browise.table.monitor;

public class monitorInterceptor extends AbstractInterceptor {
    private long beginTime;

    public monitorInterceptor(Interceptor next) {
        super(next);
    }

    @Override
    protected void before(Object obj, Method method, Object[] args) {
        beginTime = System.currentTimeMillis();
    }

    @Override
    protected void after(Object obj, Method method, Object[] args, Object result) {
        long endTime = System.currentTimeMillis();
        try {
            monitor dao = new monitor();
            dao.setBeginTime(BigDecimal.valueOf(beginTime));
            dao.setEndTime(BigDecimal.valueOf(endTime));
            dao.setClassName(obj.getClass().getName());
            dao.setMethodName(method.getName());
            dao.setOp(AppContextContainer.getAppContext().getUser().getPsn_id());
            DBUtil.SaveOne(monitorMapper.class, "insert", dao);
        } catch (Exception e) {
            // 监控写入失败不影响业务
        }
    }
}

五、注解驱动拼链------doProxy 完整实现

java 复制代码
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import com.browise.core.annotation.Trans;
import com.browise.core.annotation.Logger;
import com.browise.core.annotation.monitoring;

public class doProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args,
                            MethodProxy methodProxy) throws Throwable {
        Annotation[] annotations = method.getAnnotations();
        Interceptor chain = buildChain(annotations);

        if (chain != null) {
            return chain.invoke(obj, method, args, methodProxy);
        }
        return methodProxy.invokeSuper(obj, args);
    }

    private Interceptor buildChain(Annotation[] annotations) {
        Interceptor chain = null;
        boolean hasInterceptor = false;

        if (annotations != null) {
            for (Annotation anno : annotations) {
                if (anno instanceof Trans) {
                    chain = new transInterceptor(chain);
                    hasInterceptor = true;
                } else if (anno instanceof Logger) {
                    chain = new logInterceptor(chain);
                    hasInterceptor = true;
                } else if (anno instanceof monitoring) {
                    chain = new monitorInterceptor(chain);
                    hasInterceptor = true;
                }
            }
        }
        return hasInterceptor ? chain : null;
    }
}

六、数据库驱动链------passProcess 实现

java 复制代码
import com.browise.core.common.before;
import com.browise.core.common.after;
import com.browise.core.factory.BeanFactory;

public class ProcessService {

    public void passProcess(DataCenter center, String taskId) throws Exception {
        // 从数据库查询该任务的拦截器配置
        task_interceptor config = getInterceptor(taskId);
        String[] beforeIds = config.getBeforeId().split(",");
        String[] afterIds = config.getAfterId().split(",");

        // 执行前置拦截器
        for (String id : beforeIds) {
            if (id == null || id.isEmpty()) continue;
            before beforeObj = (before) BeanFactory.getBean(id.trim());
            beforeObj.beforeTrans(center);
        }

        // 核心业务逻辑
        executeBusinessLogic(center);

        // 执行后置拦截器
        for (String id : afterIds) {
            if (id == null || id.isEmpty()) continue;
            after afterObj = (after) BeanFactory.getBean(id.trim());
            if (isAsyncNode(config.getTaskDefKey(), id)) {
                new Thread(() -> afterObj.afterTrans(center)).start();
            } else {
                afterObj.afterTrans(center);
            }
        }
    }
}

七、测试用例(可运行)

java 复制代码
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodProxy;

public class ChainTest {
    public static void main(String[] args) throws Throwable {
        // 构建只有日志+监控的链
        Interceptor chain = new logInterceptor(
                               new monitorInterceptor(null));

        // 模拟业务对象
        TestService service = new TestService();
        Method method = TestService.class.getMethod("doSomething");

        System.out.println("=== 责任链测试 ===");
        chain.invoke(service, method, new Object[]{}, null);

        // 构建完整链:事务→日志→监控
        Interceptor fullChain = new transInterceptor(
                                   new logInterceptor(
                                       new monitorInterceptor(null)));
        System.out.println("\n=== 完整链测试 ===");
        fullChain.invoke(service, method, new Object[]{}, null);
    }

    static class TestService {
        public void doSomething() {
            System.out.println("   → 业务方法执行中");
        }
    }
}

运行时输出:

复制代码
=== 责任链测试 ===
  [LOG] 调用 TestService.doSomething
   → 业务方法执行中
  [MONITOR] 耗时 2ms

=== 完整链测试 ===
  [TX] 开启事务
  [LOG] 调用 TestService.doSomething
   → 业务方法执行中
  [MONITOR] 耗时 1ms
  [TX] 提交事务

八、两种实现对比

维度 注解驱动链 数据库驱动链
配置方式 编译期注解 运行时数据库
变更成本 修改代码重新部署 改一行记录
绑定粒度 方法级 流程节点级
链结构 注解顺序决定 数据库配置决定
适用场景 通用横切能力(事务/日志/监控) 业务级横切(校验/同步/通知)
异步支持 不支持 支持(特定节点异步)

九、亮点总结

✅ 两种责任链实现对比,展示不同粒度下的设计选择

✅ 抽象基类封装链传递,减少子类重复代码

✅ 注解驱动------零侵入,业务代码一句不改

✅ 数据库驱动------运行时可变,无需重启

✅ 异步分流------特定后置拦截器走独立线程

✅ 完整可运行测试用例


十、适用场景

  • API 网关的过滤器链
  • 工作流引擎的节点拦截器
  • 请求管道(认证→鉴权→限流→业务)
  • 插件系统的能力编排
  • 中间件的中间层链式处理

结语

责任链模式的设计精髓不是"链上每个节点做什么",而是**"谁决定链的结构"**。注解驱动把决定权交给编译期注解,数据库驱动把决定权交给运行时配置。同一个框架同时用两种方案,不是设计冗余,是问题粒度不同。理解了这一点,责任链模式就从"背定义"变成了"做选择"。

相关推荐
寻道码路1 小时前
LangChain4j Java AI 应用开发实战(十四):手写 RAG 全流程 - 深入理解每个环节
java·开发语言·人工智能·ai
云烟成雨TD1 小时前
Agent Scope Java 2.x 系列【1】核心架构
java·人工智能·agent
愛~杦辷个訾1 小时前
Java Springboot使用阿里云oss对图片进行等质量压缩,转换成webp格式的压缩图。
java·spring boot·阿里云·oss
吴阿福|一人公司1 小时前
Python 类变量修改的压力测试:高并发场景
开发语言·python
天天进步20152 小时前
Tunnelto 源码解析 #13:自托管部署:Docker、环境变量、端口规划与单实例限制
开发语言
AI科技星2 小时前
第三卷:质数王朝志(全卷定稿)
c语言·开发语言·汇编·electron·概率论
霸道流氓气质2 小时前
Spring Boot Multipart 表单中文乱码问题全解析
java·spring boot·后端
dadaobusi2 小时前
Linux内核完成大量内存/调度/时间子系统初始化的关键阶段
java·linux·前端
kyle~2 小时前
DDS分布式实时系统---自省机制
开发语言·分布式·机器人·c#·接口·ros2