一、简介
责任链模式(Chain of Responsibility),又称职责链模式,它属于行为型模式。通过责任链模式,你可以为某个请求创建一个接收者对象链。每个接收者对象依序检查此请求,并对其进行处理,如果该对象不能处理该请求或者处理完成后,就将它传递给链中的下一个接收者对象。
责任链模式的原理UML类图如下图所示:
责任链模式的角色介绍:
- Handler:抽象的处理者,定义了一个处理请求的接口。
- ConcreteHandler1,2:具体的处理者,处理它负责的请求,如果它处理不了,请将请求交给它的后继者(下一个处理者进行处理请求),从而形成一个责任链。
- 客户类:负责创建责任链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
二、使用
1.场景
学校OA系统的采购审批模块中,采购员需要采购教学器材时,有着以下要求:
- 如果0<金额<=5000,由教学主任审批
- 如果5000<金额<=10000,由院长审批
- 如果10000<金额<=30000,由副校长审批
- 如果金额大于30000,就由校长审批
2.UML类图设计
根据上述的需求采用责任链设计模式来设计程序,程序的UML类图如下:
说明:Approver审批员的角色为抽象处理者,而PurchaseRequest为购买请求实体类。
3.代码实现
抽象处理者Approver类的代码如下:
//抽象审批者
public abstract class Approver {
Approver approver;// 下一个审批者
String name;// 名字
public Approver(String name) {
this.name = name;
}
/**
* @param approver 设置下一个审批者
*/
public void setApprover(Approver approver) {
this.approver = approver;
}
// 处理请求审批的方法,由子类实现
public abstract void processRequest(PurchaseRequest purchaseRequest);
}
四个具体的处理者,CollegeApprover类等的实现代码:
public class CollegeApprover extends Approver {
public CollegeApprover(String name) {
// TODO Auto-generated constructor stub
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if (purchaseRequest.getPrice() >= 5000 && purchaseRequest.getPrice() < 10000) {
System.out.println("请求编号" + purchaseRequest.getId() + "被" + this.name + "处理了");
} else {
approver.processRequest(purchaseRequest);
}
}
}
public class DepartmentApprover extends Approver {
public DepartmentApprover(String name) {
// TODO Auto-generated constructor stub
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if (purchaseRequest.getPrice() < 5000) {
System.out.println("请求编号" + purchaseRequest.getId() + "被" + this.name + "处理了");
} else {
approver.processRequest(purchaseRequest);
}
}
}
public class SchoolMasterApprover extends Approver {
public SchoolMasterApprover(String name) {
// TODO Auto-generated constructor stub
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if (purchaseRequest.getPrice() >= 30000) {
System.out.println("请求编号" + purchaseRequest.getId() + "被" + this.name + "处理了");
} else {
approver.processRequest(purchaseRequest);
}
}
}
public class ViceSchoolMasterApprover extends Approver {
public ViceSchoolMasterApprover(String name) {
// TODO Auto-generated constructor stub
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if (purchaseRequest.getPrice() >= 10000 && purchaseRequest.getPrice() < 30000) {
System.out.println("请求编号" + purchaseRequest.getId() + "被" + this.name + "处理了");
} else {
approver.processRequest(purchaseRequest);
}
}
}
购买请求实体类PurchaseRequest的实现代码:
//购买请求实体类
public class PurchaseRequest {
private int id;
private String type;
private float price;
public PurchaseRequest(int id, String type, float price) {
super();
this.id = id;
this.type = type;
this.price = price;
}
//省略getter、setter
}
最后,在客户类Client创建和设置处理请求的责任链,代码实现如下:
public class Client {
public static void main(String[] args) {
PurchaseRequest purchaseRequest = new PurchaseRequest(1, "教材", 15000);
DepartmentApprover departmentApprover = new DepartmentApprover("张主任");
CollegeApprover collegeApprover = new CollegeApprover("李院长");
ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("王副校长");
SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("赵校长");
departmentApprover.setApprover(collegeApprover);
collegeApprover.setApprover(viceSchoolMasterApprover);
viceSchoolMasterApprover.setApprover(schoolMasterApprover);
schoolMasterApprover.setApprover(departmentApprover);
departmentApprover.processRequest(purchaseRequest);
}
}
请求购买金额为15000的教材进行测试,程序运行结果:
三、责任链模式在Tomcat中的应用
首先先复习一下Tomcat的整体架构:
(1)Tomcat整体架构分析
首先是Tomcat 的模块分层结构图,如下图所示:
Tomcat这六大模块中Catalina容器是Tomcat的核心,其他的模块都是为Catalina容器提供支撑的,比如:通过Coyote模块提供链接通信,Jasper模块提供JSP引擎,Naming提供JNDI服务,Juli提供日志服务。
其中Catalina的主要组件结构如下图所示:
如上图所示,Catalina管理Server,而Server表示整个服务器,Server下面有多个服务Service,每个服务都包含有多个(Coyote实现的)连接器组件Connector和一个容器组件Container。
在Tomcat启动时,会初始化一个Catalina实例,负责解析Tomcat的配置文件,以此来创建服务器Server组件,并根据命令来对其进行管理。其中的Server,负责组装并启动Servlet引擎,Tomcat连接器等组件,Server通过实现Lifecycle接口,提供了一种优雅的启动和关闭整个系统的方式。其中Container的结构包含了4种容器,分别是Engine、Host、Context和Wrapper,这四个容器之间不是平行关系,而是包含关系。
容器关系结构如下图所示:
(2)Tomcat中的责任链模式
在Tomcat中请求的处理流程和过滤器Filter的调用设计都采用的是责任链模式,这个设计模式是Tomcat中Container容器设计的基础,父子容器就是通过一条链连接在一起。这条链一直将请求正确的传递给最终处理请求的那个Servlet。从Engine到Host再到Context一直到Wrapper都是通过一个链传递请求。各容器中的管道Pipeline同样通过其中的阀门valve传递请求。
Tomcat中关于容器中处理请求的流程涉及的责任链模式的关键类图,如下图所示:
从上图中,我们可以看到每一个容器都会有一个Pipeline,而一个Pipeline又会具有多个Valve阀门,其中StandardEngine对应的阀门是StandardEngineValve,StandardHost对应的阀门是StandardHostValve,StandardContext对应的阀门是StandardContextValve,StandardWrapper对应的阀门是StandardWrapperValve。这里每一Pipeline就好比一个管道,而每一Valve就相当于一个阀门,一个管道可以有多个阀门,而对于阀门来说有两种,一种阀门在处理完自己的事情以后,只需要将工作委托给下一个和自己在同一管道的阀门即可,第二种阀门是负责衔接各个管道的,它负责将请求传递给下个管道的第一个阀门处理,而这种阀门叫Basic阀门,它是每个管道中最后一个阀门,上面的命名为Standard开头的Valve都属于第二种阀门。其中AccessLogValve则属于第一种阀门,AccessLogValve是在默认Host容器中默认提供的valve。这点可以在server.xml中能够知道,如下图所示:
AccessLogValve是请求访问日志阀门,通过此阀门可以记录所有客户端的访问日志,包括远程主机IP、远程主机名、请求方法、请求协议、会话id、请求时间、处理时长、数据包大小等等。它提供了任意参数化的配置,可以通过任意组合来定制你的访问日志格式。另外ErrorReportValve同样属于第一种阀门,这是一个将错误以html格式输出的阀门。
Tomcat中关于容器Host中处理请求的流程涉及的责任链模式的关键时序图,如下图所示:
通过上图,我们可以很清楚的了解到Tomcat的请求处理流程中容器之间调用的过程。当用户请求服务器的时候,EndPoint会接受请求,Processor中从Socket连接中根据http协议解析出对应的数据,构造Request和Response对象,通过CoyoteAdapter将Request和Response对象转化为HttpServletRequest和HttpServleResponse对象,然后传递给后面的容器处理。
顶层容器是StandardEngine,StandardEngine处理请求其实是通过容器的Pipeline进行的,而Pipeline其实最终是通过管道上的各个阀门进行的,当请求到达StandardEngineValve的时候,此阀门会将请求转发给对应StandardHost的Pipeline的第一个阀门处理,然后以此最终到达StandardHostValve阀门,后面过程图中省略了,后面的过程是它又会将请求转发给StandardContext的Pipeline的第一个阀门,这样以此类推,最后到达StandardWrapperValve,此阀门会根据Request来构建对应的Servlet,并将请求转发给对应的HttpServlet处理。
从这里我们可以看出其实Tomcat核心请求处理流程就是通过责任链一步步的组装起来的。
四、总结
(1)责任链模式的优点:
- 将请求的发送者和接收者解耦
- 可以简化你的对象,因为它不需要知道链的结构。
- 通过改变链内的成员或调动它们的次序,允许你动态地新增或者删除责任。
- 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
(2)责任链模式的缺点
- 并不保证请求一定会被执行,如果没有任何对象处理它的话,它可能会落到责任链尾端都没有得到处理。
- 可以不容易观察运行时的特征,有碍于发现错误。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
(3)责任链模式的用途
- 有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。
- 可动态指定一组对象处理请求,或添加新的处理者。
- 在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。