责任链模式实战:同一个框架里的两种拦截器链
本文从一个真实的生产级 Java Web 框架出发,展示责任链模式的两种正交实现:注解驱动的编译期链 和数据库驱动的运行期链。完整代码可直接运行,核心思想可迁移至任何需要拦截器链的业务系统。
文章目录
- 责任链模式实战:同一个框架里的两种拦截器链
-
- 一、场景与目标
- 二、角色与链结构
- 三、拦截器接口与基础实现
- 四、三种拦截器的完整实现
-
- [4.1 事务拦截器](#4.1 事务拦截器)
- [4.2 日志拦截器](#4.2 日志拦截器)
- [4.3 监控拦截器](#4.3 监控拦截器)
- [五、注解驱动拼链------doProxy 完整实现](#五、注解驱动拼链——doProxy 完整实现)
- [六、数据库驱动链------passProcess 实现](#六、数据库驱动链——passProcess 实现)
- 七、测试用例(可运行)
- 八、两种实现对比
- 九、亮点总结
- 十、适用场景
- 结语
一、场景与目标
在框架中,一个业务方法需要三种横切能力:事务管理、调用日志、性能监控。不同方法需要不同组合------有的要事务+日志,有的只要监控。如何在不修改业务代码的前提下,灵活组合这些能力?
答案:责任链模式------运行时动态拼装拦截器链。
本文展示两种实现路径:注解驱动(编译时确定链结构)和数据库驱动(运行时可变链结构)。
二、角色与链结构
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 网关的过滤器链
- 工作流引擎的节点拦截器
- 请求管道(认证→鉴权→限流→业务)
- 插件系统的能力编排
- 中间件的中间层链式处理
结语
责任链模式的设计精髓不是"链上每个节点做什么",而是**"谁决定链的结构"**。注解驱动把决定权交给编译期注解,数据库驱动把决定权交给运行时配置。同一个框架同时用两种方案,不是设计冗余,是问题粒度不同。理解了这一点,责任链模式就从"背定义"变成了"做选择"。