1. 什么是责任链模式
定义:为了避免请求发送者和多个请求处理者之间的耦合,于是将所有请求的处理者通过前一对象记住其后一对象的引用。当有请求来时,可将请求沿着链传递,直到有处理者处理它为止。
如图,每个节点只负责自己处理的事情,节点1处理完了再到节点2,节点2处理完了再到节点3
优点:
降低了对象之间的耦合度,该模式使得一个对象无须知道到底是哪一个对象处理其请求。
责任链简化了对象之间的连接,每个对象只需要保持一个后继者的引用,不需要保持其他所有的处理者,避免了使用众多的if/else责任分担,每个类只处理自己该处理的工作,不该处理的传递给下一个对象完成。
2. 责任链模式的2种形式
关于责任链模式,其有两种形式:
⼀种是通过外部调⽤的⽅式对链的各个节点调⽤进⾏控制,从⽽进⾏链的各个节点之间的切换;
另⼀种是链的每个节点⾃由控制是否继续往下传递链的进度,这种⽐较典型的使⽤⽅式就是Netty中的责任链模式。
3. 责任链模式项目实战
本文以电商系统中下单操作为例子:假设某商品处于活动中,下单会经过以下校验:
商品是否还处于活动中。如果没在活动中,则不能下单了。
此商品购买的最大数量。如果下单的数量超出了最大允许购买的数量,下单失败。
哪些人能购买。此商品只能允许部分人购买,其他人不具备购买资格如果下单,将会下单失败。
此商品是否能用劵购买。此商品不能使用优惠券,使用优惠券则会下单失败。
4. 没用责任链模式的写法
我们先看看传统如何写这个校验代码,每个校验操作写一个方法,本文将会以最简单的代码去演示。
public class OrderService {
public void placeOrder() {
checkActive();
checkPurchaseMaxCount();
checkCanPurchaseByUser();
checkCanUseCoupon();
System.out.println("购买成功");
}
private void checkActive() {
System.out.println("检查此商品是否还处于活动中....");
System.out.println("检查通过===========");
}
private void checkPurchaseMaxCount() {
System.out.println("检查此商品能购买的最大数量....");
System.out.println("检查通过===========");
}
private void checkCanPurchaseByUser() {
System.out.println("检查此购买用户是否有资格购买此商品....");
throw new RuntimeException("检查失败,此用户不具备购买资格");
}
private void checkCanUseCoupon() {
System.out.println("检查此商品是否能使用优惠券....");
}
public static void main(String[] args) {
OrderService orderService = new OrderService();
orderService.placeOrder();
}
}
5. 传统责任链模式写法
以下代码就是责任链模式中的节点控制模式,可以看到我在第一个节点里面我可以控制是否走下一个节点,对应的就是这句代码:super.handler.handle();
public abstract class IHandler {
// 下一个处理器
protected IHandler handler;
abstract void handle();
public void setHandler(IHandler handler) {
this.handler = handler;
}
}
public class CheckActiveHandler extends IHandler{
@Override
public void handle() {
System.out.println("检查此商品是否还处于活动中....");
System.out.println("检查通过===========");
// 进入下一个处理器
super.handler.handle();
}
}
public class CheckPurchaseMaxCountHandler extends IHandler{
@Override
public void handle() {
System.out.println("检查此商品能购买的最大数量....");
System.out.println("检查通过===========");
super.handler.handle();
}
}
public class CheckCanQualifiedToPurchaseHandler extends IHandler{
@Override
public void handle() {
System.out.println("检查此购买用户是否有资格购买此商品....");
throw new RuntimeException("检查失败,此用户不具备购买资格");
}
}
public class CheckCanUseCouponHandler extends IHandler{
@Override
public void handle() {
System.out.println("检查此商品是否能使用优惠券....");
}
}
下单操作首先要实例化所有的handler,然后设置好顺序
public class OrderService2 {
public void placeOrder() {
IHandler checkActiveHandler = new CheckActiveHandler();
IHandler checkCanQualifiedToPurchaseHandler = new CheckCanQualifiedToPurchaseHandler();
IHandler checkCanUseCouponHandler = new CheckCanUseCouponHandler();
IHandler checkPurchaseMaxCountHandler = new CheckPurchaseMaxCountHandler();
// 按顺序设置下个处理器
checkActiveHandler.setHandler(checkPurchaseMaxCountHandler);
checkPurchaseMaxCountHandler.setHandler(checkCanQualifiedToPurchaseHandler);
checkCanQualifiedToPurchaseHandler.setHandler(checkCanUseCouponHandler);
checkActiveHandler.handle();
System.out.println("购买成功");
}
public static void main(String[] args) {
OrderService2 orderService = new OrderService2();
orderService.placeOrder();
}
}
6. 责任链模式结合spring优雅实现
以下代码就是责任链模式中的外部控制模式,可以看到节点里面控制不了是否走下个节点了
public interface IHandler {
void check();
}
@Component
@Order(1) //设置检查的顺序,这是第一个检查
public class CheckActiveHandler implements IHandler {
@Override
public void check() {
System.out.println("检查此商品是否还处于活动中....");
System.out.println("检查通过===========");
}
}
@Component
@Order(2)
public class CheckPurchaseMaxCountHandler implements IHandler {
@Override
public void check() {
System.out.println("检查此商品能购买的最大数量....");
System.out.println("检查通过===========");
}
}
@Component
@Order(3)
public class CheckCanQualifiedToPurchaseHandler implements IHandler {
@Override
public void check() {
System.out.println("检查此购买用户是否有资格购买此商品....");
throw new RuntimeException("检查失败,此用户不具备购买资格");
}
}
@Component
@Order(4)
public class CheckCanUseCouponHandler implements IHandler {
@Override
public void check() {
System.out.println("检查此商品是否能使用优惠券....");
}
}
spring会按order注解标识的顺序,自动注入IHandler的所有实现类,所以我们直接循环调用就可以了
@Service
public class OrderService3 {
@Autowired //spring会按order顺序,自动注入IHandler的所有实现类
private List<IHandler> handlers;
public void placeOrder() {
handlers.forEach(handler -> handler.check());
System.out.println("购买成功");
}
}
7. 开源框架中责任链模式的运用
开源框架中很多精彩的地方都运用了责任链模式,让我们一起看看吧!
Spring的责任链模式运用
Spring Web 中的 HandlerInterceptor接口在web开发中非常常用,里面有preHandle()、postHandle()、afterCompletion()三个方法,实现这三个方法可以分别在调用”Controller”方法之前,调用”Controller”方法之后渲染”ModelAndView”之前,以及渲染”ModelAndView”之后执行。
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
HandlerInterceptor在责任链中充当处理者的角色,通过HandlerExecutionChain进行责任链调用。
public class HandlerExecutionChain {
...
@Nullable
private HandlerInterceptor[] interceptors;
private int interceptorIndex = -1;
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
}
调用的方式非常简单,即通过数组存储注册在Spring中的HandlerInterceptor,然后通过interceptorIndex作为指针去遍历责任链数组按顺序调用处理者。
mybatis使用责任链模式
Mybatis可以配置各种Plugin,无论是官方提供的还是自己定义的,Plugin和Filter类似,就在执行Sql语句的时候做一些操作。Mybatis的责任链则是通过动态代理的方式,使用Plugin代理实际的Executor类。(这里实际还使用了组合模式,因为Plugin可以嵌套代理),核心代码如下:
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;
}
}
8. 责任链模式总结
责任链模式 , 又称为 职责链模式 ;
责任链模式定义 : 为 请求 创建一个接收该 请求对象 的 链 , 链条中每个元素都是一个对象 ;
责任链模式类型 : 行为型 ;
责任链模式 适用场景 :
**
① 多个对象可以处理同一个请求,但具体由哪个对象处理则在运行时动态决定。
② 在请求处理者不明确的情况下向对个对象中的一个提交一个请求。
③ 需要动态处理一组对象处理请求。
责任链模式 优点 :
① 解耦 : 请求的 发送者 和 接收者 解耦 ; 接收者 是 请求的处理者 ;
② 动态组合 : 责任链 可以 动态组合 , 使用配置 设置责任链的 顺序及 是否出现 ; 可以随时对责任链排序 , 随时增加拆除责任链中的某个请求对象 ;
责任链模式 缺点 :
① 性能 : 如果 责任链 太长 , 或责任链中请求的 处理时间过长 , 可能会 影响性能 ;
② 个数 : 责任链 可能过多 ;