开源框架中的责任链模式实践
作者:vivo 互联网服务器团队-Wang Zhi
责任链模式作为常用的设计模式而被大家熟知和使用。本文介绍责任链的常见实现方式,并结合开源框架如Dubbo、Sentinel等进行延伸探讨。
一、责任链介绍
在GoF 的《设计模式》一书中对责任链模定义的:将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止或者所有接收对象处理一遍。
用通俗的话解释在责任链模式中,多个处理器(接收对象)依次处理同一个请求。一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫作责任链模式。
责任链模式有效地降低了发送和接收者之间的耦合度,增强了系统的可扩展性。在责任链的模式下不仅能够针对单个处理器对象进行定制升级(每个处理器对象关注各自的任务),而且能够对整个责任链的处理器对象的顺序的调整以及增删。
本文约定:责任链上的接收对象统一称为处理器;本文中介绍的责任链属于GOF定义中责任链的变种即责任链上的所有处理器都会参与任务的处理。
二、责任链实现
责任链模式有多种实现方式,从驱动责任链上处理器方式的角度可以分类两类,即责任链驱动 和 责任链处理器自驱动。
2.1 处理器自驱动
// 1、定义抽象类
public abstract class AbstractHandler {protected Handler next = null;// 绑定处理器public void setSuccessor(Handler next) {this.next = next;}// 处理器执行操作并驱动下一个处理器public abstract void handle();
}// 2、定义处理器A
public class HandlerA extends AbstractHandler {@Overridepublic void handle() {// do somethingif (next != null) {next.handle();}}
}// 3、定义处理器B
public class HandlerB extends AbstractHandler {@Overridepublic void handle() {// do somethingif (next != null) {next.handle();}}
}// 4、构建责任链并添加处理器
public class HandlerChain {// 通过链表的形式保存责任链private AbstractHandler head = null;private AbstractHandler tail = null;public void addHandler(AbstractHandler handler) {handler.setSuccessor(null);if (head == null) {head = handler;tail = handler;return;}tail.setSuccessor(handler);tail = handler;}public void handle() {if (head != null) {head.handle();}}
}// 5、整体构建责任链添加处理器并进行驱动
public class Application {public static void main(String[] args) {// 构建责任链并添加处理器HandlerChain chain = new HandlerChain();chain.addHandler(new HandlerA());chain.addHandler(new HandlerB());// 责任链负责触发chain.handle();}
}
说明:
-
责任链上的每个处理器对象维护下一个处理器对象,整个责任链的驱动由每个处理器对象自行驱动。
-
每个处理器对象Handler中包含下一个处理器对象next的变量,通过链表形式维护责任链的关系。
2.2 责任链驱动
// 1、定义抽象接口
public interface IHandler {void doSomething();
}// 2、定义处理器A
public class HandlerA implements IHandler {@Overridepublic void doSomething() {// do something}
}// 3、定义处理器B
public class HandlerB implements IHandler {@Overridepublic void doSomething() {// do something}
}// 4、构建责任链并添加处理器
public class HandlerChain {// 通过数组的形式保存处理器private List<IHandler> handlers = new ArrayList<>();public void addHandler(IHandler handler) {handlers.add(handler);}// 由责任链负责遍历所有的处理器并进行调用public void handle() {for (IHandler handler : handlers) {handler.handle();}}
}// 5、整体构建责任链添加处理器并进行驱动
public class Application {public static void main(String[] args) {HandlerChain chain = new HandlerChain();chain.addHandler(new HandlerA());chain.addHandler(new HandlerB());chain.handle();}
}
说明:
-
责任链对象本身以数组的形式维护处理器对象,即上述代码中的handlers 。
-
责任链的处理器的执行由责任链对象循环调用处理器对象驱动,即上述代码中的handle方法。
三、开源框架中责任链应用
责任链低耦合高扩展的特点让它在很多开源的框架中被采用,本文选取了开源框架中的Spring Interceptor、Servlet Filter、Dubbo、Sentinel进行责任链的实现介绍,通过对常用框架中责任链应用的了解能够更好掌握责任链落地并在日常的开发中积极的使用。
3.1 Spring Interceptor
3.1.1 Interceptor介绍
-
Spring中的拦截器(Interceptor) 用于拦截控制器方法的执行,可以在方法执行前后添加自定义逻辑类似于AOP编程思想。
-
Inteceptor的作用时机是在请求(request)进入servlet后,在进入Controller之前进行预处理。
-
Inteceptor的实际应用包括:认证授权、日志记录、字符编码转换,敏感词过滤等等。
-
Inteceptor中责任链的实现会从处理器的介绍,责任链的构建以及责任链的执行三个角度进行阐述。
3.1.2 处理器介绍
public interface HandlerInterceptor {boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
}@Component
public class TimeInterceptor extends HandlerInterceptorAdapter {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 前置处理System.out.println("time interceptor preHandle");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// 后置处理System.out.println("time interceptor postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("time interceptor afterCompletion");}
}
说明:
-
处理器Interceptor的接口HandlerInterceptor定义了三个方法,可在控制器方法执行前后添加自定义逻辑。
-
自定义处理器如上的TimeInterceptor需要自定义实现上述3个方法实现自我的逻辑。
-
所有的自定义处理会串联在HandlerExecutionChain类实现的责任链上。
3.1.3 责任链构建
public class HandlerExecutionChain {private final Object handler;private HandlerInterceptor[] interceptors;private List<HandlerInterceptor> interceptorList;private int interceptorIndex = -1;public void addInterceptor(HandlerInterceptor interceptor) {// 添加拦截器initInterceptorList().add(interceptor);}public void addInterceptors(HandlerInterceptor... interceptors) {if (!ObjectUtils.isEmpty(interceptors)) {CollectionUtils.mergeArrayIntoCollection(interceptors, initInterceptorList());}}private List<HandlerInterceptor> initInterceptorList() {if (this.interceptorList == null) {this.interceptorList = new ArrayList<HandlerInterceptor>();if (this.interceptors != null) {// An interceptor array specified through the constructorCollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);}}this.interceptors = null;return this.interceptorList;}
}
说明:
-
HandlerExecutionChain类作为串联Interceptor处理器的责任链负责责任链的构建和执行。
-
HandlerExecutionChain类通过集合对象interceptorList保存所有相关的处理器对象。
3.1.4 责任链执行
public class DispatcherServlet extends FrameworkServlet {protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {try {try {// mappedHandler代表的是HandlerExecutionChain责任链 mappedHandler = getHandler(processedRequest);HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 1、执行mappedHandler的applyPreHandle方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 2、执行controller的执行逻辑mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);// 执行mappedHandler的applyPostHandle方法mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {}processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {}finally {}}
}public class HandlerExecutionChain {private final Object handler;private HandlerInterceptor[] interceptors;private List<HandlerInterceptor> interceptorList;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, 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);}}}
}
说明:
-
在servlet的doDispatch方法中依次触发责任链的applyPreHandle的前置处理方法、applyPostHandle的后置处理方法。
-
前置处理方法applyPreHandle会遍历责任链上的处理器从前往后依次处理,后置处理方法applyPostHandle会遍历责任链上的处理器从后往前依次处理。
-
处理器的驱动由责任链对象负责依次触发,非处理器对象自驱执行。
3.2 Servlet Filter
3.2.1 Filter介绍
-
Servlet过滤器是在Java Servlet规范2.3中定义的,它能够对Servlet容器的请求和响应对象进行检查和修改,是个典型的责任链。
-
在Servlet被调用之前检查Request对象并支持修改Request Header和Request内容。
-
在Servlet被调用之后检查Response对象并支修改Response Header和Response内容。
3.2.2 处理器介绍
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();
}public class TimeFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("time filter init");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {// 1、执行处理的逻辑System.out.println("time filter doFilter");// 2、执行责任链当中的下一个 Filter 对象,等价于执行 FilterChain 的internalDoFilter方法filterChain.doFilter(servletRequest, servletResponse);}
}
说明:
-
Servlet过滤器类要实现javax.servlet.Filter接口,该接口定义了通用的3个方法。
-
init方法:负责Servlet过滤器的初始化方法,Servlet容器创建Servlet过滤器实例过程中调用这个方法。
-
doFilter方法:当客户请求访问与过滤器关联的URL时,Servlet容器会调用该方法。
-
destroy方法:Servlet容器在销毁过滤器实例前调用该方法,可以释放过滤器占用的资源。
3.2.3 责任链构建
public final class ApplicationFilterChain implements FilterChain {// 责任链上 Filter 的维护对象private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];//责任链上待执行的 Filter 对象private int pos = 0;// 责任链上拥有的 Filter 数量private int n = 0;void addFilter(ApplicationFilterConfig filterConfig) {// 避免重复添加Filterfor(ApplicationFilterConfig filter:filters)if(filter==filterConfig)return;// 按需进行扩容if (n == filters.length) {ApplicationFilterConfig[] newFilters =new ApplicationFilterConfig[n + INCREMENT];System.arraycopy(filters, 0, newFilters, 0, n);filters = newFilters;}// 保存Filter 对象filters[n++] = filterConfig;}
}
说明:
-
ApplicationFilterChain作为Filter的责任链,负责责任链的构建和执行。
-
责任链通过ApplicationFilterConfig类型的数组对象filters保存Filter处理器。
-
责任链上处理器的添加通过保存到数组filters来实现。
3.2.4 责任链执行
public final class ApplicationFilterChain implements FilterChain {// 责任链上 Filter 的维护对象private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];//责任链上待执行的 Filter 对象private int pos = 0;// 责任链上拥有的 Filter 数量private int n = 0;// 责任链的执行private void internalDoFilter(ServletRequest request,ServletResponse response)throws IOException, ServletException {// 在责任链未执行完的情况下执行责任链 if (pos < n) {// 获取当前待执行的 Filter,同时递增下一次待执行责任链的下标ApplicationFilterConfig filterConfig = filters[pos++];try {Filter filter = filterConfig.getFilter();if( Globals.IS_SECURITY_ENABLED ) {// 省略相关代码} else {filter.doFilter(request, response, this);}} catch (Throwable e) {}return;}try {if ((request instanceof HttpServletRequest) &&(response instanceof HttpServletResponse) &&Globals.IS_SECURITY_ENABLED ) {// 执行正常的业务逻辑} else {servlet.service(request, response);}} catch (Throwable e) {e = ExceptionUtils.unwrapInvocationTargetException(e);throw new ServletException(sm.getString("filterChain.servlet"), e);}}
}
说明:
-
整个责任链上Filter处理器的执行通过处理器自驱进行实现,而非由责任链对象驱动。
-
Filter处理器的在处理过程中除了执行自我逻辑,会通过 filterChain.doFilter(servletRequest, servletResponse) 触发下一个处理器的执行。
3.3 Dubbo
3.3.1 Dubbo Filter介绍
图片分享自《DUBBO官网》
-
Dubbo的Filter作用时机如上图所示,Filter实现是专门为服务提供方和服务消费方调用过程进行拦截,Dubbo本身的大多功能均基于此扩展点实现,每次远程方法执行该拦截都会被执行。
-
Dubbo官方针对Filter做了很多的原生支持,目前大致有20来个吧,包括我们熟知的RpcContext,accesslog功能都是通过filter来实现了。
-
在实际业务开发中会对Filter接口进行扩展,在服务调用链路中嵌入我们自身的处理逻辑,如日志打印、调用耗时统计等。
3.3.2 处理器介绍
@Activate(group = PROVIDER, value = ACCESS_LOG_KEY)
public class AccessLogFilter implements Filter {@Overridepublic Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {try {if (ConfigUtils.isNotEmpty(accessLogKey)) {AccessLogData logData = buildAccessLogData(invoker, inv);log(accessLogKey, logData);}} catch (Throwable t) {}// 执行下一个invokerreturn invoker.invoke(inv);}
}
说明:
-
Dubbo中的自定义Filter需要实现org.apache.dubbo.rpc.Filter类,内部通过实现invoke方法来实现自定义逻辑。
-
自定义Filter内部除了实现必要的自定义逻辑外,核心的需要通过invoker.invoke(inv)触发下一个过滤器的执行。
3.3.3 责任链构建
public class ProtocolFilterWrapper implements Protocol {private final Protocol protocol;public ProtocolFilterWrapper(Protocol protocol) {this.protocol = protocol;}private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {// 最后的 Invoker 对象Invoker<T> last = invoker;// 遍历所有 Filter 对象,构建责任链 List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);if (!filters.isEmpty()) {for (int i = filters.size() - 1; i >= 0; i--) {// 每个 Filter 封装成一个 Invoker 对象,通过 filter.invoke进行串联final Filter filter = filters.get(i);final Invoker<T> next = last;last = new Invoker<T>() {@Overridepublic Result invoke(Invocation invocation) throws RpcException {return filter.invoke(next, invocation);}};}}return last;}
}// 封装了Filter的invoker对象
static final class ProtocolFilterWrapper.1 implements Invoker < T > {final Invoker val$invoker;final Filter val$filter;// 指向下一个Invoker的变量final Invoker val$next;public Result invoke(Invocation invocation) throws RpcException {return this.val$filter.invoke(this.val$next, invocation);}ProtocolFilterWrapper.1(Invoker invoker, Filter filter, Invoker invoker2) {this.val$invoker = invoker;this.val$filter = filter;this.val$next = invoker2;}
}
说明:
-
ProtocolFilterWrapper 通过 buildInvokerChain 构建 Dubbo Filter 的责任链。
-
责任链上的处理器对象是将 Filter 封装的 Invoker 对象,每个 Invoker 对象指向下一个处理器封装的Invoker对象。
3.3.4 责任链执行
public class FailfastClusterInvoker<T> extends AbstractClusterInvoker<T> {public FailfastClusterInvoker(Directory<T> directory) {super(directory);}@Overridepublic Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {checkInvokers(invokers, invocation);Invoker<T> invoker = select(loadbalance, invocation, invokers, null);try {// 执行封装了Filter的invoker对象,驱动处理器的执行return invoker.invoke(invocation);} catch (Throwable e) {}}
}static final class ProtocolFilterWrapper.1 implements Invoker < T > {final Invoker val$invoker;final Filter val$filter;final Invoker val$next;public Result invoke(Invocation invocation) throws RpcException {return this.val$filter.invoke(this.val$next, invocation);}ProtocolFilterWrapper.1(Invoker invoker, Filter filter, Invoker invoker2) {this.val$invoker = invoker;this.val$filter = filter;this.val$next = invoker2;}
说明:
-
每个Invoker对象invoke方法会执行自定义逻辑,并触发下一个处理器的执行。
-
整个责任链上处理器的执行通过Invoker对象的驱动,而非责任链对象的驱动。
3.4 Sentinel
3.4.1 Sentinel Slot介绍
图片分享自《Sentinel官网》
-
Sentinel是面向分布式服务架构的流量治理组件,以流量为切入点提供熔断限流的功能保证系统的稳定性。
-
Sentinel 里面以Entry作为限流的资源对象,每个Entry创建的同时会关联一系列功能插槽(slot chain)。
-
Sentinel提供了通用的原生Slot处理不同的逻辑,同时支持自定义Slot来定制功能。
3.4.2 处理器介绍
public interface ProcessorSlot<T> {void entry(Context context, ResourceWrapper resourceWrapper, T param, int count, boolean prioritized,Object... args) throws Throwable;void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized,Object... args) throws Throwable;void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);void fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);
}public abstract class AbstractLinkedProcessorSlot<T> implements ProcessorSlot<T> {private AbstractLinkedProcessorSlot<?> next = null;@Overridepublic void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)throws Throwable {// 触发下一个处理器对象的处理if (next != null) {next.transformEntry(context, resourceWrapper, obj, count, prioritized, args);}}void transformEntry(Context context, ResourceWrapper resourceWrapper, Object o, int count, boolean prioritized, Object... args)throws Throwable {T t = (T)o;// 执行具体处理器的逻辑,由具体的处理器自行实现entry(context, resourceWrapper, t, count, prioritized, args);}public void setNext(AbstractLinkedProcessorSlot<?> next) {// 绑定下一个处理器的逻辑this.next = next;}
}public class NodeSelectorSlot extends AbstractLinkedProcessorSlot<Object> {private volatile Map<String, DefaultNode> map = new HashMap<String, DefaultNode>(10);@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)throws Throwable {// 1、处理器处理本身的逻辑DefaultNode node = map.get(context.getName());context.setCurNode(node);// 2、处理器驱动触发下一个处理器fireEntry(context, resourceWrapper, node, count, prioritized, args);}
}
说明:
-
Sentinel 中的 Slot 需要实现 com.alibaba.csp.sentinel.slotchain.ProcessorSlot 的通用接口。
-
自定义 Slot 一般继承抽象类 AbstractLinkedProcessorSlot 且只要改写 entry/exit 方法实现自定义逻辑。
-
Slot 通过 next 变量保存下一个处理器Slot对象。
-
在自定义实现的 entry 方法中需要通过 fireEntry 触发下一个处理器的执行,在 exit 方法中通过fireExit 触发下一个处理器的执行。
3.4.3 责任链构建
public class DefaultSlotChainBuilder implements SlotChainBuilder {@Overridepublic ProcessorSlotChain build() {// 责任链的头部对象ProcessorSlotChainProcessorSlotChain chain = new DefaultProcessorSlotChain();// sortedSlotList获取所有的处理器对象List<ProcessorSlot> sortedSlotList = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted();for (ProcessorSlot slot : sortedSlotList) {if (!(slot instanceof AbstractLinkedProcessorSlot)) {continue;}// 通过尾添法将职责slot添加到DefaultProcessorSlotChain当中chain.addLast((AbstractLinkedProcessorSlot<?>) slot);}return chain;}
}public class DefaultProcessorSlotChain extends ProcessorSlotChain {// 创建DefaultProcessorSlotChain的头尾节点first和endAbstractLinkedProcessorSlot<?> first = new AbstractLinkedProcessorSlot<Object>() {@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)throws Throwable {super.fireEntry(context, resourceWrapper, t, count, prioritized, args);}@Overridepublic void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {super.fireExit(context, resourceWrapper, count, args);}};AbstractLinkedProcessorSlot<?> end = first;@Overridepublic void addLast(AbstractLinkedProcessorSlot<?> protocolProcessor) {end.setNext(protocolProcessor);end = protocolProcessor;}
}
说明:
-
ProcessorSlotChain作为Slot的责任链,负责责任链的构建和执行。
-
责任链上的处理器对象 AbstractLinkedProcessorSlot 通过保存指向下一个处理器的对象的进行关联,整体以链表的形式进行串联。
-
责任链上的第一个处理器对象first本身不起任何作用,只是保存链表的头部。
3.4.4 责任链执行
public class CtSph implements Sph {private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)throws BlockException {Context context = ContextUtil.getContext();// 省略相关代码ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);Entry e = new CtEntry(resourceWrapper, chain, context);// 驱动责任链上的第一个处理器,进而由处理器自驱动执行下一个处理器chain.entry(context, resourceWrapper, null, count, prioritized, args);return e;}
}public class DefaultProcessorSlotChain extends ProcessorSlotChain {// 创建DefaultProcessorSlotChain的头尾节点first和endAbstractLinkedProcessorSlot<?> first = new AbstractLinkedProcessorSlot<Object>() {@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)throws Throwable {super.fireEntry(context, resourceWrapper, t, count, prioritized, args);}@Overridepublic void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {super.fireExit(context, resourceWrapper, count, args);}};AbstractLinkedProcessorSlot<?> end = first;@Overridepublic void addLast(AbstractLinkedProcessorSlot<?> protocolProcessor) {end.setNext(protocolProcessor);end = protocolProcessor;}
}public abstract class AbstractLinkedProcessorSlot<T> implements ProcessorSlot<T> {private AbstractLinkedProcessorSlot<?> next = null;@Overridepublic void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)throws Throwable {// 触发下一个处理器对象的处理if (next != null) {next.transformEntry(context, resourceWrapper, obj, count, prioritized, args);}}void transformEntry(Context context, ResourceWrapper resourceWrapper, Object o, int count, boolean prioritized, Object... args)throws Throwable {T t = (T)o;// 执行具体处理器的逻辑,由具体的处理器自行实现entry(context, resourceWrapper, t, count, prioritized, args);}public void setNext(AbstractLinkedProcessorSlot<?> next) {// 绑定下一个处理器的逻辑this.next = next;}
}
说明:
-
整个责任链上处理器的执行通过Invoker对象的驱动,而非责任链对象的驱动。
-
DefaultProcessorSlotChain的entry首先头部对象first,进而触发处理器的自驱实现处理器的执行。
-
整体按照entry → fireEntry → transformEntry → entry 的循环顺序依次触发处理器的自驱。
四、实践总结
在日常项目实践中,责任链的设计模式会在很多业务场景中落地。
譬如对于支持用户生成内容(UGC)的应用来说,用户生成的内容可能包含一些敏感内容如敏感言论或者图片等。针对这种应用场景,可以通过责任链模式设置多个处理器来处理不同的任务,如文本过滤器处理敏感词,图片过滤器处理敏感图片等等。
譬如对于电商服务中的下单流程来说,一个下单流程包含订单拆合单,优惠计算,订单生成等多个步骤,我们可以通过责任链模式设置多个处理器来处理不同的任务等等。
责任链的应用场景非常广泛,在常见的开源框架中有丰富的落地场景,同样在业务开发中也可以根据场景灵活使用。
相关文章:

开源框架中的责任链模式实践
作者:vivo 互联网服务器团队-Wang Zhi 责任链模式作为常用的设计模式而被大家熟知和使用。本文介绍责任链的常见实现方式,并结合开源框架如Dubbo、Sentinel等进行延伸探讨。 一、责任链介绍 在GoF 的《设计模式》一书中对责任链模定义的:将…...

智能配电系统:保障电力运行安全、可控与高效
智能配电系统是一种先进的电力分配技术,它通过智能化、数字化和网络化等方式,有效地保障了电力运行的安全、可控和高效。 力安科技智能配电系统是在配电室(含高压柜、变压器、低压柜)、箱式变电站、配电箱及动力柜(…...
MySQL学习系列(11)-每天学习10个知识
目录 1. 数据库设计的关键因素2. 使用存储过程和函数来提高性能和可重用性3. MySQL性能优化4. 使用视图简化查询和提供数据安全性5. 数据库备份和恢复策略的重要性和实践经验6. 在分布式系统中保证数据一致性和可用性7. 理解MySQL的复制和其在实际应用中的作用8. 使用游标进行分…...

如何通过Gunicorn和Niginx部署Django
本文主要介绍如何配置Niginx加载Django的静态资源文件,也就是Static 1、首先需要将Django项目中的Settings.py 文件中的两个参数做以下设置: STATIC_URL /static/ STATIC_ROOT os.path.join(BASE_DIR, static) 然后在宝塔面板中执行python manage.…...

C语言 cortex-A7核UART总线实验
一、C 1)uart4.h #ifndef __UART4_H__ #define __UART4_H__ #include "stm32mp1xx_rcc.h" #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_uart.h&quo…...
asp.net C#免费反编译工具ILSpy
在维护一个没有源码的C#项目,只能反编译了。 项目主页 https://github.com/icsharpcode/ILSpy 使用方法 中文界面使用简单,把你要反编译的dll拖过去就可以了。好使!!!...

演讲实录:DataFun 垂直开发者社区基于指标平台自主洞察北极星指标
在7月14日举办的 Kyligence 用户大会的数智新应用论坛上,DataFun COO 杜颖女士为大家带来了《垂直开发者社区基于指标平台自主洞察北极星指标》的主题演讲。接下来,我们一起看看 DataFun 如何在没有专门的 IT 团队的情况下,实现对北极星指标的…...

ffmpeg编译 Error: operand type mismatch for `shr‘
错误如下: D:\msys2\tmp\ccUxvBjQ.s: Assembler messages: D:\msys2\tmp\ccUxvBjQ.s:345: Error: operand type mismatch for shr D:\msys2\tmp\ccUxvBjQ.s:410: Error: operand type mismatch for shr D:\msys2\tmp\ccUxvBjQ.s:470: Error: operand type mismatch…...

【Windows Server 2012 R2搭建FTP站点】
打开服务器管理器——添加角色和功能 下一步 下一步 下一步 选择FTP服务器,勾上FTP服务和FTP扩展,点击下一步 安装 安装完成关闭 打开我们的IIS服务器 在WIN-XXX主页可以看到我们的FTP相关菜单 右键WIN-XXXX主页,添加FTP站点 输入站点名称-FT…...

python教程:使用gevent实现高并发并限制最大并发数
嗨喽~大家好呀,这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 import time import gevent from gevent.pool import Pool from gevent import monkey # 一,定义最大并发数 p Pool(20) # 二,导入gevent…...
借助reCAPTCHA实现JavaScript验证码功能
前言 验证码(CAPTCHA)是一种常见的安全验证机制,常用于区分真实用户和机器人。使用验证码可以有效防止恶意登录、自动注册或者密码爆破等攻击。本文将借助reCAPTCHA第三方库来实现JavaScript验证码功能。 验证码的原理 验证码的核心思想是要…...

监控数据的采集方式及原理
采集方法使用频率从高到低依次是读取 /proc目录、执行命令行工具、远程黑盒探测、拉取特定协议的数据、连接到目标对象执行命令、代码埋点、日志解析。 读取 /proc目录 /proc是一个位于内存中的伪文件系统,而在该目录下保存的不是真正的文件和目录,而是…...

Vue路由与node.js环境搭建
目录 前言 一.Vue路由 1.什么是spa 1.1简介 1.2 spa的特点 1.3 spa的优势以及未来的挑战 2.路由的使用 2.1 导入JS依赖 2.2 定义两个组件 2.3 定义组件与路径对应关系 2.4 通过路由关系获取路由对象 2.5 将对象挂载到vue实例中 2.6 定义触发路由事件的按钮 2.7 定…...

腾讯云16核服务器性能测评_轻量和CVM配置大全
腾讯云16核服务器配置大全,CVM云服务器可选择标准型S6、标准型SA3、计算型C6或标准型S5等,目前标准型S5云服务器有优惠活动,性价比高,计算型C6云服务器16核性能更高,轻量16核32G28M带宽优惠价3468元15个月,…...

Postman应用——下载注册和登录
文章目录 下载安装注册登录注册账号登录账号 下载安装 Postman下载:https://www.postman.com/ 访问链接后,进入首页,根据自己的操作系统下载对应的版本。 找到下载到的目录直接双击.exe文件,会默认安装在C盘,安装完会…...
uni-app混合开发 navigateTo、reLaunch、redirectTo、switchTab区别
1.navigateTo 保留当前页面,跳转到应用内的某个页面,使用uni.navigateBack可以返回到原页面。 要注意的是navigateTo只能跳转的应用内非 tabBar 的页面的路径 , 路径后可以带参数;如果跳转url参数为tabBar的路径则无法进行跳转 2.redir…...

专业软件测评中心:关于软件性能测试的实用建议
软件性能测试是通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试。性能测试在软件的质量保证中起着重要的作用,它包括的测试内容丰富多样。 一、软件性能测试的实用建议 1、制定清晰的测试目标:明确测试目标…...

vue项目通过json-bigint在前端处理java雪花id过长导致失去精度问题
这里 我简单模仿了一个接口 这里 我单纯 返回一个long类型的雪花id 然后 前端 用 axios 去请求 大家知道 axios 会对请求数据做一次处理 而我们 data才是拿到我们java这边实际返回的东西 简单说 就是输出一下我们后端返回 的内容 这里 我们网络中显示的是 35866101868095488…...

【全志V3s】SPI NAND Flash 驱动开发
文章目录 一、硬件介绍V3s的启动顺序 二、驱动支持U-Boot驱动主线 Linux 驱动已经支持 三、烧录工具 xfel四、构建U-Boot(官方的Uboot)先编译一下开始spi nand flash 代码层面的适配修改menuconfig配置ARM architecture配置Support for SPI Nand Flash o…...

【二叉树】二叉树展开为链表-力扣 114 题
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...

Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...

wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...

PH热榜 | 2025-06-08
1. Thiings 标语:一套超过1900个免费AI生成的3D图标集合 介绍:Thiings是一个不断扩展的免费AI生成3D图标库,目前已有超过1900个图标。你可以按照主题浏览,生成自己的图标,或者下载整个图标集。所有图标都可以在个人或…...

Spring AI中使用ChatMemory实现会话记忆功能
文章目录 1、需求2、ChatMemory中消息的存储位置3、实现步骤1、引入依赖2、配置Spring AI3、配置chatmemory4、java层传递conversaionId 4、验证5、完整代码6、参考文档 1、需求 我们知道大型语言模型 (LLM) 是无状态的,这就意味着他们不会保…...