SpringBoot+拦截器(Interceptor)

记录一下SpringBoot的拦截器(Interceptor)使用

拦截器(Interceptor)是AOP面向切面编程的思想来实现的,对于只写代码的来说,具体如何实现不需要多关心,只需要关心如何去使用,会用在那些地方。

当http请求进入Springboot应用程序后,会去调用 Controller 层,在进入Controller处理应用业务之前,这个请求是需要通过经过拦截器(Interceptor)的,可能是一个也可能是多个,根据需求来,在Controller处理完业务逻辑之后,也要经过拦截器(Interceptor),最终将结果返回给请求着,即使不返回,也会经过的。

在拦截器(Interceptor)中,最常的应用有几个方面

1.校验token(最常用):根据请求token拦截校验是否允许访问特定资源 比如用户列表,开放的请求地址url需要额外的拦截器配置 比如登录
2.记录日志: 记录请求信息的日志数据
3.数据统计: 统计请求某一资源的次数,统计请求进入到响应的处理时间等
4.其他一些实际需求的功能

目前使用的是SpringBoot2.0.5版本

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.0.5.RELEASE</version>
	<relativePath/> <!-- lookup parent from repository -->
</parent>

自定义 Interceptor一般是实现 org.springframework.web.servlet.HandlerInterceptor接口
myw
源码是这样的

/*
 * Copyright 2002-2017 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
 *
 *      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.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;

/**
 * Workflow interface that allows for customized handler execution chains.
 * Applications can register any number of existing or custom interceptors
 * for certain groups of handlers, to add common preprocessing behavior
 * without needing to modify each handler implementation.
 *
 * <p>A HandlerInterceptor gets called before the appropriate HandlerAdapter
 * triggers the execution of the handler itself. This mechanism can be used
 * for a large field of preprocessing aspects, e.g. for authorization checks,
 * or common handler behavior like locale or theme changes. Its main purpose
 * is to allow for factoring out repetitive handler code.
 *
 * <p>In an asynchronous processing scenario, the handler may be executed in a
 * separate thread while the main thread exits without rendering or invoking the
 * {@code postHandle} and {@code afterCompletion} callbacks. When concurrent
 * handler execution completes, the request is dispatched back in order to
 * proceed with rendering the model and all methods of this contract are invoked
 * again. For further options and details see
 * {@code org.springframework.web.servlet.AsyncHandlerInterceptor}
 *
 * <p>Typically an interceptor chain is defined per HandlerMapping bean,
 * sharing its granularity. To be able to apply a certain interceptor chain
 * to a group of handlers, one needs to map the desired handlers via one
 * HandlerMapping bean. The interceptors themselves are defined as beans
 * in the application context, referenced by the mapping bean definition
 * via its "interceptors" property (in XML: a &lt;list&gt; of &lt;ref&gt;).
 *
 * <p>HandlerInterceptor is basically similar to a Servlet Filter, but in
 * contrast to the latter it just allows custom pre-processing with the option
 * of prohibiting the execution of the handler itself, and custom post-processing.
 * Filters are more powerful, for example they allow for exchanging the request
 * and response objects that are handed down the chain. Note that a filter
 * gets configured in web.xml, a HandlerInterceptor in the application context.
 *
 * <p>As a basic guideline, fine-grained handler-related preprocessing tasks are
 * candidates for HandlerInterceptor implementations, especially factored-out
 * common handler code and authorization checks. On the other hand, a Filter
 * is well-suited for request content and view content handling, like multipart
 * forms and GZIP compression. This typically shows when one needs to map the
 * filter to certain content types (e.g. images), or to all requests.
 *
 * @author Juergen Hoeller
 * @since 20.06.2003
 * @see HandlerExecutionChain#getInterceptors
 * @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter
 * @see org.springframework.web.servlet.handler.AbstractHandlerMapping#setInterceptors
 * @see org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor
 * @see org.springframework.web.servlet.i18n.LocaleChangeInterceptor
 * @see org.springframework.web.servlet.theme.ThemeChangeInterceptor
 * @see javax.servlet.Filter
 */
public interface HandlerInterceptor {

	/**
	 * Intercept the execution of a handler. Called after HandlerMapping determined
	 * an appropriate handler object, but before HandlerAdapter invokes the handler.
	 * <p>DispatcherServlet processes a handler in an execution chain, consisting
	 * of any number of interceptors, with the handler itself at the end.
	 * With this method, each interceptor can decide to abort the execution chain,
	 * typically sending a HTTP error or writing a custom response.
	 * <p><strong>Note:</strong> special considerations apply for asynchronous
	 * request processing. For more details see
	 * {@link org.springframework.web.servlet.AsyncHandlerInterceptor}.
	 * <p>The default implementation returns {@code true}.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler chosen handler to execute, for type and/or instance evaluation
	 * @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.
	 * @throws Exception in case of errors
	 */
	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return true;
	}

	/**
	 * Intercept the execution of a handler. Called after HandlerAdapter actually
	 * invoked the handler, but before the DispatcherServlet renders the view.
	 * Can expose additional model objects to the view via the given ModelAndView.
	 * <p>DispatcherServlet processes a handler in an execution chain, consisting
	 * of any number of interceptors, with the handler itself at the end.
	 * With this method, each interceptor can post-process an execution,
	 * getting applied in inverse order of the execution chain.
	 * <p><strong>Note:</strong> special considerations apply for asynchronous
	 * request processing. For more details see
	 * {@link org.springframework.web.servlet.AsyncHandlerInterceptor}.
	 * <p>The default implementation is empty.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler handler (or {@link HandlerMethod}) that started asynchronous
	 * execution, for type and/or instance examination
	 * @param modelAndView the {@code ModelAndView} that the handler returned
	 * (can also be {@code null})
	 * @throws Exception in case of errors
	 */
	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}

	/**
	 * Callback after completion of request processing, that is, after rendering
	 * the view. Will be called on any outcome of handler execution, thus allows
	 * for proper resource cleanup.
	 * <p>Note: Will only be called if this interceptor's {@code preHandle}
	 * method has successfully completed and returned {@code true}!
	 * <p>As with the {@code postHandle} method, the method will be invoked on each
	 * interceptor in the chain in reverse order, so the first interceptor will be
	 * the last to be invoked.
	 * <p><strong>Note:</strong> special considerations apply for asynchronous
	 * request processing. For more details see
	 * {@link org.springframework.web.servlet.AsyncHandlerInterceptor}.
	 * <p>The default implementation is empty.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler handler (or {@link HandlerMethod}) that started asynchronous
	 * execution, for type and/or instance examination
	 * @param ex exception thrown on handler execution, if any
	 * @throws Exception in case of errors
	 */
	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}

}

preHandler(HttpServletRequest request, HttpServletResponse response, Object handler) 方法

这个方法是在http请求进来之后处理之前被调用的 返回是 Boolean 类型,返回 false 时,表示直接掐断请求,直接结束了,那么也就不会进入Controller;返回 true 时,会继续进入下一个拦截器同样的方法(preHandler)直到所有的preHandler方法都通过才进入Controller。

postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 方法
http请求处理好业务后,从Controller控制器返回后调用,视图解析器之前,也就是在渲染数据返回数据之前被调用。

afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法
当对应的拦截器preHandle返回值是 true 时才会在整个请求结束之后执行,视图解析器之后,也可以理解为postHandler后

自定义CustomHandlerInterceptor.java

package boot.example.interceptor.config;



import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CustomHandlerInterceptor implements HandlerInterceptor{

	/**
	 * Controller逻辑执行之前进行拦截
	 */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle:");
        String uri = request.getRequestURI();
        System.out.println("拦截的uri:"+uri);
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            System.out.println("拦截 Controller:"+ handlerMethod.getBean().getClass().getName());
            System.out.println("拦截方法:"+handlerMethod.getMethod().getName());
        }
        //拦截token,没有token的不允许继续往下执行
        String token = request.getHeader("token");
        if(token == null){
            return false;
        }
        long startTime = System.currentTimeMillis();
        System.out.println("start-time: " + startTime);
        request.setAttribute("startTime", startTime);
        return true;
    }
    
    /**
     * Controller逻辑执行完毕但是视图解析器还为进行解析之前进行拦截
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle:");
        long endTime = System.currentTimeMillis();
        long startTime = (Long) request.getAttribute("startTime");
        System.out.println("post-end-time: " + endTime);
        System.out.println("post-差值ms:" + (endTime - startTime));
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }
    
    /**
     * Controller逻辑和视图解析器执行完毕进行拦截
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion:");
        long startTime = (Long) request.getAttribute("startTime");
        long endTime = System.currentTimeMillis();
        System.out.println("after-end-time: " + endTime);
        System.out.println("after-差值ms:" + (endTime - startTime));
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

配置拦截器
bean方式(不推荐)

package boot.example.interceptor.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 使用bean的方式
 *
 */
@Configuration
public class InterceptorConfig {

	
    @Bean
    public CustomHandlerInterceptor customHandlerInterceptor(){
        return new CustomHandlerInterceptor();
    }

    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                registry.addInterceptor(customHandlerInterceptor())
                        .addPathPatterns("/**")
                        //.excludePathPatterns("/")
                        .excludePathPatterns("/index/**");
            }
        };
    }
    
}

重写方式来配置(推荐)

package boot.example.interceptor.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 重写方式来配置,推荐
 *
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Bean
    public CustomHandlerInterceptor customHandlerInterceptor(){
        return new CustomHandlerInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(customHandlerInterceptor())
                .addPathPatterns("/**")
                //.excludePathPatterns("/")
                .excludePathPatterns("/index/**");
    }
    
}

测试-BootIndexController.java

package boot.example.interceptor.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class BootIndexController {

	@RequestMapping("/")
	@ResponseBody
	public String index() {
		return "index";
	}

	@RequestMapping("/index/hello")
	@ResponseBody
	public String indexHello() {
		return "hello world";
	}

	@RequestMapping("/inter/hello")
	@ResponseBody
	public String interHello(){
		return "inter world";
	}

	@RequestMapping("/inter/sleep")
	@ResponseBody
	public String sleepHello() throws InterruptedException {
		// 假装处理2s
		Thread.sleep(2000);
		return "inter world";
	}

}

表示拦截所有的url请求

.addPathPatterns(“/**”)

表示放开拦截的url请求

.excludePathPatterns(“/”)
.excludePathPatterns(“/index/**”)

请求无拦截的情况,直接返回数据,不会经过拦截器
myw

请求有拦截的情况,没有token是访问不到资源的
myw
查看控制台
myw
随意写个token
myw
可以看到拦截通过了 执行了对应的三个方法 在这里有测试的故意延迟
myw
一个应用可能有多个拦截器,有的时候拦截器需要执行的先后顺序,默认的情况下是按照注册的先后顺序
拦截器ONE
CustomOrderOneHandlerInterceptor.java

package boot.example.interceptor.config;


import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class CustomOrderOneHandlerInterceptor implements HandlerInterceptor{

	/**
	 * Controller逻辑执行之前进行拦截
	 */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("CustomOrderOneHandlerInterceptor: preHandle");
        return true;
    }
    
    /**
     * Controller逻辑执行完毕但是视图解析器还为进行解析之前进行拦截
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("CustomOrderOneHandlerInterceptor: postHandle");
    }
    
    /**
     * Controller逻辑和视图解析器执行完毕进行拦截
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("CustomOrderOneHandlerInterceptor: afterCompletion");
    }
}

拦截器TWO
CustomOrderTwoHandlerInterceptor.java

package boot.example.interceptor.config;


import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CustomOrderTwoHandlerInterceptor implements HandlerInterceptor{

	/**
	 * Controller逻辑执行之前进行拦截
	 */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("CustomOrderTwoHandlerInterceptor: preHandle");
        return true;
    }
    
    /**
     * Controller逻辑执行完毕但是视图解析器还为进行解析之前进行拦截
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("CustomOrderTwoHandlerInterceptor: postHandle");
    }
    
    /**
     * Controller逻辑和视图解析器执行完毕进行拦截
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("CustomOrderTwoHandlerInterceptor: afterCompletion");
    }
}

拦截器配置
OderInterceptorConfig.java

package boot.example.interceptor.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 重写方式来配置,推荐
 *
 */
@Configuration
public class OderInterceptorConfig implements WebMvcConfigurer {

    @Bean
    public CustomOrderOneHandlerInterceptor customOrderOneHandlerInterceptor(){
        return new CustomOrderOneHandlerInterceptor();
    }
    @Bean
    public CustomOrderTwoHandlerInterceptor customOrderTwoHandlerInterceptor(){
        return new CustomOrderTwoHandlerInterceptor();
    }


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(customOrderOneHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index/**");
        registry.addInterceptor(customOrderTwoHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index/**");
    }
    
}

可以看到customOrderOneHandlerInterceptor()在customOrderTwoHandlerInterceptor()之前,启动程序打开访问打开控制台查看打印日志确认
myw
交换顺序可以看到TWO在ONE之前执行
myw
使用order自定义顺序 可以看到值小的比值大的先执行
myw
使用拦截器重定向
当访问

http://localhost:8080

重定向

http://localhost:8080/home

CustomRedirectOneHandlerInterceptor.java

package boot.example.interceptor.config;


import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class CustomRedirectOneHandlerInterceptor implements HandlerInterceptor{

	/**
	 * Controller逻辑执行之前进行拦截
	 */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("CustomRedirectOneHandlerInterceptor: preHandle");
        System.out.println("CustomRedirectOneHandlerInterceptor Request url: " + request.getRequestURL());
        response.sendRedirect(request.getContextPath()+ "/home");
        return false;
    }
    
    /**
     * Controller逻辑执行完毕但是视图解析器还为进行解析之前进行拦截
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("CustomRedirectOneHandlerInterceptor: postHandle");
    }
    
    /**
     * Controller逻辑和视图解析器执行完毕进行拦截
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("CustomRedirectOneHandlerInterceptor: afterCompletion");
    }
}


CustomRedirectTwoHandlerInterceptor.java


package boot.example.interceptor.config;


import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CustomRedirectTwoHandlerInterceptor implements HandlerInterceptor{

	/**
	 * Controller逻辑执行之前进行拦截
	 */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("CustomRedirectTwoHandlerInterceptor: preHandle");
        System.out.println("CustomRedirectTwoHandlerInterceptor Request url: " + request.getRequestURL());
        return true;
    }
    
    /**
     * Controller逻辑执行完毕但是视图解析器还为进行解析之前进行拦截
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("CustomRedirectTwoHandlerInterceptor: postHandle");
    }
    
    /**
     * Controller逻辑和视图解析器执行完毕进行拦截
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("CustomRedirectTwoHandlerInterceptor: afterCompletion");
    }
}

BootIndexController.java

package boot.example.interceptor.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class BootIndexController {

	@RequestMapping("/")
	@ResponseBody
	public String index() {
		return "/";
	}

	@RequestMapping("/home")
	@ResponseBody
	public String indexHello() {
		return "/home";
	}


}

用postman访问
myw
浏览器输入http://localhost:8080会跳转的
myw
myw

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

UE蓝图 RPG动作游戏(一) day15

角色状态制作 制作角色动画混合空间 创建一个动混合空间 添加动作在混合空间 动画蓝图 创建一个动画蓝图 先使用混合空间进行移动&#xff0c;后续优化后再使用状态机 编写垂直水平速度逻辑初始化&#xff0c;获取到此动画的角色组件 获取Horizontal与Vertical的速度逻辑 …

股票价格预测 | Python实现Autoformer, FEDformer和PatchTST等模型用于股价预测

文章目录 效果一览文章概述环境描述源码设计效果一览 文章概述 Autoformer、FEDformer和PatchTST是一些用于时间序列预测,包括股价预测的模型。它们都是在Transformer模型的基础上进行了改进和扩展,以更好地适应时间序列数据的特点。 Autoformer:Autoformer是一种自适应Tran…

软件测试/测试开发丨Python 面向对象编程思想

面向对象是什么 Python 是一门面向对象的语言面向对象编程&#xff08;OOP&#xff09;&#xff1a;Object Oriented Programming 所谓的面向对象&#xff0c;就是在编程的时候尽可能的去模拟真实的现实世界&#xff0c;按照现实世界中的逻辑去处理问题&#xff0c;分析问题中…

继续声明 | 连声明都抄,谁抄袭谁,一目了然,现在竟然恬不知耻的反咬一口。

继续声明 | 连声明都抄&#xff0c;谁抄袭谁&#xff0c;一目了然&#xff0c;现在竟然恬不知耻的反咬一口。 一、本账号为《机器学习之心》博主CSDN唯一官方账号&#xff0c;唯一联系方式见文章底部。 二、《机器学习之心》博主未授权任何第三方账号进行模型合作、程序设计、…

【Java进阶篇】什么是UUID,能不能保证唯一?

什么是UUID&#xff0c;能不能保证唯一? ✔️典型解析✔️优缺点 ✔️各个版本实现✔️V1.基于时间戳的UUID✔️V2.DCE(Distributed Computing Environment)安全的UUID✔️V3.基于名称空间的UUID(MD5)✔️V4.基于随机数的UUID✔️V5.基于名称空间的UUID(SHA1)✔️各个版本总结…

我在Vscode学OpenCV 图像处理四(轮廓查找 cv2.findContours() cv2.drawContours())-- 待补充

图像处理四&#xff08;轮廓查找&#xff09; 一、前言1.1 边缘检测和轮廓查找的区别是什么1.1.1 边缘检测&#xff1a;1.1.2 轮廓查找&#xff1a; 1.2 边缘检测和轮廓查找在图像处理中的关系和流程 二、查找并绘制轮廓2.1 cv2.findContours()&#xff1a;2.1.1 详细介绍&…

爬虫工作量由小到大的思维转变---<第三十章 Scrapy Redis 第一步(配置同步redis)>

前言: 要迈向scrapy-redis进行编写了;首要的一步是,如何让他们互通?也就是让多台电脑连一个任务(这后面会讲); 现在来做一个准备工作,配置好redis的同步!! 针对的是windows版本的redis同步,实现主服务和从服务共享一个redis库; 正文: 正常的redis for windows 的安装这里就…

C#,入门教程(03)——Visual Studio 2022编写彩色Hello World与动画效果

C#&#xff0c;入门教程(01)—— Visual Studio 2022 免费安装的详细图文与动画教程https://blog.csdn.net/beijinghorn/article/details/123350910 C#&#xff0c;入门教程(02)—— Visual Studio 2022开发环境搭建图文教程https://blog.csdn.net/beijinghorn/article/detail…

Hampel滤波器是一种基于中位数的离群值检测方法【异常值检测方法】

Hampel滤波器是一种基于中位数的离群值检测方法&#xff0c;也是一种线性滤波器&#xff0c;由德国数学家和统计学家John Hampel在1974年提出。它主要用于去除信号中的脉冲噪声&#xff0c;具有很强的抗干扰能力&#xff0c;因此被广泛应用于信号处理、通信系统等领域。 1.基本…

SpringBoot定时监听RocketMQ的NameServer

问题分析 自己在测试环境部署了RocketMQ&#xff0c;发现namesrv很容易挂掉&#xff0c;于是就想着监控&#xff0c;挂了就发邮件通知。查看了rocketmq-dashboard项目&#xff0c;发现只能监控Broker&#xff0c;遂放弃这一路径。于是就从报错的日志入手&#xff0c;发现最终可…

【Redis-08】Redis主从复制的实现原理

在Redis中&#xff0c;可以通过slaveof命令或者设置slaveof选项实现两台Redis服务器的主从复制&#xff0c;比如我们有两个Redis机器&#xff0c;地址分别是 127.0.0.1:6379 和 127.0.0.1:6380&#xff0c;现在我们在前者上面执行&#xff1a; 127.0.0.1:6379 > SLAVEOF 12…

STC8H系列单片机入门教程之NVC系列语音播报模块(九)

一、模块简述 ● 模组支持3.3V和5V单片机供电系统 ● 标准2.54MM间距排针与外部连接 ● 支持喇叭0.5W/8欧 ● 适合用于超声波距离、电子秤重量、时钟时间、温度、球赛比分等语音播报 二、引脚说明 序号 名称 说明 1 VCC 电源正&#xff08;3.3V-5V&#…

『精』CSS 小技巧之BEM规范

『精』CSS 小技巧之BEM规范 文章目录 『精』CSS 小技巧之BEM规范一、什么是BEM&#xff1f;二、BEM要怎么用&#xff1f;三、不用BEM会少个胳膊吗&#xff1f;&#x1f48a;四、Sass与BEM的结合&#x1f388;五、块与修饰符应放在一块&#x1f47f;参考资料&#x1f498;推荐博…

Android Matrix画布Canvas旋转Rotate,Kotlin

Android Matrix画布Canvas旋转Rotate&#xff0c;Kotlin private fun f1() {val originBmp BitmapFactory.decodeResource(resources, R.mipmap.pic).copy(Bitmap.Config.ARGB_8888, true)val newBmp Bitmap.createBitmap(originBmp.width, originBmp.height, Bitmap.Config.…

vscode调用HTML文件

vscode实现对HTML文件调用 创建html文件下载拓展内容点击拓展查找需要的拓展 导入html代码设置默认打开浏览器运行结果参考文献 做数据库课设的内容,尝试一些自己没有接触过的东西,了解如何创建一个网站以及数据库的一个应用 创建html文件 创建一个html的文件,加入后缀名 下…

docker搭建minio集群,集群分享文件URL踩坑问题

一、环境准备 3台机器&#xff0c;Ip地址依次为IP1,IP2,IP3二、设置服务器时间同步 Minio集群需要各个节点的时间保持同步&#xff0c;使用NTP作为时间同步服务&#xff0c;这里以Minio-1&#xff08;IP1&#xff09;为上游服务器&#xff0c;其它2个节点为下游服务器&#x…

HarmonyOS 组件通用属性之通用事件 文档参数讲解(点击事件)

我们组件中 会有很多通用的信息和方法 那么 首先 我们看通用事件 通用事件中 最常用的就是我们的点击事件 比如说 我们之前常写的 组件.onClick(()>{//事件逻辑 })但是 我们之前 都没有用它接参数 我们可以这样 Button("跳转").onClick((ewat: ClickEvent)>…

Matplotlib_艺术画笔见乾坤

文章目录 一、概述&#xff1a;1.matplotlib的三层api2.Artist的分类3.matplotlib标准用法 二、自定义你的Artist对象1.Artist属性 在图形中的每一个元素都对应着一个matplotlib Artist&#xff0c;且都有其对应的配置属性列表。2.属性调用方式 三、基本元素 - primitives1.2DL…

[MySQL] MySQL中的事物

本片文章对MySQL中的事物进行了详解。其中包含了事物的特性、为什么要有事物、查看事物版本支持、事物常见操作、事物的隔离界别等等内容进行详细举例解释。同时还深入讲解了事物的隔离性&#xff0c;模拟实现MVCC多版本并发控制&#xff0c;也讲解了RR和RC的本质区别。希望本篇…

汽车保养软件app开发步骤

“增强您的动力&#xff0c;为您的旅程加油——每一刻都讲述着关爱的故事。构建汽车维护软件app&#xff0c;为您的车辆提供数字化的维修站&#xff0c;从而开启长寿之路。智能驾驶、互联驾驶、自信驾驶。” 疯狂地搜索旧收据并猜测上次换油时间的日子已经一去不复返了。如果您…