责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
接下来我们将结合实际应用场景来详细分析责任链的原理和使用方式。
背景案例
小明平时工作很认真,但是最近他由于加班有点累,然后看了下日历,中秋节假期到国庆节中间有6个工作日,于是他想请6天假连上中秋国庆好好休息半个月,但是事情永远不是一帆风顺的,由于请假时间比较长,主管只能批三天的假期,需要主管审批后大领导再审批。
请假到审批流程大致如下:
案例分析
从上述描述的案例中,流程还是比较简单,只有两级,小明提交请假申请,然后审批节点先到主管那里,主管会判断有没有权限审批(3天以内),如果有的话直接通过,否则交给下一级(部门经理)审批,流程图大致如下:
审批流程中有几个关键的信息:客户端调用,审批节点以及下一级审批节点的引用。模型如下:
抽象出来大致有三个角色:客户端,抽象处理者和具体处理者。
代码实现及测试
有四个关键类型,Approver(抽象处理者),Leader(主管),Manager(经理),Client(客户端):
Approver
/**
* 抽象处理者
*
* @author Typhoon
*/
public abstract class Approver {
protected Approver next;//后继审批人
public Approver getNext() {
return next;
}
public void setNext(Approver next) {
this.next = next;
}
public abstract void approve(int days) ;
}
Leader
/**
* 主管
*
* @author Typhoon
*/
public class Leader extends Approver {
@Override
public void approve(int days) {
if(days <= 3) {
System.out.println("请假天数:" +days + ",主管审批通过");
return;
}
this.getNext().approve(days);
}
}
Manager
/**
* 经理
*
* @author Typhoon
*/
public class Manager extends Approver {
@Override
public void approve(int days) {
System.out.println("请假天数:" +days + ",经理审批通过");
}
}
Client
public class Client {
public static void main(String[] args) {
Approver leader = new Leader();//主管
Approver manager = new Manager();//经理
leader.setNext(manager);
int days = 3;//请假天数
leader.approve(days);
}
}
运行客户端:
可以看到3天的假期主管已经完成了审批,不许经理审批。
把Client中的请假天数改成6,重新运行客户端:
根据结果可以看出,对于6天的假期,主管已经无权审批,转给了下一级节点经理审批。
从上述的案例和编码实现中,相信大家对责任链模式有了初步的了解,用一句话总结就是:每个节点如果有能力和权限处理请求,就处理完返回,否则转交给下个节点处理。
应用场景分析
我们日常开发中常用到的责任链模式应用场景有servlet中的filter,dubbo中的filter以及mybatis中的plugin,接下来我们逐个做下介绍:
servlet中的Filter
servlet中分别定义了一个 Filter和FilterChain的接口,核心代码如下:
public final class ApplicationFilterChain implements FilterChain {
private int pos = 0; //当前执行filter的offset
private int n; //当前filter的数量
private ApplicationFilterConfig[] filters; //filter配置类,通过getFilter()方法获取Filter
private Servlet servlet
@Override
public void doFilter(ServletRequest request, ServletResponse response) {
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
filter.doFilter(request, response, this);
} else {
// filter都处理完毕后,执行servlet
servlet.service(request, response);
}
}
}
代码比较简单,结构也比较清晰,定义一个Chain,里面包含了Filter列表和servlet,达到在调用真正servlet之前进行各种filter逻辑。
Dubbo中的Filter
Dubbo在创建Filter的时候是另外一个方法,把Filter封装成 Invoker的匿名类,通过链表这样的数据结构来完成责任链,核心代码如下:
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker;
//获取满足条件的Filter
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (filters.size() > 0) {
for (int i = filters.size() - 1; i >= 0; i --) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
...
public Result invoke(Invocation invocation) throws RpcException {
return filter.invoke(next, invocation);
}
...
};
}
}
return last;
}
Dubbo的责任链就没有类似FilterChain这样的类吧Filter和调用Invoker结合起来,而是通过创建一个链表,调用的时候我们只知道第一个节点,每个节点包含了下一个调用的节点信息。 这里的虽然Invoker封装Filter没有显示的指定next,但是通过java匿名类和final的机制达到同样的效果。
Mybatis中的Plugin
Mybatis可以配置各种Plugin,无论是官方提供的还是自己定义的,Plugin和Filter类似,就在执行Sql语句的时候做一些操作。Mybatis的责任链则是通过动态代理的方式,使用Plugin代理实际的Executor类。核心代码如下:
public class Plugin implements InvocationHandler{
private Object target;
private Interceptor interceptor;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (满足代理条件) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
}
//对传入的对象进行代理,可能是实际的Executor类,也可能是Plugin代理类
public static Object wrap(Object target, Interceptor interceptor) {
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
}
示意图如下:
总结
责任链模式是框架中使用最频繁的设计模式之一,在日常开发中如果能够娴熟的运用责任链模式,能够让代码更加清晰并且结构化程度更好。通过上述案例的描述和编码实现以及常用使用场景的介绍,相信大家对责任链模式有了很深刻的了解,并且能够有体感的体会到责任链模式对一些常见的业务场景优秀的解决方案,希望能够给各位看官带来帮助。
本文分享自 PersistentCoder 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!