What
- 定义
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. 将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。
上面是GoF的定义,在实际的开发中,也存在对这个模式的变体,那就是请求不会中途终止传递,而是会被所有的处理器都处理一遍,或者直到某个处理器不处理了为止等。
- 应用
职责链模式常用在框架的开发中,为框架提供扩展点,让框架的使用者在不修改框架源码的情况下,基于扩展点添加新的功能,这也体现了对修改关闭对扩展开发的设计原则。实际上,更具体点来说,职责链模式最常用来开发框架的过滤器和拦截器。
- 优点
- 降低耦合度。它将请求的发送者和接收者解耦;
- 简化了对象。使得对象不需要知道链的结构;
- 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任;
- 增加新的请求处理类很方便。
- 缺点
- 不能保证请求一定被接收;
- 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用;
- 可能不容易观察运行时的特征,有碍于除错。
- 使用场景
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定;
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求;
- 可动态指定一组对象处理请求。
How
数组式
/**
* 数组式实现
* <p>
* 沿着处理链,如果某个处理器能处理该请求,就不继续往下传递;如果不能处理,则交由后面的处理器来处理
*
* @author yiy
* @date 12/22/2021
*/
public class ArrayWay {
public static void main(String[] args) {
HandlerChain chain = new HandlerChain();
chain.addHandler(new HandlerA());
chain.addHandler(new HandlerB());
chain.triggerHandle();
}
}
/**
* 处理器接口
*/
interface IHandler {
/**
* 处理逻辑
*
* @return true:能够处理,中断链路无需继续向后传递;false:不能处理,继续向后传递
*/
boolean doHandle();
}
/**
* 处理器A
*/
class HandlerA implements IHandler {
@Override
public boolean doHandle() {
//具体处理逻辑...
System.out.println("处理器A无法处理,传递至下一个处理器!");
return false;
}
}
/**
* 处理器B
*/
class HandlerB implements IHandler {
@Override
public boolean doHandle() {
//具体处理逻辑...
System.out.println("处理器B能处理,中断处理链路!");
return true;
}
}
/**
* 处理器链
*/
class HandlerChain {
/**
* 处理器
*/
private List<IHandler> handlerList = new ArrayList<>();
/**
* 添加处理器到处理器链中
*
* @param handler 处理器
*/
public void addHandler(IHandler handler) {
this.handlerList.add(handler);
}
/**
* 触发处理链
*/
public void triggerHandle() {
for (IHandler handler : handlerList) {
//执行处理器,并判断是否继续向下处理
if (handler.doHandle()) {
//中断链路
break;
}
}
}
}
链表式
/**
* 链表式实现
* 单向链表
*
* @author yiy
* @date 12/22/2021
*/
public class LinkedListWay {
public static void main(String[] args) {
HandlerChain chain = new HandlerChain();
chain.addHandler(new HandlerC());
chain.addHandler(new HandlerD());
chain.triggerHandle();
}
}
/**
* 处理器抽象父类
*/
abstract class Handler {
/**
* 下一个处理器
*/
protected Handler successor = null;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
/**
* 头部处理器触发整条链的入口
*/
public final void handle() {
boolean handled = doHandle();
if (successor != null && !handled) {
//通知下一个处理器处理
successor.handle();
}
}
/**
* 处理逻辑
*
* @return true:能够处理,中断链路无需继续向后传递;false:不能处理,继续向后传递
*/
protected abstract boolean doHandle();
}
/**
* 处理器C
*/
class HandlerC extends Handler {
@Override
protected boolean doHandle() {
//具体处理逻辑...
System.out.println("处理器C无法处理,传递至下一个处理器!");
return false;
}
}
/**
* 处理器D
*/
class HandlerD extends Handler {
@Override
protected boolean doHandle() {
//具体处理逻辑...
System.out.println("处理器D能处理,中断处理链路!");
return false;
}
}
/**
* 处理器链
*/
class HandlerChain {
private Handler head = null;
private Handler tail = null;
/**
* 添加处理器到处理器链中
*
* @param handler 处理器
*/
public void addHandler(Handler handler) {
handler.setSuccessor(null);
//处理器链为空,将处理器链的头尾都置为当前添加的处理器
if (head == null) {
head = handler;
tail = handler;
return;
}
//处理器链不为空
// 将尾部处理器指向当前添加的处理器
tail.setSuccessor(handler);
//将当前添加的处理器设置为尾部处理器
tail = handler;
}
/**
* 触发链路
*/
public void triggerHandle() {
if (head != null) {
head.handle();
}
}
}
应用案例
过滤器
- Servlet Filter 中就是数组式的变种实现
十分精妙,通过处理链中递归调用处理器,实现了在doHandle方法中,支持双向拦截,当前处理器既能拦截执行下一个处理器执行前的数据,又能拦截下一个处理器执行后的数据,这样的实现方式是参考的Servlet Filter
- 注意:这里稍微变化了一些,是将数据在整个链路中都处理一遍
```java
/**
- 过滤器 *
- @author yiy
- @date 12/22/2021
- @since Servlet Filter
- 通过递归实现的过滤器间的调用,
- 十分精妙,实现了在doFilter方法中,支持双向拦截,既能拦截执行过滤器执行前的数据,又能拦截过滤器执行后的数据
- 注意:这里稍微变化了一些,是将数据在整个链路中都过滤一遍 */ public class ServletFilterApply { public static void main(String[] args) { FilterChain chain = new FilterChain(); chain.addFilter(new FilterA()); chain.addFilter(new FilterB()); Object data = new Object(); chain.doFilter(data); } }
/**
- 过滤器接口 *
@since javax.servlet.Filter */ interface IFilter {
/**
- 过滤逻辑 *
- @param data 被过滤的数据
- @param chain 过滤器链 */ void doFilter(Object data, FilterChain chain); }
/**
过滤器A */ class FilterA implements IFilter { @Override public void doFilter(Object data, FilterChain chain) {
//本过滤器具体过滤逻辑... chain.doFilter(data); //执行下一个过滤器 //下一个过滤器完成后的过滤增强...
} }
/**
过滤器B */ class FilterB implements IFilter { @Override public void doFilter(Object data, FilterChain chain) {
//具体过滤逻辑...
} }
/**
- 过滤链 *
@since org.apache.catalina.core.ApplicationFilterChain / class FilterChain { /*
当前执行到那个过滤器了 */ private int pos = 0;
/**
过滤器链 */ private List
filterList = new ArrayList<>(); /**
- 添加过滤器到过滤器链中 *
@param filter 过滤器 */ public void addFilter(IFilter filter) { this.filterList.add(filter); }
/**
- 执行链中的过滤器 *
- @param data 过滤的数据 */ public void doFilter(Object data) { IFilter filter = filterList.get(pos++); //执行过滤器 filter.doFilter(data, this); } } ``` 说明:我看Servlet Filter源码中,将FilterChain也抽象出了一个接口,解耦更彻底。
拦截器
- SpringMVC Interceptor 中就是数组式的变种实现,拦截链示意图如下:
/**
* 拦截器
* <p>
* springMVC拦截器就是数组式的变种实现
* 执行拦截链,只要其中有一个拦截器没通过(返回false),就中断拦截链
*
* @author yiy
* @date 12/23/2021
* @since spring Interceptor
*/
public class SpringInterceptorApply {
public static void main(String[] args) {
InterceptorChain chain = new InterceptorChain();
chain.addInterceptor(new InterceptorA());
chain.addInterceptor(new InterceptorB());
chain.addInterceptor(new InterceptorC());
if (!chain.applyPreHandle()) {
return;
}
System.out.println("===========执行被拦截的逻辑!=============");
chain.applyPostHandle();
chain.triggerAfterCompletion();
}
}
/**
* 拦截器接口
*
* @since org.springframework.web.servlet.HandlerInterceptor
*/
interface IInterceptor {
/**
* 前置函数
* 可校验当前拦截器是否通过
*
* @return true:通过,校验下一个拦截器
*/
boolean preHandle();
/**
* 后置函数
* 在所有preHandle函数通过后统一执行
*/
void postHandle();
/**
* 终函数
* 无论是否校验通过,始终会执行,可用于做资源清理等操作(正常处理时机是在postHandle之后)
*/
void afterCompletion();
}
/**
* 拦截器A
*/
class InterceptorA implements IInterceptor {
@Override
public boolean preHandle() {
//校验是否通过拦截的逻辑
System.out.println("拦截器A前置函数!");
return true;
}
@Override
public void postHandle() {
//通过拦截后,具体处理逻辑
System.out.println("拦截器A后置函数!");
}
@Override
public void afterCompletion() {
//始终处理逻辑
System.out.println("拦截器A资源释放!");
}
}
/**
* 拦截器B
*/
class InterceptorB implements IInterceptor {
@Override
public boolean preHandle() {
//校验是否通过拦截的逻辑
System.out.println("拦截器B前置函数!");
return true;
}
@Override
public void postHandle() {
//通过拦截后,具体处理逻辑
System.out.println("拦截器B后置函数!");
}
@Override
public void afterCompletion() {
//始终处理逻辑
System.out.println("拦截器B资源释放!");
}
}
/**
* 拦截器C
*/
class InterceptorC implements IInterceptor {
@Override
public boolean preHandle() {
//校验是否通过拦截的逻辑
System.out.println("拦截器C前置函数!");
return true;
}
@Override
public void postHandle() {
//通过拦截后,具体处理逻辑
System.out.println("拦截器C后置函数!");
}
@Override
public void afterCompletion() {
//始终处理逻辑
System.out.println("拦截器C资源释放!");
}
}
/**
* 拦截器链
*
* @since org.springframework.web.servlet.HandlerExecutionChain
*/
class InterceptorChain {
/**
* 拦截器
*/
private List<IInterceptor> interceptorList = new ArrayList<>();
/**
* 拦截链中 中断的拦截器的index
*/
private int interceptorIndex = -1;
/**
* 添加拦截器到拦截器链中
*
* @param interceptor 拦截器
*/
public void addInterceptor(IInterceptor interceptor) {
this.interceptorList.add(interceptor);
}
/**
* 执行拦截逻辑
*
* @return false:中断拦截
*/
public boolean applyPreHandle() {
for (int i = 0; i < interceptorList.size(); i++) {
IInterceptor interceptor = interceptorList.get(i);
//执行拦截器前置函数
if (!interceptor.preHandle()) {
triggerAfterCompletion();
return false;
}
//记录中断位置
interceptorIndex = i;
}
return true;
}
/**
* 执行通过拦截后的处理逻辑
*/
public void applyPostHandle() {
for (int i = interceptorList.size() - 1; i >= 0; i--) {
IInterceptor interceptor = this.interceptorList.get(i);
interceptor.postHandle();
}
}
/**
* 触发拦截器的最终通知
*/
public void triggerAfterCompletion() {
for (int i = this.interceptorIndex; i >= 0; i--) {
IInterceptor interceptor = this.interceptorList.get(i);
interceptor.afterCompletion();
}
}
}
Why
Filter、Interceptor vs AOP
粒度 | 应用 | |
---|---|---|
Filter | 粗 | 限流就可以在Filter层去做,因为全局都需要限流防止服务被压垮。 |
Interceptor | 中 | 用户是否登录权限等可以使用Interceptor做 |
aop | 细 | 细粒度到类或者方法的控制使用AOP去做,比如日志 事务 方法级别权限 |
- Filter 可以拿到原始的http请求,但是拿不到你请求的控制器和请求控制器中的方法的信息;
- Interceptor 可以拿到你请求的控制器和方法,也可以拿到请求方法的参数;
- Aop 可以拿到方法的参数,也可以拿到http请求和响应的对象。