1.定义
避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止
2. 使用场景
一个请求需要被多个对象中的某一个处理,但是到底是哪个对象必须在运行时根据条件决定。
可以看到责任链模式只有两个角色:
1. Handler:处理器类的接口
2. ConcreteHandler x:处理器实现类,里面定义了下一个处理器以及处理方法
3. 举例分析
层层上报,最终会由校长进行审批
正常代码书写
// 辅导员审批
if (isTrue) {
instructor.approve();
// 系主任审批
if (isOk) {
departmentHead.approve();
// 院长审批
if (isGoodEnough) {
dean.approve();
// 校长审批
if (isGood) {
headmaster.approve();
} else {
System.out.println("很遗憾,未通过全校公示");
}
} else {
System.out.println("很遗憾,未在学院评选中脱颖而出");
}
} else {
System.out.println("该生表现欠佳,不予通过");
}
} else {
System.out.println("信息填写有误,请重新认真填写");
}
可以通过责任链模式进行处理,如果不满足要求直接停止处理,如果满足则向下传递处理直至到达链末尾
4. 最终实现
在自己权限下自己处理,超过权限范围上交处理
第一步:创建抽象处理者
@Data
public abstract class Handler {
private Handler next;
public abstract void handleRequest(String name, int days);
}
第二步:创建组长、总监、部长三个具体处理者,实现具体的处理逻辑
public class PMHandler extends Handler {
@Override
public void handleRequest(String name, int days) {
if (days <= 3) {
System.out.println(name + ",组长已经同意您的请假审批!");
} else {
if (getNext() != null) {
getNext().handleRequest(name, days);
} else {
System.out.println("请假天数太多,申请被驳回!");
}
}
}
}
public class DirectorHandler extends Handler {
@Override
public void handleRequest(String name, int days) {
if (days <= 7) {
System.out.println(name + ",中心总监已经同意您的请假审批");
} else {
if (getNext() != null) {
getNext().handleRequest(name, days);
} else {
System.out.println("请假天数太多,申请被驳回!");
}
}
}
}
public class MinisterHandler extends Handler {
@Override
public void handleRequest(String name, int days) {
if (days <= 15) {
System.out.println(name + ",部长已经同意您的请假审批");
} else {
if (getNext() != null) {
getNext().handleRequest(name, days);
} else {
System.out.println("请假天数太多,申请被驳回!");
}
}
}
}
第三步:创建测试类,在类中创建并使用责任链(向责任链传递请求)
public class OASystem {
public static void main(String[] args) {
// 创建具体处理者
Handler pm = new PMHandler();
Handler director = new DirectorHandler();
Handler minister = new MinisterHandler();
// 构建责任链
pm.setNext(director);
director.setNext(minister);
// 使用责任链
pm.handleRequest("张三", 5);
}
}
5. 代码改进
上面代码如果增加、删除或者调整顺序很不方便,还需要维护处理器和处理器的前后顺序关系
基于List构建隐形的责任链
// 抽象处理者
public interface Handler {
public abstract void handleRequest(String name, int days);
}
// 具体处理者
public class PMHandler implements Handler {
@Override
public void handleRequest(String name, int days) {
if (days <= 3) {
System.out.println(name +",组长已经同意您的请假审批!");
}
}
}
public class DirectorHandler implements Handler {
@Override
public void handleRequest(String name, int days) {
if (days <= 7) {
System.out.println(name + ",中心总监已经同意您的请假审批");
}
}
}
public class MinisterHandler implements Handler {
@Override
public void handleRequest(String name, int days) {
if (days <= 15) {
System.out.println(name + ",部长已经同意您的请假审批");
}
}
}
// 责任链类
public class HandlerChain implements Handler {
private List<Handler> handlerList;
public HandlerChain() {
this.handlerList = new ArrayList<>();
}
public HandlerChain addHandler(Handler handler) {
handlerList.add(handler);
return this;
}
@Override
public void handleRequest(String name, int days) {
for (Handler handler : handlerList) {
handler.handleRequest(name, days);
}
}
}
// 客户类
public class OASystem {
public static void main(String[] args) {
// 创建具体处理者
Handler pm = new PMHandler();
Handler director = new DirectorHandler();
Handler minister = new MinisterHandler();
// 构建责任链
HandlerChain chain = new HandlerChain()
.addHandler(pm)
.addHandler(director)
.addHandler(minister);
// 使用责任链
chain.handleRequest("王二", 10);
}
}
这块原来那个作者对于增加或者删除以及改变顺序的做法是这样的,使用list
记录责任链
- 可以通过set操作,将组长换成方向负责人
- 可以通过swap操作,调整部长和总监的审批顺序(不太恰当的例子😂)
- 可以通过add(index, element)操作,在指定位置增加handler
- 可以通过remove操作删除handler
6. 最终版的责任链
当时在公司里看这段代码的时候,感觉并不是责任链,直到了解了上面责任链的演化过程,才知道这段代码也是,这里面通过spring将所有的处理器放到List集合中,然后对集合进行遍历处理,通过@order注解来决定执行顺序,处理器里面对方法进行重写,对具体业务逻辑功能书写
处理器抽象类
public abstract class AbstractTestHandler {
// 测试处理器
public abstract void handleTestInfo(HandlerObject handlerObject);
}
具体对哪个对象进行处理
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class HandlerObject {
private Handler01 handler01;
private Handler02 handler02;
}
两个随意定义的类
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Handler01 {
private Integer age;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Handler02 {
private String name;
}
实现类,对某个对象进行不同的业务处理
@Component
@Order(1)
public class Handler01Handler extends AbstractTestHandler {
@Override
public void handleTestInfo(HandlerObject handlerObject) {
handlerObject.getHandler01().setAge(12);
}
}
@Component
@Order(2)
public class Handler02Handler extends AbstractTestHandler {
@Override
public void handleTestInfo(HandlerObject handlerObject) {
handlerObject.getHandler02().setName("test");
}
}
操作类,对处理器进行遍历实现功能的
@Component
public class TestOperator {
@Autowired
List<AbstractTestHandler> testHandlers;
public void execute(HandlerObject handlerObject) {
testHandlers.forEach(resourceHandler -> resourceHandler.handleTestInfo(handlerObject));
}
}
测试类
public void test01(){
HandlerObject handlerObject = new HandlerObject();
handlerObject.setHandler02(Handler02.builder().build());
handlerObject.setHandler01(Handler01.builder().build());
testOperator.execute(handlerObject);
System.out.println(handlerObject);
}
后面我又试了一下,不用抽象类,用接口定义也是可以的
7. 优缺点
优点:
降低了对象之间的耦合度:具体业务功能指派给指定处理器进行处理,降低代码冗余
增强了系统的可扩展性:更改代码的话直接在特定处理器内进行更改就可以
缺点:
按之前那种next调用的话,配错有可能出现循环调用
8. 源码学习责任链
基于tomcat源码进行学习
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.0.41</version>
</dependency>
<!-- Thanks for using https://jar-download.com -->
</dependencies>
过滤器的作用:
- 通过Filter可以对请求进行预处理,比如ip地址白名单,决定客户端的请求是否被处理,
RemoteHostFilter
- 通过Filter还能对响应结果进行处理,比如在响应头中添加信息,
HttpHeaderSecurityFilter
过滤器主要使用途径
Filter在请求进入容器后、未进入servlet之前,对请求进行预处理;同时,Filter可以在servlet返回响应后、未返回给客户端之前,对响应进行处理
tomcat的源码中,定义了Filter接口
public interface Filter {
public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
public void destroy();
}
-
init()方法:servlet容器在实例化Filter对象后、调用Filter对象的doFilter方法前,调用init()方法(仅一次)以表明该filter可以提供服务
-
doFilter()方法:
请求/响应对(pair)在FilterChain上传递,调用链上filter的doFilter()方法可以处理请求/响应对
方法入参中的FilterChain 是构成责任链的关键,通过调用chain的doFilter()方法,可以实现请求/响应对的传递
FilterChain的doFilter()方法中,会通过例如数组下标pos的形式调用下一个filter
因此,形成了一个调用链(责任链):chain.doFilter()调用filter1 --> filter1.doFilter()执行处理逻辑、调用chain.doFilter() --> chain.doFilter()调用filter2 --> filter2.doFilter()执行处理逻辑、调用chain.doFilter() --> … -
destroy()方法:
- 容器调用filter的destroy()方法,以表明该filter不再提供服务
- 仅当filter的doFilter()方法中,所有线程都退出或超时,容器才会调用destroy()
FilterChain
FilterChain接口是filter责任链接口,接口中doFilter()方法的实现逻辑可以保证请求/响应对在链上的传递与处理
public interface FilterChain {
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}
- 如果存在下一个filter,则调用链上的下一个filter处理请求/响应对
- 如果不存在下一个filter,则调用链末尾的资源,如servlet的service()方法
ApplicationFilterChain
ApplicationFilterChain有四个重要的成员变量:
- filters:ApplicationFilterConfig类型的数组,记录FilterChain上的filter;初始化长度为0,使用时按照INCREMENT = 10的增量进行扩容
- pos:保存FilterChain当前位置的int变量,实际就是filters数组的索引,可以指向某个filter
- n:FilterChain的实际大小,也就是filters数组中存储的filter的数量;filters数组每次按照10进行扩容,这是filters数组的容量,并非filter的数量
- servlet:FilterChain上的filter访问到链尾时,将执行servlet实例的service()方法;通过servlet的service()方法,实现对用户请求的处理
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
private int pos = 0;
private int n = 0;
private Servlet servlet = null;
ApplicationFilterChain相关方法的简单介绍:
ApplicationFilterFactory的createFilterChain()是一个工厂方法,负责完成ApplicationFilterChain的创建,包括设置servlet、向链中添加filter等
public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {
// 为FilterChain添加filter的关键代码
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName());
// ...,其他代码省略
filterChain.addFilter(filterConfig);
}
ApplicationFilterChain的addFilter()方法,被createFilterChain()方法调用,涉及filter是否重复添加的校验、filters数组的扩容、filter的添加
void addFilter(ApplicationFilterConfig filterConfig) {}
doFilter()方法(重头戏)
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
@Override
public Void run()
throws ServletException, IOException {
internalDoFilter(req,res);
return null;
}
}
);
} catch( PrivilegedActionException pe) {
// 省略异常的处理
}
} else {
internalDoFilter(request,response);
}
}
internalDoFilter()方法:
1. 利用pos变量,从头开始逐个访问FilterChain上的filter,调用filter的doFilter()方法处理请求/响应对
2. 如果FilterChain上的所有所有filter都已经执行完毕,则执行servlet的service()方法处理请求
private void internalDoFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = null;
try {
filter = filterConfig.getFilter();
// ... 省略代码
if( Globals.IS_SECURITY_ENABLED ) {
// ... 省略代码
SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response);
}
// ... 省略catch
return; // 巧妙的return处理,后续会提到原因
}
try {
// ... 省略代码
if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) {
if( Globals.IS_SECURITY_ENABLED ) {
// ... 省略代码
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args, principal);
} else {
servlet.service(request, response);
}
} else {
servlet.service(request, response);
}
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response);
}
// ... 省略catch和finally
}
- 笔者在看到这里时,整个人是懵逼的:pos的更新确实能沿着FilterChain调用其上的filter,问题是开头是if (pos < n)并非while(pos < n),这咋能实现循环呢?别骗我读书少哦 😠
- 耐下性子,点开filter的doFilter()方法,发现奥妙就在这里:filter的doFilter()方法,都会在合适的时机调用传入的chain的doFilter()方法
- 例如,FailedRequestFilter的doFilter()方法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (!isGoodRequest(request)) {
FailReason reason = (FailReason) request.getAttribute(Globals.PARAMETER_PARSE_FAILED_REASON_ATTR);
int status;
switch (reason) {
// 为各种错误类型设置status
}
// 执行失败,直接返回携带错误信息的响应
((HttpServletResponse) response).sendError(status);
return;
}
// 执行成功,继续访问FilterChain上的下一个filter
chain.doFilter(request, response);
}
RemoteHostFilter的doFilter()方法:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
process(request.getRemoteHost(), request, response, chain);
}
protected void process(String property, ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (isAllowed(property)) { // 请求被允许,继续访问FilterChain上的下一个Filter
chain.doFilter(request, response);
} else {
// 请求不被允许,则返回携带错误信息的响应
}
}
也就是说,FilterChain的doFilter()方法和filter的doFilter()方法,通过相互调用形成了一条处理请求/响应对的责任链
FilterChain的doFilter()方法中的return语句
return语句的作用:避免多次调用servlet.service()方法
- 场景一: FilterChain上的filter均成功执行
- 从上面的分析可知,如果所有的filter都成功执行,最终将会将会访问到FilterChain上的最后一个filter
- 此时,最后一个filter的doFilter()方法将调用chain.doFilter()方法,发现pos < n条件不满足,则if语句之外的diamante,即调用servlet.service()方法
- servlet.service()方法执行完成后,从最后一个filter的doFilter()方法退出
- 此时,如果没有return语句,会再次执行servlet.service()方法
- 场景二: filter处理请求失败
- 如果filter处理请求/响应对时失败,例如,发现客户端IP不是受信任的(RemoteHostFilter),发现请求体格式不对(FailedRequestFilter)
- 此时,都将直接返回携带错误信息的响应,最终会从filter的doFilter()方法中返回
- 如果不存在return语句,则会继续调用servlet.service()方法处理请求
- 其实,这样的情况下,根本无需调用servlet.service()方法处理请求