设计模式(责任链模式)
责任链模式
模板模式、策略模式和责任链模式,这三种模式具有相同的作用:复用和扩展,在实际的项目开发中比较常用,特别是框架开发中,我们可以利用它们来提供框架的扩展点,能够让框架的使用者在不修改框架源码的情况下,基于扩展点定制化框架的功能。
这篇文章主要讲解责任链模式的原理和实现。除此之外还会贴合实战,通过剖析Servlet Filter、Spring Interceptor来看,如利用责任链模式实现框架中常用的过滤器、拦截器。
原理
职责链模式的英文翻译是Chain Of Responsibility Design Pattern。在GoF的《设计模式》中,它是这么定义的:
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.
翻译成中文就是:将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。
这么说比较抽象,我用更加容易理解的话来进一步解读一下。
在职责链模式中,多个处理器(也就是刚刚定义中说的“接收对象”)依次处理同一个请求。一个请求先经过A处理器处理,然后再把请求传递给B处理器,B处理器处理完后再传递给C处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫作职责链模式。
实现
关于职责链模式,我们先来看看它的代码实现。结合代码实现,你会更容易理解它的定义。职责链模式有多种实现方式,我们这里介绍两种比较常用的。
第一种实现方式如下所示。其中,Handler是所有处理器类的抽象父类,handle()是抽象方法。每个具体的处理器类(HandlerA、HandlerB)的handle()函数的代码结构类似,如果它能处理该请求,就不继续往下传递;如果不能处理,则交由后面的处理器来处理(也就是调用successor.handle())。HandlerChain是处理器链,从数据结构的角度来看,它就是一个记录了链头、链尾的链表。其中,记录链尾是为了方便添加处理器。
public abstract class Handler {protected Handler successor = null;public void setSuccessor(Handler successor) {this.successor = successor;}public abstract void handle();
}public class Handler extends Handler {@Overridepublic void handle() {boolean handled = false;//...if (!handled && successor != null) {successor.handle();}}
}public class HandlerB extends Handler {@Overridepublic void handle() {boolean handled = false;//...if (!handled && successor != null) {successor.handle();} }
}public class HandlerChain {private Handler head = null;private Handler tail = null;public void addHandler(Handler 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();}}
}// 使用举例
public class Application {public static void main(String[] args) {HandlerChain chain = new HandlerChain();chain.addHandler(new HandlerA());chain.addHandler(new HandlerB());chain.handle();}
}
实际上,上面的代码实现不够优雅。处理器类的handle()函数,不仅包含自己的业务逻辑,还包含对下一个处理器的调用,也就是代码中的successor.handle()。一个不熟悉这种代码结构的程序员,在添加新的处理器类的时候,很有可能忘记在handle()函数中调用successor.handle(),这就会导致代码出现bug。
针对这个问题,我们对代码进行重构,利用模板模式,将调用successor.handle()的逻辑从具体的处理器类中剥离出来,放到抽象父类中。这样具体的处理器类只需要实现自己的业务逻辑就可以了。重构之后的代码如下所示:
public 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();}}protected abstract boolean doHandle();
}public class HandlerA extends Handler {@Overrideprotected boolean doHandle() {boolean handled = false;//...return handled;}
}public class HandlerB extends Handler {@Overrideprotected boolean doHandle() {boolean handled = false;//...return handled;}
}// HandlerChain和Application代码不变
框架中的责任链模式
职责链模式常用在框架的开发中,为框架提供扩展点,让框架的使用者在不修改框架源码的情况下,基于扩展点添加新的功能。实际上,更具体点来说,职责链模式最常用来开发框架的过滤器和拦截器。今天,我们就通过Servlet Filter、Spring Interceptor这两个Java开发中常用的组件,来具体讲讲它在框架开发中的应用。
Servlet责任链:
/** Licensed to the Apache Software Foundation (ASF) under one or more* contributor license agreements. See the NOTICE file distributed with* this work for additional information regarding copyright ownership.* The ASF licenses this file to You under the Apache License, Version 2.0* (the "License"); you may not use this file except in compliance with* the License. You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package org.apache.catalina.core;import java.io.IOException;
import java.security.Principal;
import java.security.PrivilegedActionException;
import java.util.Set;import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.apache.catalina.Globals;
import org.apache.catalina.security.SecurityUtil;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.res.StringManager;/*** Implementation of <code>javax.servlet.FilterChain</code> used to manage the execution of a set of filters for a* particular request. When the set of defined filters has all been executed, the next call to <code>doFilter()</code>* will execute the servlet's <code>service()</code> method itself.** @author Craig R. McClanahan*/
public final class ApplicationFilterChain implements FilterChain {// Used to enforce requirements of SRV.8.2 / SRV.14.2.5.1private static final ThreadLocal<ServletRequest> lastServicedRequest;private static final ThreadLocal<ServletResponse> lastServicedResponse;static {if (ApplicationDispatcher.WRAP_SAME_OBJECT) {lastServicedRequest = new ThreadLocal<>();lastServicedResponse = new ThreadLocal<>();} else {lastServicedRequest = null;lastServicedResponse = null;}}// -------------------------------------------------------------- Constantspublic static final int INCREMENT = 10;// ----------------------------------------------------- Instance Variables/*** Filters.*/private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];/*** The int which is used to maintain the current position in the filter chain.*/private int pos = 0;/*** The int which gives the current number of filters in the chain.*/private int n = 0;/*** The servlet instance to be executed by this chain.*/private Servlet servlet = null;/*** Does the associated servlet instance support async processing?*/private boolean servletSupportsAsync = false;/*** The string manager for our package.*/private static final StringManager sm = StringManager.getManager(ApplicationFilterChain.class);/*** Static class array used when the SecurityManager is turned on and <code>doFilter</code> is invoked.*/private static final Class<?>[] classType =new Class[] { ServletRequest.class, ServletResponse.class, FilterChain.class };/*** Static class array used when the SecurityManager is turned on and <code>service</code> is invoked.*/private static final Class<?>[] classTypeUsedInService =new Class[] { ServletRequest.class, ServletResponse.class };// ---------------------------------------------------- FilterChain Methods/*** Invoke the next filter in this chain, passing the specified request and response. If there are no more filters in* this chain, invoke the <code>service()</code> method of the servlet itself.** @param request The servlet request we are processing* @param response The servlet response we are creating** @exception IOException if an input/output error occurs* @exception ServletException if a servlet exception occurs*/@Overridepublic 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((java.security.PrivilegedExceptionAction<Void>) () -> {internalDoFilter(req, res);return null;});} catch (PrivilegedActionException pe) {Exception e = pe.getException();if (e instanceof ServletException) {throw (ServletException) e;} else if (e instanceof IOException) {throw (IOException) e;} else if (e instanceof RuntimeException) {throw (RuntimeException) e;} else {throw new ServletException(e.getMessage(), e);}}} else {internalDoFilter(request, response);}}private void internalDoFilter(ServletRequest request, ServletResponse response)throws IOException, ServletException {// Call the next filter if there is oneif (pos < n) {ApplicationFilterConfig filterConfig = filters[pos++];try {Filter filter = filterConfig.getFilter();if (request.isAsyncSupported() &&"false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);}if (Globals.IS_SECURITY_ENABLED) {final ServletRequest req = request;final ServletResponse res = response;Principal principal = ((HttpServletRequest) req).getUserPrincipal();Object[] args = new Object[] { req, res, this };SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);} else {filter.doFilter(request, response, this);}} catch (IOException | ServletException | RuntimeException e) {throw e;} catch (Throwable e) {e = ExceptionUtils.unwrapInvocationTargetException(e);ExceptionUtils.handleThrowable(e);throw new ServletException(sm.getString("filterChain.filter"), e);}return;}// We fell off the end of the chain -- call the servlet instancetry {if (ApplicationDispatcher.WRAP_SAME_OBJECT) {lastServicedRequest.set(request);lastServicedResponse.set(response);}if (request.isAsyncSupported() && !servletSupportsAsync) {request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);}// Use potentially wrapped request from this pointif ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse) &&Globals.IS_SECURITY_ENABLED) {final ServletRequest req = request;final ServletResponse res = response;Principal principal = ((HttpServletRequest) req).getUserPrincipal();Object[] args = new Object[] { req, res };SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args, principal);} else {servlet.service(request, response);}} catch (IOException | ServletException | RuntimeException e) {throw e;} catch (Throwable e) {e = ExceptionUtils.unwrapInvocationTargetException(e);ExceptionUtils.handleThrowable(e);throw new ServletException(sm.getString("filterChain.servlet"), e);} finally {if (ApplicationDispatcher.WRAP_SAME_OBJECT) {lastServicedRequest.set(null);lastServicedResponse.set(null);}}}/*** The last request passed to a servlet for servicing from the current thread.** @return The last request to be serviced.*/public static ServletRequest getLastServicedRequest() {return lastServicedRequest.get();}/*** The last response passed to a servlet for servicing from the current thread.** @return The last response to be serviced.*/public static ServletResponse getLastServicedResponse() {return lastServicedResponse.get();}// -------------------------------------------------------- Package Methods/*** Add a filter to the set of filters that will be executed in this chain.** @param filterConfig The FilterConfig for the servlet to be executed*/void addFilter(ApplicationFilterConfig filterConfig) {// Prevent the same filter being added multiple timesfor (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;}filters[n++] = filterConfig;}/*** Release references to the filters and wrapper executed by this chain.*/void release() {for (int i = 0; i < n; i++) {filters[i] = null;}n = 0;pos = 0;servlet = null;servletSupportsAsync = false;}/*** Prepare for reuse of the filters and wrapper executed by this chain.*/void reuse() {pos = 0;}/*** Set the servlet that will be executed at the end of this chain.** @param servlet The Wrapper for the servlet to be executed*/void setServlet(Servlet servlet) {this.servlet = servlet;}void setServletSupportsAsync(boolean servletSupportsAsync) {this.servletSupportsAsync = servletSupportsAsync;}/*** Identifies the Filters, if any, in this FilterChain that do not support async.** @param result The Set to which the fully qualified class names of each Filter in this FilterChain that does not* support async will be added*/public void findNonAsyncFilters(Set<String> result) {for (int i = 0; i < n; i++) {ApplicationFilterConfig filter = filters[i];if ("false".equalsIgnoreCase(filter.getFilterDef().getAsyncSupported())) {result.add(filter.getFilterClass());}}}
}
Spring拦截器:
/** Copyright 2002-2020 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.web.servlet;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;/*** Handler execution chain, consisting of handler object and any handler interceptors.* Returned by HandlerMapping's {@link HandlerMapping#getHandler} method.** @author Juergen Hoeller* @since 20.06.2003* @see HandlerInterceptor*/
public class HandlerExecutionChain {private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);private final Object handler;private final List<HandlerInterceptor> interceptorList = new ArrayList<>();private int interceptorIndex = -1;/*** Create a new HandlerExecutionChain.* @param handler the handler object to execute*/public HandlerExecutionChain(Object handler) {this(handler, (HandlerInterceptor[]) null);}/*** Create a new HandlerExecutionChain.* @param handler the handler object to execute* @param interceptors the array of interceptors to apply* (in the given order) before the handler itself executes*/public HandlerExecutionChain(Object handler, @Nullable HandlerInterceptor... interceptors) {this(handler, (interceptors != null ? Arrays.asList(interceptors) : Collections.emptyList()));}/*** Create a new HandlerExecutionChain.* @param handler the handler object to execute* @param interceptorList the list of interceptors to apply* (in the given order) before the handler itself executes* @since 5.3*/public HandlerExecutionChain(Object handler, List<HandlerInterceptor> interceptorList) {if (handler instanceof HandlerExecutionChain) {HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;this.handler = originalChain.getHandler();this.interceptorList.addAll(originalChain.interceptorList);}else {this.handler = handler;}this.interceptorList.addAll(interceptorList);}/*** Return the handler object to execute.*/public Object getHandler() {return this.handler;}/*** Add the given interceptor to the end of this chain.*/public void addInterceptor(HandlerInterceptor interceptor) {this.interceptorList.add(interceptor);}/*** Add the given interceptor at the specified index of this chain.* @since 5.2*/public void addInterceptor(int index, HandlerInterceptor interceptor) {this.interceptorList.add(index, interceptor);}/*** Add the given interceptors to the end of this chain.*/public void addInterceptors(HandlerInterceptor... interceptors) {CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);}/*** Return the array of interceptors to apply (in the given order).* @return the array of HandlerInterceptors instances (may be {@code null})*/@Nullablepublic HandlerInterceptor[] getInterceptors() {return (!this.interceptorList.isEmpty() ? this.interceptorList.toArray(new HandlerInterceptor[0]) : null);}/*** Return the list of interceptors to apply (in the given order).* @return the list of HandlerInterceptors instances (potentially empty)* @since 5.3*/public List<HandlerInterceptor> getInterceptorList() {return (!this.interceptorList.isEmpty() ? Collections.unmodifiableList(this.interceptorList) :Collections.emptyList());}/*** Apply preHandle methods of registered interceptors.* @return {@code true} if the execution chain should proceed with the* next interceptor or the handler itself. Else, DispatcherServlet assumes* that this interceptor has already dealt with the response itself.*/boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {for (int i = 0; i < this.interceptorList.size(); i++) {HandlerInterceptor interceptor = this.interceptorList.get(i);if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}return true;}/*** Apply postHandle methods of registered interceptors.*/void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)throws Exception {for (int i = this.interceptorList.size() - 1; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptorList.get(i);interceptor.postHandle(request, response, this.handler, mv);}}/*** Trigger afterCompletion callbacks on the mapped HandlerInterceptors.* Will just invoke afterCompletion for all interceptors whose preHandle invocation* has successfully completed and returned true.*/void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptorList.get(i);try {interceptor.afterCompletion(request, response, this.handler, ex);}catch (Throwable ex2) {logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}}/*** Apply afterConcurrentHandlerStarted callback on mapped AsyncHandlerInterceptors.*/void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {for (int i = this.interceptorList.size() - 1; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptorList.get(i);if (interceptor instanceof AsyncHandlerInterceptor) {try {AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptor;asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);}catch (Throwable ex) {if (logger.isErrorEnabled()) {logger.error("Interceptor [" + interceptor + "] failed in afterConcurrentHandlingStarted", ex);}}}}}/*** Delegates to the handler's {@code toString()} implementation.*/@Overridepublic String toString() {return "HandlerExecutionChain with [" + getHandler() + "] and " + this.interceptorList.size() + " interceptors";}}
相关文章:
设计模式(责任链模式)
责任链模式 模板模式、策略模式和责任链模式,这三种模式具有相同的作用:复用和扩展,在实际的项目开发中比较常用,特别是框架开发中,我们可以利用它们来提供框架的扩展点,能够让框架的使用者在不修改框架源…...
【Mac-ML-DL】深度学习使用MPS出现内存泄露(leaked semaphore)以及张量转换错误
MPS加速修改总结 先说设备:MacBook Pro M4 24GB 事情的起因是我在进行深度学习的时候想尝试用苹果自带的MPS进行训练加速,修改设备后准备开始训练,但是出现如下报错: UserWarning: resource_tracker: There appear to be 1 leak…...
Hadoop集群部署教程-P5
Hadoop集群部署教程-P5 Hadoop集群部署教程(续) 第十七章:安全增强配置 17.1 认证与授权 Kerberos认证集成: # 生成keytab文件 kadmin -q "addprinc -randkey hdfs/masterEXAMPLE.COM" kadmin -q "xst -k hdfs.…...
Github 2FA(Two-Factor Authentication/两因素认证)
Github 2FA认证 多因素用户认证(Multi-Factor Authentication),基本上各个大互联网平台,尤其是云平台厂商(如:阿里云的MFA、华为云、腾讯云/QQ安全中心等)都有启用了,Github算是搞得比较晚些了。 双因素身…...
Spark大数据分析与实战笔记(第四章 Spark SQL结构化数据文件处理-05)
文章目录 每日一句正能量第4章 Spark SQL结构化数据文件处理章节概要4.5 Spark SQL操作数据源4.5.1 Spark SQL操作MySQL4.5.2 操作Hive数据集 每日一句正能量 努力学习,勤奋工作,让青春更加光彩。 第4章 Spark SQL结构化数据文件处理 章节概要 在很多情…...
使用 Azure AKS 保护 Kubernetes 部署的综合指南
企业不断寻求增强其软件开发和部署流程的方法。DevOps 一直是这一转型的基石,弥合了开发与运营之间的差距。然而,随着安全威胁日益复杂,将安全性集成到 DevOps 流水线(通常称为 DevSecOps)已变得势在必行。本指南深入探…...
遵守 Vue3 的单向数据流原则:父组件传递对象 + 子组件修改对象属性,安全地实现父子组件之间复杂对象的双向绑定示例代码及讲解
以下是针对 父组件传递对象 子组件修改对象属性 的完整示例代码,同时遵守 Vue3 的单向数据流原则: 1. 父组件代码 (ParentComponent.vue) vue <template><!-- 通过 v-model 传递整个对象 --><ChildComponent v-model"formData&qu…...
Unchained 内容全面上链,携手 Walrus 迈入去中心化媒体新时代
加密新闻媒体 Unchained — — 业内最受信赖的声音之一 — — 现已选择 Walrus 作为其去中心化存储解决方案,正式将其所有媒体内容(文章、播客和视频)上链存储。Walrus 将替代 Unchained 现有的中心化存储架构,接管其全部历史内容…...
20.3 使用技巧2
版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的 20.3.3 修改表头单元格 设置列表头单元格的内容: 一是可以通过 DataGridView.Columns[列号].HeaderCell来获得对应列的单…...
【Axure绘制原型】小图标使用技巧
获取小图标的网站:https://www.iconfont.cn/ 搜索相关图标 点击下载-复制SVG代码 回到Axure软件中粘贴,此时会显示出图片 在Axure软件中右键-变换图片-转换为形状 即可...
音视频之H.265/HEVC预测编码
H.265/HEVC系列文章: 1、音视频之H.265/HEVC编码框架及编码视频格式 2、音视频之H.265码流分析及解析 3、音视频之H.265/HEVC预测编码 预测编码是视频编码中的核心技术之一。对于视频信号来说,一幅图像内邻近像素之间有着较强的空间相关性,相邻图像之…...
无人机遥感与传统卫星遥感:谁更适合你的需求?
在对地观测领域,无人机遥感和卫星遥感是两种重要的技术手段,各自具有独特的技术原理、性能特点和应用优势。本文将从技术原理、性能特点和应用场景三个方面,对无人机遥感和卫星遥感进行系统对比,帮助读者全面了解两种技术的差异与…...
学习笔记—C++—模板初阶
目录 模板初阶 泛型编程 函数模板 模版概念 函数模版格式 模版的原理 函数模板的实例化 模版参数的匹配规则 类模板 模板初阶 泛型编程 使用函数重载虽然可以实现,但是有一下几个不好的地方: 1. 重载的函数仅仅是类型不同,代码复…...
【Python进阶】字典:高效键值存储的十大核心应用
目录 前言:技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解核心作用讲解关键技术模块技术选型对比 二、实战演示环境配置要求核心代码实现(10个案例)案例1:基础操作案例2:字典推导式…...
充电宝项目中集成地图地址解析功能梳理
文章目录 MongoDB数据库引入pom依赖配置yaml配置文件参考POJOXLocationRepositoryservice服务方法 腾讯地图接口申请api key配置api key启动类配置RestTemplate控制层服务层 MongoDB数据库 MongoDB对应经纬度的查询具体很好的支持. 引入pom依赖 <dependency><group…...
算法基础(以acwing讲述顺序为主,结合自己理解,持续更新中...)
文章目录 算法的定义一、基础算法排序二分高精度前缀和与差分双指针算法位运算离散化区间合并 算法的定义 这是我从百度上面搜的定义 算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系…...
栈实现队列
栈实现队列 用栈实现队列:C 语言代码解析栈的基本实现栈的初始化栈的销毁入栈操作检查栈是否为空出栈操作获取栈顶元素获取栈中元素个数 用栈实现队列队列的创建入队操作出队操作获取队首元素检查队列是否为空队列的销毁 总结 用栈实现队列:C 语言代码解…...
Redis原理与Windows环境部署实战指南:助力测试工程师优化Celery调试
引言 在分布式系统测试中,Celery作为异步任务队列常被用于模拟高并发场景。而Redis作为其核心消息代理,其性能和稳定性直接影响测试结果。本文将深入解析Redis的核心原理,主要讲解Windows环境部署redis,为测试工程师提供一套完整…...
HWDeviceDRM的三个子类,HWPeripheralDRM HWTVDRM HWVirtualDRM
在很多采用 DRM 架构的 Android 平台(尤其是 QTI 平台,比如 sdm / display-hal 模块中),HWDeviceDRM 是一个基类,抽象了所有类型的 Display 输出设备的共通 DRM 行为,而它有三个常见的子类,对应…...
金融 IC 卡 CCRC 认证:从合规到业务安全的升级路径
在金融科技飞速发展的当下,金融 IC 卡作为现代金融交易的重要载体,广泛应用于各类支付场景,从日常的购物消费到线上金融理财,其安全性直接关系到用户的资金安全和金融机构的稳定运营。CCRC(中国网络安全审查技术与认证…...
微硕WSP6949 MOS管在强排热水器中的应用与市场分析
微硕WSP6949 MOS管在强排热水器中的应用与市场分析 一、引言 强排热水器作为一种常见的家用电器,其核心部件之一是驱动电路,而MOS管作为驱动电路中的关键元件,其性能直接影响到热水器的运行效率和稳定性。微硕半导体推出的WSP6949 MOS管&am…...
文件操作(二进制文件)
C中对文件操作需要包含头文件 #include<fstream> 文件类型分为两类: 1. 文本文件:文件以文本对应的 ASCII 码形式存储在计算机中 2. 二进制文件:文件以文本的二进制形式存储在计算机中,用户一 般不能直接读懂 文件…...
ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(输入类外设之按键Button)
ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(输入类外设之按键Button) 版本信息: ESP-ADF v2.7-65-gcf908721 简介 本文档详细分析ESP-ADF中的输入类外设实现机制,包括按键(button)、触摸(touch)和ADC按键(a…...
HOW - 企业团队自建 npm 仓库
文章目录 一、明确需求二、选型:常用方案三、Verdaccio 搭建步骤1. 安装 Node.js 环境2. 全局安装 verdaccio3. 启动服务4. 配置(可选)5. 用户登录与发布四、团队使用方式1. 使用 `.npmrc` 文件统一配置2. 发布范围包(Scoped packages)五、权限控制六、进阶集成七、测试和…...
键值对和Map的区别
数组里存储键值对和使用Map(在不同语言里也被叫做字典、哈希表等)存在多方面的区别,下面从多个维度进行分析,同时给出C#和C的代码示例。 区别分析 1. 查找效率 数组存储键值对:查找特定键的值时,通常需要…...
CS61A:STRING REPRESENTATION
Python 规定所有对象都应该产生两种不同的字符串表示形式:一种是人类可解释的文本,另一种是 Python 可解释的表达式。字符串的构造函数 str 返回一个人类可读的字符串。在可能的情况下,repr 函数会返回一个计算结果相等的 Python 表达式。rep…...
AI编程新纪元:GitHub Copilot、CodeGeeX与VS2022的联合开发实践
引言:AI编程时代的到来 在软件开发领域,我们正站在一个历史性的转折点上。GitHub Copilot、CodeGeeX等AI编程助手的出现,结合Visual Studio 2022的强大功能,正在重塑代码编写的本质。这不仅是工具层面的革新,更是开发范式的根本转变。能够有效利用这些AI工具的开发者将跨…...
iOS崩溃堆栈分析
文章目录 一、背景二、获取崩溃日志三、使用 dSYM 文件符号化堆栈信息1. 准备 dSYM 文件2. 符号化方法使用 Xcode使用 atos 命令 一、背景 在 iOS 开发中,分析崩溃日志和堆栈信息是调试的重要环节。上线APP往往只能获取到堆栈信息无法获取到具体的崩溃日志…...
kafka服务端和springboot中使用
kafka服务端和springboot中使用 一、kafka-sever安装使用 下载kafka-server https://kafka.apache.org/downloads.html 启动zookeeper zookeeper-server-start.bat config\zookeeper.properties 启动kafka-server kafka-server-start.bat config\server.properties创建主…...
05-DevOps-Jenkins自动拉取构建代码
新建Gitlab仓库 先在Gitab上创建一个代码仓库,选择创建空白项目 安装说明进行填写,然后点击创建项目 创建好的仓库是空的,什么都没有 新建一个springboot项目,用于代码上传使用。 只是为了测试代码上传功能,所以代码…...
win7/win10/macos如何切换DNS,提升网络稳定性
本篇教程教您如何在Windows10、Windows8.1、Windows7、MacOS操作系统切换DNS,以提升系统的稳定性,获得更好的操作体验。 Windows10及Windows8.1 1、右键单击“此计算机”,然后选择“属性”。进入Windows系统界面后,选择左侧的“…...
【正点原子STM32MP257连载】第四章 ATK-DLMP257B功能测试——A35M33异核通信测试
1)实验平台:正点原子ATK-DLMP257B开发板 2)浏览产品:https://www.alientek.com/Product_Details/135.html 3)全套实验源码手册视频下载:正点原子资料下载中心 第四章 ATK-DLMP257B功能测试——A35&M33…...
maven如何解决jar包依赖冲突
maven如何解决jar包依赖冲突 1.背景2.报错信息3.解决思路3.1.查找jsqlparser冲突3.2.发现冲突3.2.解决冲突 4.Dromara Warm-Flow 1.背景 在ruoyi-vue项目集成Warm-Flow过程中,需要把mybatis升级为mybatis-plus,按照Warm-Flow常见问题中升级过程…...
过往记录系列 篇六:国家队护盘历史规律梳理
文章目录 系列文章护盘触发条件与时间规律护盘信号识别特征市场反应规律退出策略历史演变系列文章 过往记录系列 篇一:牛市板块轮动顺序梳理 过往记录系列 篇二:新年1月份(至春节前)行情历史梳理 过往记录系列 篇三:春节行情历史梳理 过往记录系列 篇四:年报月行情历史梳…...
string的模拟实现 (6)
目录 1.string.h 2.string.cpp 3.test.cpp 4.一些注意点 本篇博客就学习下如何模拟实现简易版的string类,学好string类后面学习其他容器也会更轻松些。 代码实现如下: 1.string.h #define _CRT_SECURE_NO_WARNINGS 1 #pragma once #include <…...
多模态思维链AI医疗编程:从计算可持续性到开放域推理的系统性解决方案
多模态思维链AI医疗编程:从计算可持续性到开放域推理的系统性解决方案 医疗AI领域的多模态思维链技术正在重塑临床决策支持、医学影像分析和医疗流程优化的范式。本指南从计算可持续性、错误传播控制、伦理安全防护和通用性扩展四大维度,系统解析医疗大模型落地落地的关键要…...
BTS7960 直流电机控制程序
/*************正转逻辑*****************/ LEN1 REN1 while() { LPWN0 DELAY LPWM1 DELAY } /************反转逻辑******************/ LEN1 REN1 while() { RPWN0 DELAY RPWM1 DELAY } /******************************/ /***2025 测试直流电机正反转past…...
vue3 uniapp vite 配置之定义指令
动态引入指令 // src/directives/index.js import trim from ./trim;const directives {trim, };export default {install(app) {console.log([✔] 自定义指令插件 install 触发了!);Object.entries(directives).forEach(([key, directive]) > {app.directive(…...
Mysql-JDBC
JDBCUtils public class JDBCUtils {/*** 工具类的构造方法一般写成私有*/private JDBCUtils(){}//静态代码块再类加载的时候执行,且执行一次static{try {Class.forName("com.mysql.cj.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackT…...
如何在爬虫中合理使用海外代理?在爬虫中合理使用海外ip
我们都知道,爬虫工作就是在各类网页中游走,快速而高效地采集数据。然而如果目标网站分布在多个国家或者存在区域性限制,那靠普通的网络访问可能会带来诸多阻碍。而这时,“海外代理”俨然成了爬虫工程师们的得力帮手! …...
安卓环境搭建开发工具下载Gradle下载
1.安装jdk(使用java语言开发安卓app) 核心库 java.lang java.util java.sq; java.io 2.安装开发工具(IDE)android studio https://r3---sn-2x3elnel.gvt1-cn.com/edgedl/android/studio/install/2023.3.1.18/android-studio-2023.3.1.18-windows.exe下载完成后一步一步安装即…...
k8s+helm部署tongweb7云容器版(by lqw)
安装准备 1.联系销售获取安装包和授权(例如:tongweb-cloud-7.0.C.6_P3.tar.gz)。 2.已安装docker和k8s集群,参考: k8s集群搭建 3.有对应的docker私库,没有的可以参考: harbor搭建 4.docker已经…...
关于DApp、DeFi、IDO私募及去中心化应用开发的综合解析
一、DApp(去中心化应用)技术开发 1. 技术架构与开发流程 分层架构 : 前端层 :使用React/Vue.js构建用户界面,通过Web3.js或Ethers.js与区块链交互。 智能合约层 :以太坊系常用Solidity,Solana…...
招贤纳士|Walrus 亚太地区招聘高级开发者关系工程师
职位介绍: 开发者关系团队(Developer Relations)通过线上线下方式与开发者社区互动,提供专业支持和指导,帮助他们在 Sui 和 Walrus 上构建下一代 Web3 应用。团队通过与社区对话,了解开发者的痛点…...
Qt实现文件传输客户端(图文详解+代码详细注释)
Qt实现文件传输客户端 1、 客户端UI界面设计2、客户端2.1 添加网络模块和头文件2.2 创建Tcp对象2.3 连接按钮2.3.1 连接按钮连接信号与槽2.3.2 连接按钮实现 2.4 读取文件2.4.1 连接读取文件的信号与槽2.4.2 读取文件槽函数实现2.5 进度条2.5.1 设置进度条初始值2.5.2 初始化进…...
STL详解 - list的模拟实现
目录 1. list 的基本结构 1.1 构造函数 2. 迭代器的实现 2.1 构造函数 2.2 自增和自减操作符 2.3 比较操作符 2.4 解引用和箭头操作符 3. list 容器的实现 3.1 构造函数 3.2 拷贝构造 3.3 赋值运算符重载 3.4 析构函数 3.5 迭代器相关函数 3.6 插入和删除函数 3.…...
ROS 2 的bag
ROS 1 和 ROS 2 的bag包互转方法 1. 安装rosbags工具: 使用pip安装最新版本的rosbags库(确保版本大于等于0.9.15) pip install rosbags --upgrade 2. db3文件bag包互转:使用rosbags-convert命令进行转换 rosbags-convert --sr…...
微软承认Win11出现极端错误,只能强制关机或重装系统
最近,不少使用 Windows 11 的用户反映,在系统更新后,“Windows Hello”突然失效,原本便捷的人脸识别和PIN登录功能统统无法使用。更糟的是,有人在重置系统后直接被挡在系统门外,这让人不禁发问:…...
bininote: 使用AI将视频转换了Markdown笔记
GitHub:https://github.com/JefferyHcool/BiliNote 更多AI开源软件:发现分享好用的AI工具、AI开源软件、AI模型、AI变现 - 小众AI BiliNote 是一个开源的 AI 视频笔记助手,支持通过哔哩哔哩、YouTube 等视频链接,自动提取内容并生…...
Python自动化办公
第五篇:Python自动化办公:10行代码搞定重复性工作 适合读者:职场人士、数据分析师 | 阅读时长:12分钟 引言 每天重复处理Excel、PDF或邮件?Python可以帮你自动化这些枯燥任务,节省90%的时间。本文通过实际…...