责任链模式
百度百科的介绍:
责任链模式是一种设计模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
维基百科的介绍:
责任链模式在面向对象程式设计里是一种软件设计模式,它包含了一些命令对象和一系列的处理对象。每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。该模式还描述了往该处理链的末尾添加新的处理对象的方法。
核心作用
责任链模式的本质是解耦请求与处理,让请求在处理链中能进行传递与被处理;理解责任链模式应当理解其模式,而不是其具体实现。责任链模式的独到之处是将其节点处理者组合成了链式结构,并允许节点自身决定是否进行请求处理或转发,相当于让请求流动起来。
结构
责任链模式主要包含以下角色。
- 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
- 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
- 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
责任链的分类
纯的责任链模式:
- 一个具体处理者对象只能在两个行为中选择一个:要么承担全部责任,要么将责任推给下家,不允许出现某一个具体处理者对象在承担了一部分或全部责任后又将责任向下传递的情况
- 一个请求必须被某一个处理者对象所接收,不能出现某个请求未被任何一个处理者对象处理的情况
不纯的责任链模式:
- 允许某个请求被一个具体处理者部分处理后再向下传递或者一个具体处理者处理完某请求后其后继处理者可以继续处理该请求
- 一个请求可以最终不被任何处理者对象所接收
介绍完毕,下课!啊不不不,正文才开始!
“链”的构建
对于责任链模式的这一条“链”的构建,相关博客文章都有介绍,但大部分都是简单的去进行数组的增加操作。而本文探讨的除了责任链模式本身以外,还将探讨一种异于常规方案的构造“链”的方式。
本实现纯粹是学习 Spring Security 源码的后遗症,对于链节点顺序的配置完全可以通过直接设置顺序索引实现,各位看官不喜勿喷键下留情。造轮子,看源码,两不误。
Spring Security 中 FilterChain 的构造
Spring Security(基于Spring Security 5.4+)中,对于安全校验过滤链的构造,是通过 HttpSecurity 中 addFilterBefore,addFilterAfter 来实现过滤链的构造(省略无关代码):
@Override
public HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) {
this.comparator.registerAfter(filter.getClass(), afterFilter);
return addFilter(filter);
}
@Override
public HttpSecurity addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter) {
this.comparator.registerBefore(filter.getClass(), beforeFilter);
return addFilter(filter);
}
// this.filters 只是单纯的一个 List<Filter> 啦
@Override
public HttpSecurity addFilter(Filter filter) {
// ...
this.filters.add(filter);
return this;
}
其核心就是通过一个 List<Filter> 对象来保存 builder 中添加的过滤器,当在 HttpSecurity#performBuild() 方法被调用时候,将已配置的过滤器按照一定的顺序重新排序组合起来,最终构造成一条符合顺序要求过滤器链。
那问题来了:如何确定过滤器的顺序?
诸君请看这一段:
# addFilterAfter
this.comparator.registerAfter(filter.getClass(), afterFilter);
# addFilterBefore
this.comparator.registerBefore(filter.getClass(), beforeFilter);
里面的 comparator 是何方神圣?
好了,点进去,看到,它就是 FilterComparator,它的定义是这样的:
final class FilterComparator implements Comparator<Filter>, Serializable
Spring Security 团队是这样对它定义的:
An internal use only “Comparator” that sorts the Security “Filter” instances to ensure they are in the correct order.
翻译一下,FilterComparator 就是用来确定每个所需要配置的 Filter 实例在这一条链中的顺序的。
它在 HttpSecurity#performBuild() 中是这样被调用的:
@Override
protected DefaultSecurityFilterChain performBuild() {
this.filters.sort(this.comparator);
return new DefaultSecurityFilterChain(this.requestMatcher, this.filters);
}
通过 this.comparator 对 this.filters 进行排序,这样就能确定过滤器链的顺序,从而进行其他后续操作。
借鉴思想
说到借鉴,首先必须对被借鉴对象有着基本的了解,废话不多说,Show you its code。
FilterComparator 中,通过 registerAfter(Class<T> targer, Class<T> after),registerBefore(Class<T> targer, Class<T> befroe) 来进行设置:
void registerAfter(Class<? extends Filter> filter, Class<? extends Filter> afterFilter) {
Integer position = getOrder(afterFilter);
// ...
put(filter, position + 1);
}
void registerBefore(Class<? extends Filter> filter, Class<? extends Filter> beforeFilter) {
Integer position = getOrder(beforeFilter);
// ...
put(filter, position - 1);
}
// 向缓存对象 filterToOrder 设置顺序
private void put(Class<? extends Filter> filter, int position) {
String className = filter.getName();
this.filterToOrder.put(className, position);
}
/**
* Gets the order of a particular Filter class taking into consideration
* superclasses.
*/
private Integer getOrder(Class<?> clazz) {
while (clazz != null) {
// 当直接获取类型顺序时,不存在该类型的配置,获取父类型的索引,循环操作
Integer result = this.filterToOrder.get(clazz.getName());
if (result != null) {
return result;
}
clazz = clazz.getSuperclass();
}
return null;
}
其核心就是通过 filterToOrder 对象保存过滤器实际类名和过滤器在链中的索引值。
最后 FilterComparator 的核心实现方法(在对待配置过滤器集合进行排序时候调用):
@Override
public int compare(Filter lhs, Filter rhs) {
Integer left = getOrder(lhs.getClass());
Integer right = getOrder(rhs.getClass());
return left - right;
}
// invoking:
this.filters.sort(this.comparator);
通过上述这样那样的方法,Spring Security 实现了对安全过滤器的排序设置,实现了安全过滤相关的功能实现。
实现责任链工具
期望操作
// 通过 Builder 进行链的构建,屏蔽具体的处理器设置逻辑
ChainBuilder<String> builder = ChainManager.builder();
// 通过 addHandler、addHandlerBefore、addHandlerAfter 来设置处理器顺序
builder.addHandler(new A())
.addHandlerBefore(new B(), A.class)
.addHandlerBefore(new C(), A.class);
ChainManager<String> manager = builder.build();
// 处理实际的内容
manager.handle("A");
三个相关测试类的设计:
static class A implements Handler<String> {
@Override
public void handle(String arg, Chain<String> chain) {
System.out.println("A---");
if (!"A".equals(arg)) {
chain.handle(arg);
return;
}
System.out.println(arg);
}
}
static class B implements Handler<String> {
@Override
public void handle(String arg, Chain<String> chain) {
System.out.println("B---");
if (!"B".equals(arg)) {
chain.handle(arg);
return;
}
System.out.println(arg);
}
}
static class C implements Handler<String> {
@Override
public void handle(String arg, Chain<String> chain) {
System.out.println("C---");
if (!"C".equals(arg)) {
chain.handle(arg);
return;
}
System.out.println(arg);
}
}
期望输出
B---
C---
A---
A
说明:
- 构造的链为:B-->C-->A
- 实际调用处理器 A
定义相关的接口:
// Chain.java
public interface Chain<T> {
void handle(T arg);
}
// Handler.java
public interface Handler<T> {
void handle(T arg, Chain<T> chain);
}
说明:
- Chain.java:定义责任链的调用者操作的基本接口,客户端直接操作 Chain 接口实现业务功能
- Handler.java:定义链操作节点的接口,在链中实现直接操作逻辑的基本单位
- 泛型 <T>:对入参进行类型限定
链的容器:
// ChainManager.java
public class ChainManager<T> {
private final Step step = new Step();
private final Chain<T> delegateChain;
// 客户端不能直接创建对象
ChainManager(List<Handler<T>> list) {
this.delegateChain = new DelegateHandlerChain<>(this.step, list);
}
// 提供构建Builder对象
public static <T> ChainBuilder<T> builder() {
return new ChainBuilder<>();
}
// 管理器仅提供处理方法
public void handle(T arg) {
// 调用处理链进行业务的处理
this.delegateChain.handle(arg);
// 执行一轮链处理后,重置计步器
this.step.reset();
}
// 代理 Chain 接口的实现,实际管理链中各个处理器的获取、调用等
private static class DelegateHandlerChain<T> implements Chain<T> {
// 已完成排序的处理器集合
private final List<Handler<T>> handlers;
// 当前链所包含的处理器个数
private final int handlersSize;
// 当前链的节点计步器
private final Step step;
DelegateHandlerChain(Step step, List<Handler<T>> handlers) {
this.step = step;
this.handlers = handlers;
handlersSize = handlers.size();
}
// 调用下一处理器的干货逻辑
// 在具体处理器逻辑中调用: chain.handle(arg);
@Override
public void handle(T arg) {
// 获取当前处理器的索引
int current = this.step.currentStep();
if (current < handlersSize) {
Handler<T> handler = this.handlers.get(current);
// 计步器递进一个单位
this.step.next();
handler.handle(arg, this);
}
}
}
}
// ChainBuilder.java
public class ChainBuilder<T> {
private final OrderComparator<Handler<T>> comparator = new OrderComparator<>();
// 构造器中,待配置的处理器集合
private final List<Handler<T>> handlers = new LinkedList<>();
// 不能通过外部调用创建对象
ChainBuilder() {
}
// 添加处理器
public ChainBuilder<T> addHandler(Handler<T> handler) {
Class<? extends Handler<T>> handlerClass
= (Class<? extends Handler<T>>) handler.getClass();
this.comparator.register(handlerClass);
this.handlers.add(handler);
return this;
}
// 指定构造器类型后添加处理器
public ChainBuilder<T> addHandlerAfter(Handler<T> handler, Class<? extends Handler<T>> afterHandler) {
Class<? extends Handler<T>> handlerClass
= (Class<? extends Handler<T>>) handler.getClass();
this.comparator.registerAfter(handlerClass, afterHandler);
this.handlers.add(handler);
return this;
}
// 指定构造器类型前添加处理器
public ChainBuilder<T> addHandlerBefore(Handler<T> handler, Class<? extends Handler<T>> beforeHandler) {
Class<? extends Handler<T>> handlerClass
= (Class<? extends Handler<T>>) handler.getClass();
this.comparator.registerBefore(handlerClass, beforeHandler);
this.handlers.add(handler);
return this;
}
// 构建 ChainManager 对象
public ChainManager<T> build() {
// 调整顺序
this.handlers.sort(this.comparator);
List<Handler<T>> list = Collections.unmodifiableList(this.handlers);
return new ChainManager<>(list);
}
}
说明:
- Step 计步器为计步对象,为工具类的对象,当前处理器的索引号
- OrderComparator 对象,实现类似 Spring Security 中的 FilterComparator 的功能,在链构建器中使用
测试
测试字符串 A
说明:经过了 B,C,A 处理器,被 A 处理器捕获并进行处理
测试字符串 B
说明:经过了 B 处理器,被 B 处理器捕获并进行处理
测试字符串 C
说明:经过了 B,C 处理器,被 C 处理器捕获并进行处理
测试字符串 D
说明:经过了 B,C,A 处理器,没有合适的处理器被调用
附:OrderComparator、Step 实现
public class OrderComparator<T> implements Comparator<T> {
private final Map<Class<? extends T>, Integer> sortedMap = new LinkedHashMap<>();
public void register(Class<? extends T> type, int index) {
if (!this.sortedMap.isEmpty()) {
throw new IllegalArgumentException("Cannot add object if current sorted context is not empty");
}
this.sortedMap.put(type, index);
}
public void register(Class<? extends T> type) {
register(type, 0);
}
public void registerBefore(Class<? extends T> target, Class<? extends T> before) {
if (this.sortedMap.isEmpty()) {
register(target);
return;
}
Integer index = getIndex(before);
this.sortedMap.put(target, index - 1);
}
public void registerAfter(Class<? extends T> target, Class<? extends T> after) {
if (this.sortedMap.isEmpty()) {
register(target);
return;
}
Integer index = getIndex(after);
this.sortedMap.put(target, index + 1);
}
private Integer getIndex(Class<? extends T> target) {
for (val entry : this.sortedMap.entrySet()) {
if (entry.getKey().equals(target)) {
return entry.getValue();
}
}
throw new IllegalArgumentException("Type[" + target + "] is not in current sorted context");
}
@SuppressWarnings("unchecked")
@Override
public int compare(T o1, T o2) {
Integer left = getIndex((Class<T>) o1.getClass());
Integer right = getIndex((Class<T>) o2.getClass());
return left - right;
}
}
public final class Step {
private final static int DEFAULT_SIZE = 1;
private final static int INITIAL_VALUE = 0;
private final int size;
private int current = INITIAL_VALUE;
public Step(int size) {
this.size = size;
}
public Step(int initial, int size) {
this.current = initial;
this.size = size;
}
public Step() {
this(DEFAULT_SIZE);
}
public void next() {
this.current += this.size;
}
public int currentStep() {
return this.current;
}
public void reset() {
this.current = INITIAL_VALUE;
}
}
总结
应用实例
- Express 框架中的 “洋葱模型”
- Servlet API 规范中的 Filter
- Spring Seccurity 中的拦截器链
- Mybatis 中的 Plugin 机制
理解责任链模式
责任链模式,即是对于一种输入,将一类对该输入有着外观上相似的而实际上各不相同的任务逻辑按照一定的顺序组合起来,成为一条工作流程:每个节点只关心它该关心的入参部分,对入参信息进行处理,完成后要么结束,要么流转到下一节点处理。
真正理解责任链模式,需要在不同的角度分析:
- 在调用方角度,调用方只关心链的构造、处理方法的调用
调用方根据业务需要,设计相应的处理逻辑,并根据需要对这些逻辑顺序进行编排,以及执行数据的处理方法调用
- 在实际处理到的链节点的角度,每个节点都需要知道入参信息、以及下一节点的执行接口。
每个节点对入参进行检查、验证等前置操作,判断该参数是否由自身进行处理。若不为自身所关心的参数,则需要通知链的管理者调用下一节点处理
责任链模式的用途,总结起来大概是一下几方面:
- 拦截非法输入
- 对参数进行过滤、转换、格式化操作
- 实际业务逻辑的前置、后置处理
责任链模式也不是完美的模式,使用不当也会产生很多奇奇怪怪的问题:
- 增加系统的复杂度
原来 if-else 能解决的业务,凭空增加了很多无用代码,在处理的逻辑分之不多时候,它就是杀鸡用牛刀