设计模式四:适配器模式

1、适配器模式的理解

适配器模式可以理解为有两个现成的类Adaptee和Target,它们两个是不能动的,要求必须使用B这个类来实现一个功能,但是A的内容是能复用的,这个时候我们需要编写一个转换器

适配器模式

  • Adaptee:被适配者,现有的接口或者类;
  • Adapter:适配器类,适配器模式的核心,转换作用
  • Target:目标接口或者类

适配器模式的实现步骤

  • 定义目标接口,即客户端所期望的接口。
  • 定义适配器类,继承目标接口并包含一个适配者对象。
  • 在适配器类中实现目标接口的方法,调用适配者对象的相关方法进行适配。
  • 在客户端中使用适配器类进行操作。

适用场景

  • 继承:需要用到别人的类,但是不能修改
  • 多适配者:多个不同的对象,需要让他们具有相同的方法
  • 缺省形式

适配器模式有类适配器和对象适配器两种方式,一般采用前者

2、继承场景

来公司第二天,写一个简单的业务,看到同事写的接口中5个方法有3个正好可以用的,但是还需要另外新增一个,于是我便开始了改造(新增一个方法);提交后同事大发雷霆,谁让你新增的,新增了这个方法,这个接口就失去了本来的意思。。。。。。

我去***,那算了,我还是自己写一个吧,想想怎么能在不懂他的接口前提下,复用一下他的代码,那边使用适配器模式

//同事的接口为被适配者
public interface Adaptee{void method1();void method2();void method3();void method4();void method5();
}
public class AdapteeImpl implements Adaptee {@Overridepublic void method1() {System.out.println("method1 execute......");}@Overridepublic void method2() {System.out.println("method2 execute......");}@Overridepublic void method3() {System.out.println("method3 execute......");}@Overridepublic void method4() {System.out.println("method4 execute......");}@Overridepublic void method5() {System.out.println("method5 execute......");}
}//我需要使用的接口为目标接口
public interface Target{void method1();void method2();void method3();void method6();
}
//构建适配器类Adapter
//方式一:类适配器方式
public class Adapter1 extends AdapteeImpl implements Target {@Overridepublic void method6() {System.out.println("method6 execute......");}
}public class Client {public static void main(String[] args) {Target target = new Adapter1();target.method1();target.method2();target.method3();target.method6();}
}//方式二:对象适配器方式
public class Adapter2 implements Target {private Adaptee adaptee;public Adapter2(Adaptee adaptee) {this.adaptee = adaptee;}@Overridepublic void method1() {adaptee.method1();}@Overridepublic void method2() {adaptee.method2();}@Overridepublic void method3() {adaptee.method3();}@Overridepublic void method6() {System.out.println("method6 execute......");}
}public class Client {public static void main(String[] args) {Target target = new Adapter2(new AdapteeImpl());target.method1();target.method2();target.method3();target.method6();}
}

3、多适配者

多个适配者,适配同一个目标接口,为每一个适配者创建一个适配器

这种是最常用的方式,在springMVC和AOP中都有用到,后面会进行解析

例:新能源汽车的发动机有电能发动机(Electric Motor)和光能发动机(Optical Motor)等,各种发动机的驱动方法不同,例如,电能发动机的驱动方法 electricDrive() 是用电能驱动,而光能发动机的驱动方法 opticalDrive() 是用光能驱动,它们是适配器模式中被访问的适配者。

客户端希望用统一的发动机驱动方法 drive() 访问这两种发动机,所以必须定义一个统一的目标接口 Motor,然后再定义电能适配器(Electric Adapter)和光能适配器(Optical Adapter)去适配这两种发动机。

在这里插入图片描述

package adapter;//目标:发动机
interface Motor {public void drive();
}//适配者1:电能发动机
class ElectricMotor {public void electricDrive() {System.out.println("电能发动机驱动汽车!");}
}//适配者2:光能发动机
class OpticalMotor {public void opticalDrive() {System.out.println("光能发动机驱动汽车!");}
}//电能适配器
class ElectricAdapter implements Motor {private ElectricMotor emotor;public ElectricAdapter() {emotor=new ElectricMotor();}public void drive() {emotor.electricDrive();}
}//光能适配器
class OpticalAdapter implements Motor {private OpticalMotor omotor;public OpticalAdapter() {omotor=new OpticalMotor();}public void drive() {omotor.opticalDrive();}
}//客户端代码
public class MotorAdapterTest {public static void main(String[] args) {System.out.println("适配器模式测试:");Motor motor=(Motor)ReadXML.getObject();motor.drive();}
}

4、缺省形式

缺省就是对于一个具有很多方法的接口,我没只需要其中几个,不想把所有的方法都实现一遍,可以写一个抽象类作为适配器,再去继承该适配器

public interface Adaptee {void model1();void model2();void model3();void model4();void model5();void model6();void model7();void model8();void model9();void model10();
}public abstract class Adapter implements Adaptee{@Overridepublic void model1() {}@Overridepublic void model2() {}@Overridepublic void model3() {}@Overridepublic void model4() {}@Overridepublic void model5() {}@Overridepublic void model6() {}@Overridepublic void model7() {}@Overridepublic void model8() {}@Overridepublic void model9() {}@Overridepublic void model10() {}
}public class Target extends Adapter{@Overridepublic void model3() {System.out.println("model3......");}@Overridepublic void model7() {System.out.println("model7......");}
}

5、spring中的适配器模式

5.1 SpringMVC
请求处理流程

HandlerAdapter 在 Spring MVC 中使用了适配器模式。HandlerAdapter 主要用于支持不同类型的处理器(如 Controller、HttpRequestHandler 或者 Servlet 等),让它们能够适配统一的请求处理流程。这样,Spring MVC 可以通过一个统一的接口来处理来自各种处理器的请求。在 Spring MVC 的工作流程中,HandlerAdapter 扮演了一个重要角色。以下是其工作原理的简化版:

在这里插入图片描述

  • DispatcherServlet:前端控制器(Front Controller),负责接收所有请求并将其分发给相应的处理程序
  • HandlerMapping:处理器映射器,立请求URL与处理程序之间的映射关系,默认为RequestMappingHandlerMapping(根据@Controller注解和@RequestMapping注解来进行URL与处理程序的映射),可以在Spring配置文件(如XML配置文件或Java配置类)中显式地配置所需的HandlerMapping
  • HandlerAdapter:处理器适配器,通过handlerMapping获取到对应的处理程序handler(controller)后,通过所有注册的handlerAdaptersupports()方法找到对应的适配器来调用,当存在多个合适的HandlerAdapter时,框架会根据一定规则进行判断和选择

  1. 用户发起一个 HTTP 请求到 Spring MVC 应用。
  2. DispatcherServlet 接收到请求后,首先会调用 HandlerMapping,寻找合适的处理器(Handler)来处理这个请求。
  3. 找到合适的处理器后,DispatcherServlet 需要找到一个能够处理这个处理器的 HandlerAdapter。为此,它会遍历所有已注册的 HandlerAdapter,调用它们的 supports 方法,检查它们是否支持当前处理器。
  4. 找到支持当前处理器的 HandlerAdapter 后,DispatcherServlet 会调用该 HandlerAdapterhandle 方法,将请求委托给处理器进行处理。
  5. 处理器处理完请求后,会返回一个 ModelAndView 对象,DispatcherServlet 会将这个对象传递给适当的 ViewResolver,以解析视图并将响应返回给用户。

我们在ControllerServiceMapper中写的业务逻辑,在HandlerAdapter的处理过程中执行

以下是一个简化的 HandlerAdapter 接口示例:

public interface HandlerAdapter {boolean supports(Object handler);ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

Spring MVC 提供了一些内置的 HandlerAdapter 实现,如RequestMappingHandlerAdapterSimpleControllerHandlerAdapterHttpRequestHandlerAdapter 是 Spring MVC 中内置的几个 HandlerAdapter 实现,它们分别用于支持不同类型的处理器。

  1. RequestMappingHandlerAdapter:支持基于注解的处理器,如使用 @Controller@RequestMapping 注解的处理器方法。这是 Spring MVC 中最常用的处理器类型。
  2. SimpleControllerHandlerAdapter:支持实现 org.springframework.web.servlet.mvc.Controller 接口的处理器。这是 Spring MVC 早期版本中的处理器类型,现在已经较少使用。
  3. HttpRequestHandlerAdapter:支持实现 org.springframework.web.HttpRequestHandler 接口的处理器。这种类型的处理器主要用于处理静态资源,如图片、样式表等。

举例:

@Controller
@RequestMapping("/user")
public class UserController {@GetMapping("/{id}")public String getUserById(@PathVariable("id") Long id, Model model) {// 执行业务逻辑return "userDetails";}
}

在上述例子中有一个controller,当spring接收到请求时,处理过程为:

假设我们使用了默认的配置和注解驱动。

1、首先,前端控制器(DispatcherServlet)会根据请求的 URL 路径来选择合适的 HandlerMapping。在 Spring MVC 中,默认使用的是 RequestMappingHandlerMapping,它会根据 @RequestMapping 注解以及其他相关注解来确定处理程序(handler)与请求路径之间的映射关系。

2、 对于 UserController 中的 getUserById() 方法,使用了 @GetMapping 注解,并指定了路径为 “/{id}”。因此,当客户端发送 GET 请求到 “/user/{id}” 路径时,RequestMappingHandlerMapping 就会匹配到这个处理程序(handler)。

3、 接下来,在执行处理程序之前,前端控制器还需要选择合适的 HandlerAdapter,并将请求委托给它来执行处理程序

4、对于标注有 @GetMapping、@PostMapping 等注解的方法,默认情况下会选择 RequestMappingHandlerAdapter 作为主要的 HandlerAdapter。

5、RequestMappingHandlerAdapter 是一个功能强大且灵活的适配器,能够处理包含模型数据、参数绑定、异常处理等复杂情况。

在上述例子中,当通过 RequestMappingHandlerMapping 匹配到 UserController 的 getUserById() 方法后,前端控制器就会选择 RequestMappingHandlerAdapter 来执行这个处理程序,并传递相应的请求参数和模型数据给该方法。

在 Spring MVC 中,前端控制器(DispatcherServlet)会根据请求路径和方法来选择合适的 HandlerAdapter。

选择后,HandlerAdapter 将接收到的请求参数、模型数据等信息传递给相应的处理程序(业务逻辑),并执行该处理程序。

HandlerAdapter 根据具体的处理程序类型来确定调用哪个方法,以及如何将请求数据传递给该方法。

不同类型的处理程序可能有不同的要求和处理方式。 例如,在使用注解配置的方法作为处理程序时,默认情况下会选择 RequestMappingHandlerAdapter。

RequestMappingHandlerAdapter 会根据注解中定义的规则,将请求参数、路径变量、请求体内容等绑定到方法的入参上,并调用该方法执行业务逻辑。它还能够通过模型对象(Model)来向视图传递数据。

可以说 HandlerAdapter 是负责将请求数据传递给我们自己编写的业务逻辑处理程序,并执行其中定义的方法。它是框架与业务代码之间进行通信和协调的重要环节。

自定义handlerAdapter

通常情况下,Spring MVC 默认提供的 HandlerAdapters 是能够满足大多数应用场景的,并且能够处理各种类型的处理程序(handler)。

在某些特殊情况下,可能需要自定义 HandlerAdapter 来满足特定的需求。以下是一些需要自定义 HandlerAdapter 的情况:

  1. 需要支持新的处理程序类型:如果你使用了自定义的处理程序类型,而该类型不属于 Spring MVC 默认支持的类型,那么你可以编写自己的 HandlerAdapter 来支持该类型。

  2. 特定的请求/响应方式:如果你想要处理一种不常见或非标准的请求/响应方式,例如 WebSocket、SSE(服务器发送事件)等,你可能需要编写一个适配器来处理这些方式。

  3. 定制化业务逻辑:如果你有特殊的业务需求,需要在执行业务逻辑之前或之后添加额外的逻辑操作,例如权限校验、日志记录等,你可以编写一个自定义 HandlerAdapter 来扩展框架提供的功能。

  4. 性能优化:如果你对性能有极高要求,并希望通过优化执行过程来提升系统性能,可以根据具体场景编写一个更高效的 HandlerAdapter 来替换默认实现。

总之,在需要与 Spring MVC 框架进行更深度集成、满足特定需求或优化性能时,可以考虑自定义 HandlerAdapter。但在大多数情况下,默认提供的 HandlerAdapters 能够满足开发需求。

handlerAdapter的适配器模式

可以看到处理器的类型不同,有多重实现方式,那么调用方式就不是确定的,如果需要直接调用 Controller 方法,需要调用的时候就得不断地使用 if else 来进行判断是哪一种子类然后执行。那么如果后面要扩展 Controller, 就得修改原来的代码,这样违背了 OCP 原则

在这里插入图片描述

下面手写一下handlerAdapter的选择流程

//controller类,我们自己写的逻辑,即handler
public interface Controller {}public class AnnotationController implements Controller{public void doAnnotationHandler() {System.out.println("annotation...");}
}public class HttpController implements Controller{public void doHttpHandler() {System.out.println("http...");}
}public class SimpleController implements Controller{public void doSimpleHandler() {System.out.println("simple...");}
}
//handlerAdapter类 处理器适配器
//通过handlermapping获取到对应的handler后,再查找对应的处理器适配器调用对应方法
public interface HandlerAdapter {boolean supports(Object handler);void handle(Object handler);
}public class AnnotationHandlerAdapter implements HandlerAdapter{@Overridepublic boolean supports(Object handler) {// TODO Auto-generated method stubreturn (handler instanceof AnnotationController);}@Overridepublic void handle(Object handler) {// TODO Auto-generated method stub((AnnotationController)handler).doAnnotationHandler();}
}public class HttpHandlerAdapter implements HandlerAdapter {@Overridepublic boolean supports(Object handler) {// TODO Auto-generated method stubreturn (handler instanceof HttpController);}@Overridepublic void handle(Object handler) {// TODO Auto-generated method stub((HttpController)handler).doHttpHandler();}
}public class SimpleHandlerAdapter implements HandlerAdapter{@Overridepublic boolean supports(Object handler) {// TODO Auto-generated method stubreturn (handler instanceof SimpleController);}@Overridepublic void handle(Object handler) {// TODO Auto-generated method stub((SimpleController)handler).doSimpleHandler();}
}
//DispatcherServlet类 执行整体逻辑
public class DispatchServlet {public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>();//注内置的处理器适配器,也可以自定义,然后注册到DispatcherServlet类中public DispatchServlet() {handlerAdapters.add(new AnnotationHandlerAdapter());handlerAdapters.add(new HttpHandlerAdapter());handlerAdapters.add(new SimpleHandlerAdapter());}public void doDispatch() {/**   1、这里用来调用handlermapping获取对应的controller(handler)*   springmvc中默认使用的是 RequestMappingHandlerMapping*/AnnotationController annotationController = new AnnotationController();//2、通过controller(handler)选择对应的handlerAdapterHandlerAdapter handlerAdapter = getHandler(annotationController);//3、调用对应处理程序(controller的方法),处理请求handlerAdapter.handle(annotationController);}public HandlerAdapter getHandler(Controller controller) {for (HandlerAdapter handlerAdapter : handlerAdapters) {if(handlerAdapter.supports(controller)) {return handlerAdapter;}}return null;}
}
//客户端
public class Client {public static void main(String[] args) {new DispatchServlet().doDispatch();}
}
自定义适配器

要自定义一个 HandlerAdapter,你需要实现 org.springframework.web.servlet.HandlerAdapter 接口,并提供对你的自定义处理器的支持。下面是一个简单的自定义 HandlerAdapter 示例:

首先,创建一个自定义处理器:

public class CustomHandler {public String handleRequest() {return "Hello, CustomHandler!";}
}

接着,实现一个自定义的 HandlerAdapter

import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class CustomHandlerAdapter implements HandlerAdapter {@Overridepublic boolean supports(Object handler) {return handler instanceof CustomHandler;}@Overridepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {CustomHandler customHandler = (CustomHandler) handler;String result = customHandler.handleRequest();return new ModelAndView("customView", "message", result);}@Overridepublic long getLastModified(HttpServletRequest request, Object handler) {return -1;}
}

要在 Spring MVC 应用中使用这个自定义的 HandlerAdapter,你需要将其注册到 DispatcherServlet 中。在基于 Java 配置的应用中,你可以这样做:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {@Overrideprotected void addAdapters(List<HandlerAdapter> adapters) {adapters.add(new CustomHandlerAdapter());super.addAdapters(adapters);}
}

这样,你的自定义 HandlerAdapter 就会在 Spring MVC 应用中生效,并能处理 CustomHandler 类型的处理器。

5.2 AOP

在Spring的Aop中,使用Advice(通知)来增强被代理类的功能,Advice的类型有:BeforeAdvice、AfterReturningAdvice、ThreowSadvice。

每种Advice都有对应的拦截器,MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor。

各种不同类型的Interceptor,通过适配器统一对外提供接口,如下类图所示:client —> target —> adapter —> interceptor —> advice。最终调用不同的advice来实现被代理类的增强
原理springmvc类似
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/260740.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis学习总结

MyBatis分页如何实现 分页分为 逻辑分页&#xff1a;查询出所有的数据缓存到内存里面&#xff0c;在从内存中筛选出需要的数据进行分页 物理分页&#xff1a;直接用数据库语法进行分页limit mybatis提供四种方法分页&#xff1a; 直接在sql语句中分页&#xff0c;传递分页参数…

主流开发语言和开发环境介绍

主流开发语言和开发环境介绍文章目录 ⭐️ 主流开发语言&#xff1a;2024年2月编程语言排行榜&#xff08;TIOBE前十&#xff09;⭐️ 主流开发语言开发环境介绍1.Python2.C3.C4.Java5.C#6.JavaScript7.SQL8.GO9.Visual Basic10.PHP ⭐️ 主流开发语言&#xff1a;2024年2月编程…

二叉树(6)——二叉树的创建和销毁

1 二叉树的创建 整体思路 将数组里的元素一直分为根&#xff0c;左子树&#xff0c;右子树&#xff0c;遇到#就返回NULL&#xff0c;链接到上层递归的左子树或者右子树&#xff0c;一定要把一个节点的左子树全部递归完才能返回到右子树。这种方法也可以scanf一个数组里的元素&…

苍穹外卖——第一天nginx

放到全是英文路径的打不开 到安装路径进入cmd&#xff0c;输入nginx -t nginx: the configuration file E:\Astudy\nginx-1.20.2/conf/nginx.conf syntax is ok nginx: [emerg] bind() to 0.0.0.0:80 failed (10013: An attempt was made to access a socket in a way forbid…

考研证件照可以自己用手机拍吗?考研证件照p过可以通过审核吗?考研证件照有什么要求

一、考研证件照可以自己用手机拍吗 现在的智能手机相机技术先进&#xff0c;大多都配备了高像素摄像头&#xff0c;使得自拍照片的质量有了大幅提升。相较于传统的证件照拍摄&#xff0c;使用手机自拍考研证件照理论上是可行的。然而&#xff0c;考研证件照需要满足一定的规定…

ansible剧本中的角色

1 roles角色 1.1 roles角色的作用&#xff1f; 可以把playbook剧本里的各个play看作为一个角色&#xff0c;将各个角色打的tasks任务、vars变量、template模版和copy、script模块使用的相关文件等内容放置在指定角色的目录里统一管理&#xff0c;在需要的时候可在playbook中使…

【linux】查看openssl程序的安装情况

【linux】查看openssl程序的安装情况 1、查看安装包信息 $ rpm -qa |grep openssl 2、安装路径 $ rpm -ql openssl $ rpm -ql openssl-libs $ rpm -ql openssl-devel 3、相关文件和目录 /usr/bin/openssl /usr/include/openssl /usr/lib64/libssl.so.* /usr/lib64/libcrypto…

Java实现Redis延时队列

“如何实现Redis延时队列”这个面试题应该也是比较常见的&#xff0c;解答如下&#xff1a; 使用sortedset&#xff08;有序集合&#xff09; &#xff0c;拿时间戳作为 score &#xff0c;消息内容作为key 调用 zadd 来生产消息&#xff0c;消费者用zrangebyscore 指令获取 N …

视频生成模型作为世界模拟器

我们探索了在视频数据上大规模训练生成模型。具体来说&#xff0c;我们联合训练文本条件扩散模型&#xff0c;处理不同持续时间、分辨率和宽高比的视频和图像。我们利用一种在时空补丁上操作视频和图像潜码的transformer架构。我们最大的模型&#xff0c;Sora&#xff0c;能够生…

LeetCode 450.删除二叉搜索树中的节点和669.修建二叉搜索树思路对比 及heap-use-after-free问题解决

题目描述 450.删除二叉搜索树中的节点 给定一个二叉搜索树的根节点 root 和一个值 key&#xff0c;删除二叉搜索树中的 key 对应的节点&#xff0c;并保证二叉搜索树的性质不变。返回二叉搜索树&#xff08;有可能被更新&#xff09;的根节点的引用。 一般来说&#xff0c;…

java 使用documents4j将XML转为pdf文件的方式

1.背景&#xff1a; 通过spire.doc.free将word转换成PDF时存在缺陷&#xff1a;只能获取前3页。获取全文另外需支付费用。 2.解决办法 使用documents4j&#xff0c;documents4j会保留原word文件中更多的样式&#xff0c;如修订模式下的差异化字体颜色、文档右侧修订记录等。 …

掌握Go并发:Go语言并发编程深度解析

&#x1f3f7;️个人主页&#xff1a;鼠鼠我捏&#xff0c;要死了捏的主页 &#x1f3f7;️系列专栏&#xff1a;Golang全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&…

【Docker】集群容器监控和统计 Portainer基本用法

Portainer是一款轻量级的应用&#xff0c;它提供了图形化界面&#xff0c;用川于方便地管理Docker环境&#xff0c;包括单机环境和集群环境。 主要功能&#xff1a;实现集群容器的监控和统计 下载安装 官网&#xff1a;https://www.portainer.io 文档&#xff1a;https://do…

C#分部类的应用:记录学生信息

目录 一、分部类及其用途 二、实例 再发一个分部类的应用&#xff0c;巩固一下。 一、分部类及其用途 C#中的部分类也被称为分部类。 C#中的部分类是一种将类的定义分成多个部分&#xff0c;每个部分都位于自己的文件中&#xff0c;然后在编译时合并在一起的机制。 部分类…

(十)【Jmeter】线程(Threads(Users))之jp@gc - Stepping Thread Group (deprecated)

简述 操作路径如下: 作用:通过逐步增加线程数来模拟用户并发访问。配置:设置This group will start、First,wait for 、Then start、Next , add等参数。使用场景:模拟逐步增长的并发访问,观察应用程序的性能变化。优点:适用于测试应用程序在逐步增加负载下的性能表现。…

stm32——hal库学习笔记(定时器)

这里写目录标题 一、定时器概述&#xff08;了解&#xff09;1.1&#xff0c;软件定时原理1.2&#xff0c;定时器定时原理1.3&#xff0c;STM32定时器分类1.4&#xff0c;STM32定时器特性表1.5&#xff0c;STM32基本、通用、高级定时器的功能整体区别 二、基本定时器&#xff0…

智慧城市与数字孪生:实现城市可持续发展的关键

一、引言 随着全球城市化进程的加速&#xff0c;城市面临着诸多挑战&#xff0c;如资源紧张、环境恶化、交通拥堵等。为了解决这些问题&#xff0c;智慧城市的概念应运而生。智慧城市利用先进的信息通信技术&#xff0c;提升城市治理水平&#xff0c;改善市民的生活质量。而数…

[C#]winform基于opencvsharp结合pairlie算法实现低光图像增强黑暗图片变亮变清晰

【低光图像增强介绍】 在图像处理领域&#xff0c;低光图像增强是一个具有挑战性的任务。由于光线不足&#xff0c;这些图像往往呈现出低对比度、高噪声和细节丢失等问题&#xff0c;严重影响了图像的视觉效果和后续分析的准确性。因此&#xff0c;开发有效的低光图像增强方法…

Redis(03)——发布订阅

基础命令 基于频道 publish channel message&#xff1a;将信号发送到指定的频道pubsub subcommand [argument [argyment]]&#xff1a;查看订阅或发布系统状态subscribe channel [channel]&#xff1a;订阅一个或多个频道的信息unsubscribe [channel [channel]]&#xff1a;退…

SpringBootWeb学习笔记——12万字详细总结!

0. 写在前面 注:这套笔记是根据黑马程序员B站2023-3-21的视频学习的成果,其中省略了前端基础部分、Maven部分和数据库基础部分,详情可见目录。 注注:目前文章内结尾处多幅图片加载不出来,因为图片还存在本地没被传上来,过段时间再改~ 所有的Spring项目都基于Spring Fra…