springmvc5.x-mvc实现原理及源码实现

上文:spring5.x-声明式事务原理及源码实现


系列文章:

                    spring5.x-声明式事务原理及源码实现

                    spring5.x-AOP实现原理及源码分析

                    spring5.x-监听器原理及源码实现

                    spring5.x-解决循环依赖分析

                    spring5.x-IOC模块源码学习

                    spring5.x介绍及搭配spring源码阅读环境


基础知识

请看原来写的文章:springmvc

源码学习

cca5a76aada179e072014928521a51f5.png

@RequestMapping("/{id}")public String showUserInfo(ModelMap modelMap, @PathVariable("id")Integer id){Student student = new Student();student.setId(id);student.setAge(100);student.setName("test");modelMap.addAttribute("name", student.getName());modelMap.addAttribute("age", student.getAge());modelMap.addAttribute("id", student.getId());return "result";}

springmvc的初始化

  1. DispatcherServlet 初始化:DispatcherServlet 是 Spring MVC 的前端控制器,在 web.xml 或 WebApplicationInitializer 中配置 Servlet 容器时会初始化 DispatcherServlet。DispatcherServlet 的初始化源码位置为 org.springframework.web.servlet.DispatcherServlet。

<servlet><servlet-name>springDispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet>

代码位置:org.springframework.web.servlet.DispatcherServlet#initStrategies

//用于初始化 Spring MVC 的各个策略组件。
protected void initStrategies(ApplicationContext context) {//初始化处理文件上传的解析器,用于解析请求中的 multipart 数据。this.initMultipartResolver(context);//初始化处理国际化的解析器,用于解析请求中的语言区域信息。this.initLocaleResolver(context);//初始化处理主题的解析器,用于解析请求中的主题信息。this.initThemeResolver(context);//初始化处理器映射器,用于将请求映射到对应的处理器(Controller)。this.initHandlerMappings(context);//初始化处理器适配器,用于调用处理器的方法并从中获取 ModelAndView 对象。this.initHandlerAdapters(context);//初始化处理器异常解析器,用于处理请求过程中发生的异常。this.initHandlerExceptionResolvers(context);//初始化请求到视图名称的转换器,用于将处理器返回的逻辑视图名称转换为实际的视图路径。this.initRequestToViewNameTranslator(context);//初始化视图解析器,用于将视图名称解析为具体的视图类型。this.initViewResolvers(context);//初始化 FlashMap 管理器,用于处理请求间的数据传递。this.initFlashMapManager(context);}

dc032971efa1ce353e8c28da77fc3fe8.png

  1. 注册 ServletContextListener:在 Servlet 容器启动时,通常会注册一个监听器(ServletContextListener)来初始化 Spring MVC 的上下文。在监听器的 contextInitialized 方法中实现 Spring MVC 的初始化,例如加载配置文件、创建 ApplicationContext 等。

代码位置:org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener

//执行应用程序事件监听器
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {//调用监听器的 并传递当前发生的应用程序事件作为参数。listener.onApplicationEvent(event);} catch (ClassCastException var6) {String msg = var6.getMessage();if (msg != null && !this.matchesClassCastMessage(msg, event.getClass().getName())) {throw var6;}Log logger = LogFactory.getLog(this.getClass());if (logger.isDebugEnabled()) {logger.debug("Non-matching event type for listener: " + listener, var6);}}}
  1. WebApplicationContext 初始化:Spring MVC 使用了自己的容器(WebApplicationContext),该容器会在 ServletContextListener 初始化时创建并配置。关于 WebApplicationContext 的初始化可以参考 org.springframework.web.context.support.XmlWebApplicationContext 或 org.springframework.web.context.support.AnnotationConfigWebApplicationContext 源码。

da408f84bedb1e065eb92149bcf7dc26.png

  1. HandlerMapping 和 HandlerAdapter 初始化:HandlerMapping 负责将请求映射到对应的处理器(Controller),而 HandlerAdapter 则负责调用相应的处理器方法。这些组件的初始化涉及配置文件、注解扫描等过程,相关源码位置为 org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping、org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping、org.springframework.web.servlet.handler.SimpleUrlHandlerMapping,以及 org.springframework.web.servlet.handler.BeanNameUrlHandlerAdapter、org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter 等。

90d762452f9c9b0ad1331f787b3c0601.pnga02ec54c3a625cdd83c7e76f1bb511f0.png

注意监听器:org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener

1e32edea4a64e084cb4983a003009cbb.png

  1. 视图解析器初始化:视图解析器(ViewResolver)负责将处理器方法的返回值解析为具体的视图。Spring MVC 支持多种类型的视图解析器,如 InternalResourceViewResolver、FreeMarkerViewResolver 等。初始化过程中会配置视图解析器的相关属性和位置,例如视图前缀、后缀等。相关源码位置为 org.springframework.web.servlet.view.InternalResourceViewResolver。

由于我们配的视图是:

<!-- 配置视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/views/"></property><property name="suffix" value=".jsp"></property></bean>

所以解析出来是:

代码位置:org.springframework.beans.factory.BeanFactoryUtils#beansOfTypeIncludingAncestors(org.springframework.beans.factory.ListableBeanFactory, java.lang.Class<T>, boolean, boolean)

5e453c7e2e35df940479ed79abea1754.png

最终我们都会调onRefresh()完成初始化。代码位置:org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

if (!this.refreshEventReceived) {// Either the context is not a ConfigurableApplicationContext with refresh// support or the context injected at construction time had already been// refreshed -> trigger initial onRefresh manually here.onRefresh(wac);}

springmvc分发实现

那么spring加载完成后,就是调用的问题,这里注意,会根据不同的调用方式来进行分发,比如http tcp 等的分发方式都不太一样。那最终都会调到doDispatch.

代码位置:org.springframework.web.servlet.DispatcherServlet#doDispatch

//分发方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {//初始化请求信息HttpServletRequest processedRequest = request;//用于存储根据请求对象匹配到的处理器对象HandlerExecutionChain mappedHandler = null;//标识是否已解析多部分请求boolean multipartRequestParsed = false;//获取当前WebAsyncManager对象(异步处理)WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {//初始化视图模型ModelAndView mv = null;Exception dispatchException = null;try {//检查请求是否为多部分请求(文件或),通过检查请求头中的 "Content-Type" 是否以 "multipart/" 开头来判断。processedRequest = checkMultipart(request);//判断是否一致,如果是则为truemultipartRequestParsed = (processedRequest != request);// 获取处理对象mappedHandler = getHandler(processedRequest);//为空就是没找着路劲 返回404if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 获取处理对HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 获取请求方法String method = request.getMethod();//判断是否为get请求boolean isGet = "GET".equals(method);//如果是 或头为HEADif (isGet || "HEAD".equals(method)) {//获取最后时间long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (logger.isDebugEnabled()) {logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);}//新请一个响应并检查,如果不通过直接中止if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}//调用处理器的预处理方法 ,如果不通过中止if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 调用适配器mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//检查异步处理是否已经开始if (asyncManager.isConcurrentHandlingStarted()) {return;}//设置默认的视图名到ModelAndView中applyDefaultViewName(processedRequest, mv);//调用后置处理器方法mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.//如果出象则创建一个错误的异常dispatchException = new NestedServletException("Handler dispatch failed", err);}//返回客户端processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {// 方法检查异步处理是否已经开始if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {//用于在异步处理开始后执行相关的清理操作或其他逻辑处理mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {//用于清理多部分请求中使用的资源// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}

15bdc30486a7e6764ddc821df1631bf7.png

接着细节深入

processedRequest = checkMultipart(request);

代码位置:org.springframework.web.servlet.DispatcherServlet#checkMultipart

//将请求转换为分段请求,并使分段解析程序可用。如果未设置多部分解析程序,则只需使用现有请求。
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {//不为空 且 请求是否是multipart类型if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {//将请求转换为 multipart类型的请求对象,if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +"this typically results from an additional MultipartFilter in web.xml");}//检查当前请求是否已经发生过多部分请求解析失败的异常else if (hasMultipartException(request) ) {logger.debug("Multipart resolution failed for current request before - " +"skipping re-resolution for undisturbed error rendering");}else {try {//转换为HttpServletRequest 并返回return this.multipartResolver.resolveMultipart(request);}catch (MultipartException ex) {if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {logger.debug("Multipart resolution failed for error dispatch", ex);// Keep processing error dispatch with regular request handle below}else {throw ex;}}}}// If not returned before: return original request.return request;}

上面这个方法解析multipart类型的请求。

接着:mappedHandler = getHandler(processedRequest); 这个是用于确定当前请求的处理程

Nullableprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {//不为空if (this.handlerMappings != null) {//循环调用每个 HandlerMapping 的 getHandler(request) 方法,传入当前的 HttpServletRequest 对象作为参数,来获取对应的处理器。for (HandlerMapping hm : this.handlerMappings) {if (logger.isTraceEnabled()) {logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");}HandlerExecutionChain handler = hm.getHandler(request);if (handler != null) {return handler;}}}return null;}

当面这个用于获取HandlerExecutionChain,其实就是请求处理器(Handler)的方法。

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

根据刚刚获取的处理器进行获取HandlerAdapter,其实就是决定用来调modelandview或其他视图,有很多种,比如:

  1. RequestMappingHandlerAdapter:适配处理器函数、带有注解的控制器等类型的处理器。

  2. SimpleControllerHandlerAdapter:适配基于实现 Controller 接口的控制器。

  3. HttpRequestHandlerAdapter:适配实现 HttpRequestHandler 接口的处理器,用于处理原始的 HttpServletRequest 与 HttpServletResponse。

  4. HandlerAdapter 的默认实现 DefaultHandlerAdapter:用于处理没有明确适配器的处理器类型,默认使用 ServletInvocableHandlerMethod 来执行处理器方法。

  5. 针对异步请求的适配器:例如 AsyncHandlerInterceptor 和 Callable 所对应的适配器。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {//循环判断是哪种类型匹配,匹配就返回for (HandlerAdapter ha : this.handlerAdapters) {if (logger.isTraceEnabled()) {logger.trace("Testing handler adapter [" + ha + "]");}//获取最终的HandlerAdapterif (ha.supports(handler)) {return ha;}}}throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");}

mappedHandler.applyPreHandle(processedRequest, response),这个方法用于记录拦截器的执行位置。

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];//调用前置拦截器方法 如果返回值为 false,表示拦截器不允许继续执行后续的处理逻辑if (!interceptor.preHandle(request, response, this.handler)) {//方法进行拦截器链的后置处理,并直接返回 falsetriggerAfterCompletion(request, response, null);return false;}//记录位置this.interceptorIndex = i;}}return true;}
//用于触发拦截器链的后置处理(afterCompletion
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);}}}}

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

接受三个参数:processedRequest 是经过前置处理器链处理后的请求对象,response 是响应对象,mappedHandler.getHandler() 是映射到的请求处理器对象。

在执行 handle() 方法时,会根据请求处理器的类型调用相应的处理逻辑。不同的请求处理器可能是不同类型的对象,例如 Controller、HttpRequestHandler 或 HttpMessageConverter 等。

注意:一般HTTP 请求中可以包含多种类型的参数,常见的有以下几种类型:

  1. 查询参数(Query Parameters):位于 URL 中,以 ? 开头,键值对使用 key=value 的形式表示,多个参数之间使用 & 分隔。例如: http://hong.com/api?key1=value1&key2=value2。可以通过解析 URL 来获取查询参数。

  2. 路径参数(Path Parameters):位于 URL 路径中,用于表示特定资源的标识符或属性。路径参数通常用于 RESTful 风格的路由中,使用占位符来代表参数值。例如: http://hong.com/api/users/{userId},其中 {userId} 就是路径参数。

  3. 请求体参数(Request Body Parameters):位于请求体中,通常使用表单数据或 JSON 格式来传递。可以通过 HTTP 请求的 Content-Type 头部字段来确定参数的类型。常见的参数类型有:

    • 表单参数(Form Parameters):使用表单数据格式传递,即 key=value 的形式。

    • JSON 参数(JSON Parameters):使用 JSON 格式传递,请求体中的数据是一个合法的 JSON 对象。

    • 文件参数(File Parameters):用于上传文件,请求体中包含文件的二进制数据。

判断请求参数的方式取决于你使用的服务器端框架或编程语言。大多数框架提供了相应的工具或库来解析和获取请求参数。一般而言,可以通过从请求对象中获取相应的参数来获取请求参数。例如,在 Java 的 Spring 框架中,可以使用 @RequestParam 注解、HttpServletRequest 对象等来获取请求参数。

这个位置非常复杂。有兴趣可以深入。

那么有些同学会疑问,springmvc可以支持哪些参数?

  1. 查询参数(Query Parameters):将查询参数作为方法参数进行接收。可以使用 @RequestParam 注解将参数与请求中的查询参数绑定,还可以指定默认值、是否必需等属性。

  2. 路径参数(Path Parameters):通过在请求路径中使用占位符来接收参数。使用 @PathVariable 注解将路径参数与方法参数进行绑定。

  3. 请求体参数(Request Body Parameters):通常用于接收 POST 或 PUT 请求中的数据。可以使用 @RequestBody 注解将请求体中的数据绑定到方法参数上。支持的数据格式包括 JSON、XML 等。

  4. 头部信息(Request Header):可以使用 @RequestHeader 注解将特定的请求头信息与方法参数绑定。

  5. Cookie 参数(Cookie Parameters):使用 @CookieValue 注解将特定的 Cookie 值与方法参数进行绑定。

  6. 表单参数(Form Parameters):适用于接收表单提交的参数。可以使用 @RequestParam 注解或 @ModelAttribute 注解将表单字段与方法参数进行绑定。

  7. 文件上传(File Upload):接收文件上传请求时,可以使用 MultipartFile 类型的方法参数来接收上传的文件数据。

当然上面是我所看源码了解到的,目前有没有其它暂时没看到。可以HandlerMethodArgumentResolver

6cf5361b22eadd6ab088a4d983c577a0.png

mappedHandler.applyPostHandle(processedRequest, response, mv);

上面这段是拦截器的一些实现,我们有些请求或不开放的接口权限等可以结合这个来进行拦截。

代码位置:org.springframework.web.servlet.HandlerExecutionChain#applyPostHandle

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);}}}

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

这个是最后的视图的实现了,但是视图有好多种 如下:

  1. JSP 视图(InternalResourceView):使用 JSP(JavaServer Pages)作为视图技术。通过 InternalResourceViewResolver 视图解析器,将逻辑视图名映射到 JSP 文件,并将模型数据传递给 JSP 进行渲染。

  2. Thymeleaf 视图(ThymeleafView):使用 Thymeleaf 模板引擎进行视图渲染。Thymeleaf 是一个现代化的 Java 模板引擎,可以与 HTML、XML、JavaScript 等文件进行集成。

  3. Freemarker 视图(FreeMarkerView):使用 FreeMarker 模板引擎进行视图渲染。FreeMarker 是一个模板引擎,通过模板文件和数据模型生成最终的输出。

  4. Velocity 视图(VelocityView):使用 Apache Velocity 模板引擎进行视图渲染。Velocity 是一个基于 Java 的模板引擎,可用于生成文本、HTML、XML 等格式的输出。

  5. JSON 视图(MappingJackson2JsonView):将模型数据以 JSON 格式返回给客户端。通过 Jackson 库将模型数据序列化为 JSON 字符串,并通过 HttpServletResponse 返回给客户端。

  6. XML 视图(MarshallingView):将模型数据以 XML 格式返回给客户端。通过 JAXB(Java Architecture for XML Binding)将模型数据转换为 XML,并通过 HttpServletResponse 返回给客户端。

  7. 等等:除了上述视图以外,Spring 还支持自定义视图解析器和自定义视图类型,可以根据业务需求使用其他视图技术来进行视图渲染。

代码位置:org.springframework.web.servlet.DispatcherServlet#processDispatchResult

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;//不为空if (exception != null) {//匹配类型为ModelAndViewDefiningExceptionif (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);//转换成ModelAndViewDefiningExceptionmv = ((ModelAndViewDefiningException) exception).getModelAndView();}else {//获取自定义异常Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);//转换mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}// 不为空且 非被清除if (mv != null && !mv.wasCleared()) {//进行视图创建render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else { //证明被解析过了//打印日志if (logger.isDebugEnabled()) {logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +"': assuming HandlerAdapter completed request handling");}}//若请求是异步处理的(Concurrent handling started during a forward),则直接返回,不做后续处理。if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Concurrent handling started during a forwardreturn;}//不为空打印日志if (mappedHandler != null) {mappedHandler.triggerAfterCompletion(request, response, null);}}

后面就是一些异常和finally的处理,都是清空缓存的一些处理。不细看,有兴趣可以了解一下。

最后

springmvc非常重要,特别源码这块,涉及视图解析以及如何拦截等逻辑,这些核心特别是想在spring方面有所提升的同学,建议可以再细详深入debug一行一行把核心逻辑过一下,真的后面想走得深入或做架构方面及整合一些框架这些流程先后顺序必须懂,否则很容易吃一大亏。当然以上仅是本人看法。

参考文章:

https://www.yii666.com/blog/452442.html

https://blog.csdn.net/weixin_56644618/article/details/127594065

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

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

相关文章

python实现语音识别

1. 首先安装依赖库 pip install playsound # 该库用于播放音频文件 pip install speech_recognition # 该库用于语音识别 pip install PocketSphinx # 语音识别模块中只有sphinx支持离线的&#xff0c;使用该模块需单独安装 pip install pyttsx3 # 该库用于将文本转换为语音播…

app自动化测试(Android)

Capability 是一组键值对的集合&#xff08;比如&#xff1a;"platformName": "Android"&#xff09;。Capability 主要用于通知 Appium 服务端建立 Session 需要的信息。客户端使用特定语言生成 Capabilities&#xff0c;最终会以 JSON 对象的形式发送给 …

【C语言】入门——指针

目录 ​编辑 1.指针是什么 2.指针类型和指针运算 2.1指针-整数 2.2指针-指针 2.3指针的关系运算 3.野指针 3.1野指针成因 &#x1f44d;指针未初始化&#xff1a; &#x1f44d;指针越界访问&#xff1a; &#x1f44d;指针指向空间释放&#xff1a; 3.2如何规避野指针 …

以antd为例 React+Typescript 引入第三方UI库

本文 我们来说说 第三方UI库 其实应用市场上的 第三方UI库都是非常优秀的 那么 react 我们比较熟的肯定还是 antd 我们还是来用它作为演示 这边 我们先访问他的官网 https://3x.ant.design/index-cn 点击开始使用 在左侧 有一个 在 TypeScript 中使用 通过图标我们也可以看出…

【Linux从入门到精通】通信 | 管道通信(匿名管道 命名管道)

本派你文章主要是对进程通信进行详解。主要内容是介绍 为什么通信、怎么进行通信。其中本篇文章主要讲解的是管道通信。希望本篇文章会对你有所帮助。 文章目录 一、进程通信简单介绍 1、1 什么是进程通信 1、2 为什么要进行通信 1、3 进程通信的方式 二、匿名管道 2、1 什么是…

【OpenCV入门】第七部分——图像的几何变换

文章结构 缩放dsize参数实现缩放fx参数和fy参数实现缩放 翻转仿射变换平移旋转倾斜 透视cmath模块 缩放 通过resize()方法可以随意更改图像的大小比例&#xff1a; dst cv2.resize(src, dsize, fx, fy, interpolation)src&#xff1a; 原始图像dsize&#xff1a; 输出图像的…

华为云云服务器评测|云耀云服务器L实例快速部署MySQL使用指南

文章目录 前言云耀云服务器L实例介绍什么是云耀云服务器L实例&#xff1f;产品优势智能不卡顿价优随心用上手更简单管理更省心 快速购买查看优惠卷购买 安装MySQL重置密码安装更新apt的软件源列表安装MySQL 设置用户名、密码、权限配置安全组 总结 前言 哈喽大家好&#xff0c…

设置Linux CentOS7桥接模式连网

在虚拟机上安装centos7系统后&#xff0c;首要任务就是设置网络。 我们在文章《设置linux centos7连接网络》中讨论了如何设置NAT模式连网。本文讨论如何在设置好NAT模式后&#xff0c;调换为桥接模式。 仍采用图形化方式设置方法。 一、查看物理机网络 把虚拟机设置为桥接…

Doris workload group实战

1.创建测试用户&#xff1a;创建一个用户名为test&#xff0c;密码为test 的用户&#xff1a; create user test% IDENTIFIED BY test;给测试用户赋权&#xff1a;给用户test赋予数据库test.* 权限 grant SELECT_PRIV,LOAD_PRIV,CREATE_PRIV,ALTER_PRIV ON test.* TO test;开…

信息系统概述-生命周期-开发方法

信息系统概述-生命周期 考点分析信息系统概述信息系统分类企业目前所使用的具体的信息化系统信息系统的生命周期&#xff08;重要&#xff09;信息系统的开发方法&#xff08;重要&#xff09; 考点分析 每年都会考3分&#xff0c;2分会超纲 信息系统概述 信息系统分类 业务处理…

时间序列分析:掌握平稳性的概念

摄影&#xff1a;Chris Lawton on Unsplash 一、说明 平稳性是时间序列问题中的一个关键概念。它是指统计属性&#xff08;如均值、方差和协方差&#xff09;随时间变化的稳定性。为了建立有效的预测模型并确定时间序列数据中有意义的模式&#xff0c;了解平稳性的概念以及它与…

Spring MVC 之MVC 体系结构、什么是SpringMVC

Spring MVC简介 MVC 体系结构三层架构MVC设计模式 Spring MVC 是什么&#xff1f;扩展知识Spring模块Data Access/Integration&#xff08;数据访问/集成&#xff09;Web&#xff08;网络层&#xff09;AOP&#xff08;面向切面&#xff09;Messaging&#xff08;消息传送&…

文字验证码:简单有效的账号安全守卫!

前言 文字验证码不仅是一种简单易懂的验证方式&#xff0c;同时也是保护您的账号安全的重要工具。通过输入正确的文字组合&#xff0c;您可以有效地确认自己的身份&#xff0c;确保只有真正的用户才能访问您的账号。 HTML代码 <script src"https://cdn6.kgcaptcha.…

java八股文面试[数据库]——Page页的结构

mysql中数据是存储在物理磁盘上的&#xff0c;而真正的数据处理又是在内存中执行的。由于磁盘的读写速度非常慢&#xff0c;如果每次操作都对磁盘进行频繁读写的话&#xff0c;那么性能一定非常差。为了上述问题&#xff0c;InnoDB将数据划分为若干页&#xff0c;以页作为磁盘与…

算法通关村第十九关——最小路径和

LeetCode64. 给定一个包含非负整数的 m n 网格 grid,请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 输入&#xff1a;grid[[1,3,1],[1,5,1],[4,2,1]] 输出&#xff1a;7 解释&#xff1a;因为路径1→3→1→1→1的总和最小。 public int minPath…

Qt鼠标点击事件处理:按Escape键退出程序

创建项目 Qt 入门实战教程&#xff08;目录&#xff09; 首先&#xff0c;创建一个名称为QtKeyEscape的Qt默认的窗口程序。 参考 &#xff1a;Qt Creator 创建 Qt 默认窗口程序 Qt响应键盘Escape事件 打开Qt Creator >>编辑 >> 项目 >> Headers>> …

【iVX】iVX的低代码未来发展趋势:加速应用开发的创新之路

简介&#xff1a; 随着数字化转型的飞速发展&#xff0c;企业和组织对快速开发和交付高质量应用的需求越来越迫切。低代码开发平台作为一种创新的解决方案&#xff0c;极大地简化了应用程序的开发过程。在这一领域&#xff0c;iVX低代码平台作为领先的创业公司&#xff0c;正在…

Python小知识 - Python爬虫进阶:如何克服反爬虫技术

Python爬虫进阶&#xff1a;如何克服反爬虫技术 爬虫是一种按照一定的规则&#xff0c;自动抓取网页信息的程序。爬虫也叫网页蜘蛛、蚂蚁、小水滴&#xff0c;是一种基于特定算法的自动化程序&#xff0c;能够按照一定的规则自动的抓取网页中的信息。爬虫程序的主要作用就是从一…

记录 使用 git 克隆仓库报错:Warning: Permanently added‘github.com’ to the .....(ssh )

解决方法&#xff1a; 1. 新建空文件夹->右键->点击 Git Bash Here2. 输入 cd C&#xff1a;3. 输入 cat ~/.ssh/id_rsa.pub4. 输入 ssh-keygen重复回车&#xff0c;生成一个矩形&#xff0c;则说明公钥已经生成了。重复步骤3&#xff0c;生成publickey&#xff0c;右键…

Vue2基础学习

vue基础学习 Vue基础指令v-show 和 v-ifv-on指令v-bind指令v-bind操作classv-bind 操作stylev-for 指令练习&#xff1a;图书管理案例v-modelv-model原理 指令修饰符v-model指令修饰符click.stop-》阻止冒泡按键修饰符阻止默认行为 计算属性计算属性简写computed计算属性VS方法…