设计模式七:责任链模式
文章目录
- 1、责任链模式
- 2、spring中的责任链模式
- Spring Interceptor
- Servlet Filter
- Netty
1、责任链模式
责任链模式为请求创建了一个接收者对象的链,在这种模式下,通常每个节点都包含对另一个节点者的引用。每个节点针对请求,处理自己感兴趣的内容,处理完之后可以结束,也可以向下一个节点传递继续处理;
角色:
- 抽象处理者角色:处理请求的抽象类,定义了处理请求的抽象方法;(抽象类或接口实现);
- 具体处理者角色:处理请求的具体实现类;(持有下家对象的引用);
例:请假流程都是先由本部门审核,根据时间长短再进行下一级审批
//抽象类
public abstract class Handler {/*** 请假天数*/public int maxday;/*** 请假人*/public String name;public Handler(String name, int maxday) {this.maxday = maxday;this.name = name;}private Handler nextHandler;public void setNextHandler(Handler nextHandler) {this.nextHandler = nextHandler;}/*** 处理请假:判断请假天数,超过本部门限定时间则交由上一级部门*/public final void handlingFakeSlips(int day) {if (this.maxday >= day) {this.agree(day);}else {if (nextHandler != null) {System.out.println(name+":天数已超过我的审批权限,已提交我的上级审批");nextHandler.handlingFakeSlips(day);}else {System.out.println("天数时间过长,准备辞职吧!!!");}}}/*** 审批动作:子类来实现* @param day*/abstract void agree(int day);
}//部门实现类
public class RDDepartment extends Handler{public RDDepartment(String name, int maxday) {super(name, maxday);}@Overridevoid agree(int maxday) {System.out.println(name + ":研发部门请假审批通过,请假天数:" + maxday);}
}//主管实现类
public class Supervisor extends Handler{public Supervisor(String name, int maxday) {super(name, maxday);}@Overridevoid agree(int maxday) {System.out.println(name + ":主管请假审批通过,请假天数:" + maxday);}
}//董事实现类
public class Director extends Handler{public Director(String name, int maxday) {super(name, maxday);}@Overridevoid agree(int maxday) {System.out.println(name + ":请假董事审批通过,请假天数:" + maxday);}
}//组装链
public class HandlerChain {private Handler head;private Handler tail;public HandlerContext(){RDDepartment rdDepartment = new RDDepartment("研发部门",5);Supervisor supervisor = new Supervisor("项目主管",30);Director director = new Director("董事",180);rdDepartment.setNextHandler(supervisor);supervisor.setNextHandler(director);head = rdDepartment;tail = director;}public void doHandler(Integer days){head.handlingFakeSlips(days);}
}//请求
public class Request {public static void main(String[] args) {HandlerChain handlerChain = new HandlerChain();handlerChain.doHandler(360);}
}
优点(when,why):
1.发送者与接收者之间的耦合度降低(解耦)
2.可以灵活添加新的责任链中的对象
缺点:
1.不能保证请求一定被接收
2.一定程度上影响性能
这种形式很难进行动态新增和调整处理节点,一种比较复杂的控制节点的形式如Netty中的责任链模式应用,见下一节
2、spring中的责任链模式
Spring Interceptor
回顾springmvc
处理请求的流程:DispatcherServlet
接收到请求后,执行doDispatcher()
方法,流程回顾请求处理流程图
其中通过HandlerMapping
获得的是HandlerExecutionChain
对象
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {//......HandlerExecutionChain mappedHandler = null;//......mappedHandler = getHandler(processedRequest);//...... HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//......
}protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;
}protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) {if (adapter.supports(handler)) {return adapter;}}}throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
HandlerExecutionChain
中包含一个handler
对象(后面匹配能处理handler的适配器对象执行,详情对应适配器模式中讲解),还有一个拦截器列表List<HandlerInterceptor> interceptorList
,所有的实现了HandlerInterceptor
接口的类都会被加载进这个集合中,在请求处理前后分别以责任链的形式调用拦截器的preHandle
和postHandle
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);}}
}
这里的链是由集合List维护,使用List有序的特性一次调用每个拦截器,通过方法返回的结果判断是否需要传递到下一个拦截器
Servlet Filter
servlet
中Filter
的调用也是通过责任链模式,通过FilterChain
作为链条的管理者
//FilterChain接口
public interface FilterChain {public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;
}//FilterChain实现类
public class MockFilterChain implements FilterChain {//......private final List<Filter> filters;//......@Overridepublic void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {Assert.notNull(request, "Request must not be null");Assert.notNull(response, "Response must not be null");Assert.state(this.request == null, "This FilterChain has already been called!");if (this.iterator == null) {this.iterator = this.filters.iterator();}if (this.iterator.hasNext()) {Filter nextFilter = this.iterator.next();nextFilter.doFilter(request, response, this);}this.request = request;this.response = response;}
}
由上可知,
FilterChain
中管理的List<Filter> filters
即为所有实现了Filter
的过滤器,调用过滤器的时候,通过FilterChain
进行链条的调用。
//Filter接口
public interface Filter {default public void init(FilterConfig filterConfig) throws ServletException {}public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain)throws IOException, ServletException;default public void destroy() {}
}//Filter实现类
public class AuthFilter extends AuthenticationFilter {//......public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest httpRequest = toLowerCase((HttpServletRequest)request);String tokenString = httpRequest.getParameter("delegation");if (tokenString != null) {filterChain.doFilter(httpRequest, response);} else {super.doFilter(httpRequest, response, filterChain);}}//......
}
Filter
的doFilter
方法最后调用filterChain.doFilter(httpRequest, response);
即传递至下一个Filter进行处理
Netty
Netty中的handler使用了责任链模式,但是其中回调过多,责任链模式的体现不清晰,参考该文章**Spring中如何使用责任链模式**,将责任链抽离出来,完成在spring中的调用
该模型中,具有多条链,每条链属于不同层级,链中节点为
HandlerContext
,HandlerContext
包含相邻接点的引用,还有Handler
的引用
Pipeline
:为链条的管理者,通过Pipeline
来调用责任链
HandlerContext
:为链条中节点的上下文,它里面有链条的前一个节点和后一个节点的HandlerContext
引用
Handler
: 具体的处理程序,与HandlerContext一一对应
我们先仅看Filter事件这一条链,整个结构由Pipeline
管理整条链
//Pipelie接口
public interface Pipeline {Pipeline fireTaskFiltered();
}//Pipeline实现类
Component("pipeline")
@Scope("prototype")
public class DefaultPipeline implements Pipeline, ApplicationContextAware, InitializingBean {// 创建一个默认的handler,将其注入到首尾两个节点的HandlerContext中,其作用只是将链往下传递private static final Handler DEFAULT_HANDLER = new Handler() {};// 将ApplicationContext注入进来的主要原因在于,HandlerContext是prototype类型的,因而需要// 通过ApplicationContext.getBean()方法来获取其实例private ApplicationContext context;// 创建一个头结点和尾节点,这两个节点内部没有做任何处理,只是默认的将每一层级的链往下传递,// 这里头结点和尾节点的主要作用就是用于标志整个链的首尾,所有的业务节点都在这两个节点中间private HandlerContext head;private HandlerContext tail;// 用于业务调用的request对象,其内部封装了业务数据private Request request;// 用于执行任务的task对象private Task task;// 最初始的业务数据需要通过构造函数传入,因为这是驱动整个pipeline所需要的数据,// 一般通过外部调用方的参数进行封装即可public DefaultPipeline(Request request) {this.request = request;}// 这里我们可以看到,每一层级的调用都是通过HandlerContext.invokeXXX(head)的方式进行的,// 也就是说我们每一层级链的入口都是从头结点开始的,当然在某些情况下,我们也需要从尾节点开始链// 的调用,这个时候传入tail即可。// 触发任务过滤的链调用@Overridepublic Pipeline fireTaskFiltered() {HandlerContext.invokeTaskFiltered(head, task);return this;}// 用于往Pipeline中添加节点的方法,读者朋友也可以实现其他的方法用于进行链的维护void addLast(Handler handler) {HandlerContext handlerContext = newContext(handler);tail.prev.next = handlerContext;handlerContext.prev = tail.prev;handlerContext.next = tail;tail.prev = handlerContext;}// 这里通过实现InitializingBean接口来达到初始化Pipeline的目的,可以看到,这里初始的时候// 我们通过ApplicationContext实例化了两个HandlerContext对象,然后将head.next指向tail节点,// 将tail.prev指向head节点。也就是说,初始时,整个链只有头结点和尾节点。@Overridepublic void afterPropertiesSet() throws Exception {head = newContext(DEFAULT_HANDLER);tail = newContext(DEFAULT_HANDLER);head.next = tail;tail.prev = head;}// 使用默认的Handler初始化一个HandlerContextprivate HandlerContext newContext(Handler handler) {HandlerContext context = this.context.getBean(HandlerContext.class);context.handler = handler;return context;}// 注入ApplicationContext对象@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {this.context = applicationContext;}
}
Pipeline
的实现类内部除了实现接口的方法,其他方法均为初始化
DEFAULT_HANDLER
:Pipeline
管理一条链表,该链表的每个节点包含HandlerContext
和Handler
两个对象, 而链表的首和尾两个节点由Pipeline
自己指定(其他自定义的节点放在首尾两节点之间),DEFAULT_HANDLER
用来作为首尾节点的Handler
,不起任何作用context
: 由于这些类的作用域均不是单例,所以要使用ApplicationContext.getBean()
方法获取,所以类实现了ApplicationContextAware
接口的setApplicationContext
方法,用于注入ApplicationContext对象private HandlerContext head, tail
: 为一条链的首尾两个节点,从这儿也可以看出,链条的每个节点都是通过HandlerContext
来引用的,HandlerContext
再引用一个Handler
@Component
@Scope("prototype")
public class HandlerContext {HandlerContext prev;HandlerContext next;Handler handler;private Task task;public void fireTaskFiltered(Task task) {invokeTaskFiltered(next(), task);}/*** 处理任务过滤事件*/static void invokeTaskFiltered(HandlerContext ctx, Task task) {if (null != ctx) {try {ctx.handler().filterTask(ctx, task);} catch (Throwable e) {ctx.handler().exceptionCaught(ctx, e);}}}private HandlerContext next() {return next;}private Handler handler() {return handler;}
}
HandlerContext
作为节点,应有前后两个节点的引用pre
next
,还有具体处理任务的Handler
fireTaskFiltered
方法供Hanndler
调用,将请求传递给下一个节点处理(方法实现中区下一个HandlerContext
去执行)
invokeTaskFiltered
静态方法供Pipeline
和 上一个节点的fireTaskFiltered
方法调用,去执行Handler
的方法
public interface Handler {/*** 查询到task之后,进行task过滤的逻辑*/default void filterTask(HandlerContext ctx, Task task) {ctx.fireTaskFiltered(task);}
}
Handler
定义了感兴趣的事件(暂时只看过滤事件)
Handler
的实现类由我们根据自己的需要去编写,实现Handler
接口即可
@Component
public class DurationHandler implements Handler {@Overridepublic void filterTask(HandlerContext ctx, Task task) {System.out.println("时效性检验");ctx.fireTaskFiltered(task);}
}@Component
public class RiskHandler implements Handler {@Overridepublic void filterTask(HandlerContext ctx, Task task) {System.out.println("风控拦截");ctx.fireTaskFiltered(task);}
}@Component
public class TimesHandler implements Handler {@Overridepublic void filterTask(HandlerContext ctx, Task task) {System.out.println("次数限制检验");ctx.fireTaskFiltered(task);}
}
这里我们已经实现了
Pipeline
,HandlerContext
和Handler
,知道这些bean都是被Spring所管理的bean,那么我们接下来的问题主要在于如何进行整个链的组装。这里的组装方式比较简单,其主要需要解决两个问题:
- 对于后续写业务代码的人而言,其只需要实现一个
Handler
接口即可,而无需处理与链相关的所有逻辑,因而我们需要获取到所有实现了Handler
接口的bean;- 将实现了
Handler
接口的bean通过HandlerContext
进行封装,然后将其添加到Pipeline
中。
以上可以由spring完成,通过生命实现接口BeanPostProcessor
的类,实现postProcessAfterInitialization
方法,可以在初始化完Pipeline
后,获取所有实现了Handler
的bean
,并添加到pipeline
中,spring自动调用该方法
@Component
public class HandlerBeanProcessor implements BeanPostProcessor, ApplicationContextAware {private ApplicationContext context;// 该方法会在一个bean初始化完成后调用,这里主要是在Pipeline初始化完成之后获取所有实现了// Handler接口的bean,然后通过调用Pipeline.addLast()方法将其添加到pipeline中@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {if (bean instanceof DefaultPipeline) {DefaultPipeline pipeline = (DefaultPipeline) bean;Map<String, Handler> handlerMap = context.getBeansOfType(Handler.class);handlerMap.forEach((name, handler) -> pipeline.addLast(handler));}return bean;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {this.context = applicationContext;}
}
postProcessAfterInitialization(Object bean, String beanName)
: 实现了BeanPostProcessor
的该方法,在spring启动之后没初始化完成一个Bean
之后,都会调用该方法如此,当初始化完
pipeline
之后,获取实现了Handler接口的所有实现类,在addLast()
方法中为每一个Handler
初始化一个HandlerContext
,并添加到Pipeline
中
如此下来,整个过程调用如下
@Service
public class ApplicationService {@Autowiredprivate ApplicationContext context;public void mockedClient(Request request) {Pipeline pipeline = newPipeline(request);pipeline.fireTaskFiltered();}private Pipeline newPipeline(Request request) {return context.getBean(DefaultPipeline.class, request);}
}
1、spring项目启动,加载和初始化
Bean
,当加载到DefaultPipeline
的时候,由于实现了InitializingBean
接口,所以会调用初始化方法afterPropertiesSet()
,为DefaultPipeline
添加两个首尾节点HandlerContext
;2、当初始化完成
Pipeline
之后,调用postProcessAfterInitialization(Object bean, String beanName)
;3、加载所有实现了
Hnadler
接口的Bean
,并通过pipeline.addLast(Handler handler
方法为每一个handler
创建一个HandlerContext
,并添加到链条中;至此形成了一条链
4、
service
想执行该链的时候,通过有参构造方法,将请求request
传递给pipeline
,调用pipeline.fireTaskFiltered()
;5、
pipeline.fireTaskFiltered()
中,会调用HandlerContext
的静态方法HandlerContext.invokeTaskFiltered(HandlerContext ctx, Task task)
将第一个HandlerContext
传入去执行,其handler
的filterTask(HandlerContext ctx, Task task)
方法执行具体逻辑6、
handler
的filterTask(HandlerContext ctx, Task task)
方法最后会调用ctx
的invokeTaskFiltered(HandlerContext ctx, Task task)
方法,该方法会使用invokeTaskFiltered(next(), task)
去执行下一个节点ctx.handler().filterTask(ctx, task)
7、直至最后到节点
tail
没有下一个节点会停止执行;
至此单条链的责任链模式已完成
在netty中,并不是一条链,每一个handler有很多针对不同的事件的处理
在pipeline中有所有的事件,我们相对某一个事件处理是,实现handler的对应方法的处理逻辑,就会在对应层级的链中加入该handler,netty多层级代码责任链参考:Spring中如何使用责任链模式
相关文章:

设计模式七:责任链模式
文章目录 1、责任链模式2、spring中的责任链模式Spring InterceptorServlet FilterNetty 1、责任链模式 责任链模式为请求创建了一个接收者对象的链,在这种模式下,通常每个节点都包含对另一个节点者的引用。每个节点针对请求,处理自己感兴趣…...
Git,GitHub与GitLab分别是什么?有什么关系和区别?
Git 定义:Git 是一个分布式版本控制系统,用于跟踪文件的变化,并协助多人协作开发软件项目。作用:Git 可以在本地存储完整的项目历史记录,并允许开发者在不同的分支上进行独立的开发,最后将它们合并到主干分…...
【Spring连载】使用Spring Data访问 MongoDB----Template API 查询Documents
【Spring连载】使用Spring Data访问 MongoDB----Template API 查询Documents 一、 查询集合中的Documents二 选择字段三、 其他查询选项3.1 Hints3.2 游标批大小Cursor Batch Size3.3 Collations3.4 读取首选项Read Preference3.5 Comments 四、查询Distinct值五、GeoSpatial Q…...
git describe
git describe一般用于查看当前提交距离上次最近的tag是什么,并且还可以知道差了多少个commit,在工程实践当中是个非常好用的命令 如果git describe后面什么都不加的话,默认找的是最近的有注释的tag 如何打有注释的tag打注释? g…...

React Switch用法及手写Switch实现
问:如果注册的路由特别多,找到一个匹配项以后还会一直往下找,我们想让react找到一个匹配项以后不再继续了,怎么处理?答:<Switch>独特之处在于它只绘制子元素中第一个匹配的路由元素。 如果没有<Sw…...
PowerShell执行策略:确保脚本安全执行的关键
PowerShell执行策略:确保脚本安全执行的关键 在自动化和脚本任务管理中,PowerShell 是 Windows 系统管理员和自动化工程师的强大工具。但随着这种强大的能力也带来了安全风险,特别是在执行未经验证的脚本时。为了降低这种风险,Po…...

LeetCode 热题 100 | 图论(上)
目录 1 200. 岛屿数量 2 994. 腐烂的橘子 2.1 智障遍历法 2.2 仿层序遍历法 菜鸟做题,语言是 C 1 200. 岛屿数量 解题思路: 遍历二维数组,寻找 “1”(若找到则岛屿数量 1)寻找与当前 “1” 直接或间接连接在…...

跟着cherno手搓游戏引擎【25】封装2DRenderer,封装shader传参,自定义Texture
封装2DRenderer: Renderer.h: #include"ytpch.h" #include"Renderer.h" #include <Platform/OpenGL/OpenGLShader.h> #include"Renderer2D.h" namespace YOTO {Renderer::SceneData* Renderer::m_SceneData new Renderer::S…...
多个值时 if [ -z 报错 binary operator expected
if [ ! -z "\$client_pid" ]; then 报错: line 23: [: 662: binary operator expected 改成 if [[ ! -z "\$client_pid" ]]; then 即可。 unix - binary operator expected error when checking if a file with full pathname exists - Stack Overflo…...

如何使用ChatGPT创建一份优质简历
目录 第一步:明确目标和重点 第二步:与ChatGPT建立对话 第三步:整理生成的内容 第四步:注重行文风格 第五步:强调成就和量化结果 第六步:个性化和定制 第七步:反复修改和完善 总结 在现…...
k8s(6)
目录 一.kubectl 命令行管理K8S 陈述式资源管理方式(可理解成使用一条kubectl命令及其参数选项来实现资源对象的管理操作) service的4的基本类型: service的端口 应用发布策略: 声明式资源管理方式(可理解成使用…...

自动驾驶框架:自动驾驶汽车定位-感知-规划-决策-控制概述,按照我的架构图理解:决策决定的是速度,规划决定的是路径(架构理解推荐)
1.按照我的架构图理解:决策决定的是速度,规划决定的是路径 参考链接:【自动驾驶】运动规划丨速度规划丨自动驾驶速度规划及状态协调方法 2.下面是参考别人的理解: 自动驾驶汽车定位-感知-规划-决策-控制概述 规划-决策-控制知…...
Gemma
Gemma 1.使用2.RAG3.LoRA3.1LoRA分类任务3.2LoRA中文建模任务 1.使用 首先是去HF下载模型,但一直下载不了,所以去了HF镜像网站,下载gemma需要HF的Token,按照步骤就可以下载。代码主要是Kaggle论坛里面的分享内容。 huggingface-…...
淘宝关键词搜索API、搜索商品接口、商品价格监控
淘宝搜索引擎的工作原理: 淘宝搜索引擎的工作原理是基于搜索引擎的核心技术——爬虫和索引,通过对海量数据的抓取、分析和存储,提供给用户最准确的搜索结果。 具体来说,淘宝搜索引擎的工作流程如下: 企业级api数据…...

vue实现水印功能
目录 一、应用场景 二、实现原理 三、详细开发 1.水印的实现方式 2.防止用户通过控制台修改样式去除水印效果(可跳过,有弊端) 3.水印的使用 (1)单页面/全局使用 (2)全局使用个别页面去掉…...
记录一下我的Ruby On Rails的systemd服务脚本
自己也是一个 ROR 框架的学习者,同时也是 Ruby 的新手。对于如何让 ROR 应用随系统自动启动并不是很了解。在尝试了各种方法之后,我最终找到了一条可行的途径。虽然不确定是否完全正确,但服务已经成功启动了。因此,我决定在这里保…...

【计算机网络】传输层——TCP和UDP详解
文章目录 一. TCP和UDP简介二. UDP 协议详解1. UDP报文格式2. UDP的使用场景 三. TCP 协议详解1. TCP报文格式2. TCP协议的重要机制确认应答(保证可靠传输的最核心机制)超时重传连接管理(三次握手、四次挥手)!…...

stm32和嵌入式linux可以同步学习吗?
在开始前我有一些资料,是我根据网友给的问题精心整理了一份「stm3的资料从专业入门到高级教程」, 点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!!如果需要使用STM32,建…...
maven--->maven中的<properties>属性有什么作用?
🙌🙌🙌🙌🙌🙌 在Maven中,元素用于定义项目中可重用的属性值。这些属性值可以在项目的POM文件中被引用,以便在整个项目中统一管理和使用。通过使用元素,可以避免在POM文件…...
android 网络请求总结
1 先看下基础部分: android okhttp网络访问是基于 tcp/ip 的 最上层是应用层的封装,有http,https(加密),ftp 下面是socket套接字的封装,就是将ip和端口的封装 在下面就是tcp/udp 在下面 ip协议…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...