【springboot进阶】基于starter项目构建(二)构建starter项目-web

news2024/11/19 19:36:58

目录

一、创建 web-spring-boot-starter 项目

二、添加 pom 文件依赖

三、构建配置

1. rest模板配置 RestTemplateConfig

2. 统一异常处理 BackendGlobalExceptionHandler

3. 统一返回数据结构

4. jwt鉴权处理

5. 请求日志切面处理 WebLogAspect

6. 邮件配置 BackendMailConfig

7. mvc 配置

四、加载自动化配置

五、打包

六、使用


这个系列讲解项目的构建方式,主要使用 父项目 parent 和 自定义 starter 结合。项目使用最新的 springboot3 和 jdk19。本系列的代码仓库看查看 gitee 仓库 的 starter 目录。

这篇我们开始学习创建属于自己的 starter ,实现一些常用模块的封装和自动配置,模拟 spirngboot 的 starter 模式,看看怎么将项目构建为 web starter 

一、创建 web-spring-boot-starter 项目

一般官方的 starter 是以 spring-boot-starter-{模块名},所以我们这边自定义的时候,区分于官方的命令,将模块名放在前面。

我们还是以一个 springboot 项目的方式来创建,如下图。

 选择目前最新的3.0.0版本,下面的依赖不需要勾选,等下我们再添加。

二、添加 pom 文件依赖

先贴上 pom.xml 代码,这里使用到上一章介绍的 backend-parent 父级项目作为这里的 parent

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.liurb.springboot.scaffold</groupId>
		<artifactId>backend-parent</artifactId>
		<version>1.0.0</version>
		<relativePath />
	</parent>

	<artifactId>web-spring-boot-starter</artifactId>
	<version>1.0.0</version>
	<name>web-spring-boot-starter</name>
	<description>web-spring-boot-starter</description>

	<properties>
		<common-spring-boot-starter.version>1.0.0</common-spring-boot-starter.version>
	</properties>

	<dependencies>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-json</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
			<exclusions>
				<exclusion>
					<artifactId>tomcat-embed-el</artifactId>
					<groupId>org.apache.tomcat.embed</groupId>
				</exclusion>
			</exclusions>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

		<dependency>
			<groupId>org.liurb.springboot.scaffold</groupId>
			<artifactId>common-spring-boot-starter</artifactId>
			<version>${common-spring-boot-starter.version}</version>
			<optional>true</optional>
		</dependency>

		<dependency>
			<groupId>com.squareup.okhttp3</groupId>
			<artifactId>okhttp</artifactId>
		</dependency>

		<dependency>
			<groupId>javax.mail</groupId>
			<artifactId>mail</artifactId>
		</dependency>

		<dependency>
			<groupId>com.auth0</groupId>
			<artifactId>java-jwt</artifactId>
		</dependency>

		<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>

		<dependency>
			<groupId>org.mapstruct</groupId>
			<artifactId>mapstruct</artifactId>
			<scope>compile</scope>
		</dependency>

		<dependency>
			<groupId>org.mapstruct</groupId>
			<artifactId>mapstruct-processor</artifactId>
			<scope>compile</scope>
		</dependency>

		<!--表示两个项目之间依赖不传递;不设置optional或者optional是false,表示传递依赖-->
		<!--例如:project1依赖a.jar(optional=true),project2依赖project1,则project2不依赖a.jar-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-autoconfigure</artifactId>
			<optional>true</optional>
		</dependency>

	</dependencies>

	<build>
		<plugins>

			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<classifier>exec</classifier>
				</configuration>
			</plugin>

		</plugins>
	</build>

</project>

依赖说明:

1)移除 spring-boot-starter-json :因为项目将使用 fastjson 或者 gson ,所以这里要移除springboot默认的 jackson 依赖。

2)okhttp:RestTemplate 请求模板使用 okhttp3 

3)mail:邮件模块,主要用于系统异常的邮件通知功能

4)java-jwt:jwt模板,业务系统主要使用 jwt 鉴权

5)mapstruct:类复制工具,强烈推荐,不要再使用低效的 BeanUtils 工具了

三、构建配置

搭建好的 starter 目录与代码如下图,这个项目我们主要配置的是与 web 项目环境相关的内容,例如:rest请求模板的配置、统一异常处理、统一返回数据结构、jwt鉴权处理、请求返回日志打印、邮件发送配置、mvc相关配置等。

1. rest模板配置 RestTemplateConfig

@ConditionalOnClass({RestTemplate.class})
@ConditionalOnProperty(
        prefix = "web.starter.rest-template.config",
        value = {"enabled"},
        matchIfMissing = true)
@AutoConfiguration
public class RestTemplateConfig {

    // todo ...    

}

这里有一个配置开关,可以在配置文件中启用这个配置。我们一般在这个配置类设置一些请求超时时间、https证书问题的配置、日志打印拦截器等,具体可以查看源码。

2. 统一异常处理 BackendGlobalExceptionHandler

public abstract class BackendGlobalExceptionHandler {


    @Resource
    MailAccount mailAccount;    

    // todo ...


    /**
     * 邮件收件人列表
     *
     * @return
     */
    public abstract List<String> tos();

    /**
     * 邮件标题
     *
     * @return
     */
    public abstract String subject();

    /**
     * 是否发送邮件
     *
     * @return
     */
    public abstract boolean isSendMail();

    /**
     * 发送邮件前处理
     *
     * @param requestUri
     * @param errorMsg
     * @return
     */
    public abstract boolean sendMailBefore(String requestUri, String errorMsg);

    /**
     * 发送邮件后处理
     *
     * @param requestUri
     * @param errorMsg
     * @return
     */
    public abstract void sendMailAfter(String requestUri, String errorMsg);

}

这是一个抽象类,并定义了几个与发邮件相关的抽象方法,让业务子类去实现相关的异常邮件配置。其中 发送邮件前处理 和 发送邮件后处理,这两个抽象方法主要用于限制发送异常邮件的次数,要不然邮箱就很容易满了。

3. 统一返回数据结构

这里主要由三个类实现:

  • 统一结果返回实体 Result
@Data
public class Result<T> {

    private Integer code;
    private String msg;
    private Boolean success;
    private T data;

}

code为状态码,msg为消息,success为判断此次请求是否成功,T为返回的数据结果 

  • 统一返回信息枚举 ResultEnum
@Getter
@AllArgsConstructor
public enum ResultEnum {

    /**
     * 默认失败
     */
    DEFAULT_FAIL(-99, "失败"),
    /**
     * 接口调用错误返回
     *
     */
    API_ERROR(-2,"接口调用错误"),
    /**
     * 系统错误返回
     *
     */
    SYS_ERROR(-1,"系统错误"),
    /**
     * 成功返回
     */
    SUCCESS(0,"成功"),
    ;
    
    final Integer code;
    final String msg;
}

 可以列举一些系统公共的状态码。

  • 统一结果返回帮助类 ResultUtil
public class ResultUtil {

    /**
     * 成功返回
     *
     * @param object
     * @return
     */
    public static Result success(Object object){
        Result result = new Result();
        result.setSuccess(true);
        result.setCode(ResultEnum.SUCCESS.getCode());
        result.setMsg(ResultEnum.SUCCESS.getMsg());
        result.setData(object);
        return result;
    }

    public static Result success(Integer code, String msg, Object object){
        Result result = new Result();
        result.setSuccess(true);
        result.setCode(code);
        result.setMsg(msg);
        result.setData(object);
        return result;
    }

    /**
     * 成功但不带数据
     *
     * @return
     */
    public static Result success(){
        return success(null);
    }

    /**
     * 默认失败返回
     *
     * @param msg
     * @return
     */
    public static Result fail(String msg) {
        Result result = new Result();
        result.setSuccess(false);
        result.setCode(ResultEnum.DEFAULT_FAIL.getCode());
        result.setMsg(msg);
        return result;
    }

    /**
     * 失败返回
     *
     * @param code
     * @param msg
     * @return
     */
    public static Result fail(Integer code, String msg){
        Result result = new Result();
        result.setSuccess(false);
        if (null == code) {
            code = ResultEnum.DEFAULT_FAIL.getCode();
        }
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }


}

定义请求 成功 和 失败 的返回信息结构。

具体的使用方法,可以查看这篇文章 接口的统一结果返回

4. jwt鉴权处理

使用 注解 + aop 的方式,对请求头的 token 信息鉴权并转为系统用户信息。

  • 切入点注解 JwtUserAnnotation
  • aop 处理 JwtUserAspect
  • 系统用户信息类 JwtUser

具体的使用方法,可查看这篇文章aaa

5. 请求日志切面处理 WebLogAspect

使用 aop 方式,对 controller 控制器层的请求和返回分别处理日志的打印。

@Aspect
@Component
@Slf4j
@Order(10)
public class WebLogAspect {

    /**
     * 标记
     */
    private String requestId;
    /**
     * 进入方法时间戳
     */
    private Long startTime;
    /**
     * 方法结束时间戳(计时)
     */
    private Long endTime;

    public WebLogAspect() {
    }


    /**
     * 定义请求日志切入点,其切入点表达式有多种匹配方式,这里是指定路径
     */
    @Pointcut("execution(public * org.liurb..*.controller..*Controller.*(..))")
    public void webLogPointcut() {
    }

    /**
     * 前置通知:
     * 1. 在执行目标方法之前执行,比如请求接口之前的登录验证;
     * 2. 在前置通知中设置请求日志信息,如开始时间,请求参数,注解内容等
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLogPointcut()")
    public void doBefore(JoinPoint joinPoint) {

        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //创建标记
        requestId = request.getHeader("backend-request-id");
        if (StrUtil.isBlank(requestId)) {
            requestId = StrUtil.uuid().replace("-","").toUpperCase();
        }
        //打印请求的内容
        startTime = System.currentTimeMillis();
        log.info("{} 请求Url : {}", requestId, request.getRequestURL().toString());
        String userAgent = request.getHeader(HttpHeaders.USER_AGENT);
        log.info("{} 请求UA : {}", requestId, userAgent);
        log.info("{} 请求ip : {}", requestId, RequestHttpUtil.getIpAddress(request));
        log.info("{} 请求方法 : {}", requestId, joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        log.info("{} 请求参数 : {}", requestId, Arrays.toString(joinPoint.getArgs()));
    }

    /**
     * 返回通知:
     * 1. 在目标方法正常结束之后执行
     * 1. 在返回通知中补充请求日志信息,如返回时间,方法耗时,返回值,并且保存日志信息
     *
     * @param ret
     */
    @AfterReturning(returning = "ret", pointcut = "webLogPointcut()")
    public void doAfterReturning(Object ret) {
        endTime = System.currentTimeMillis();
        log.info("{} 请求耗时:{}", requestId, (endTime - startTime) + "ms");
        // 处理完请求,返回内容
        log.info("{} 请求返回 : {}", requestId, ret);
    }

    /**
     * 异常通知:
     * 1. 在目标方法非正常结束,发生异常或者抛出异常时执行
     * 1. 在异常通知中设置异常信息,并将其保存
     *
     * @param throwable
     */
    @AfterThrowing(value = "webLogPointcut()", throwing = "throwable")
    public void doAfterThrowing(Throwable throwable) {
        // 打印异常日志记录
        log.error("{} 抛出异常:{}", requestId, throwable.getMessage(), throwable);
    }

}

doBefore 方法打印请求时的参数信息,也可以将IP、UA等信息打印出来。

doAfterReturning 方法打印返回时的数据信息,并记录这次请求的耗时。

doAfterThrowing 方法打印异常的情况信息。

6. 邮件配置 BackendMailConfig

邮件使用的是 hutool 的工具包,它是一个门面,具体的实现需要我们在项目中引入 javax.mail 的包才能够使用。

使用的时候只需要在项目的 resources 目录下创建 mail.setting 文件即可,具体的配置项可以参考项目代码。

7. mvc 配置

1)web配置类 BackendWebMvcConfig

public class BackendWebMvcConfig extends WebMvcConfigurationSupport {

    /**
     * 添加静态资源
     *
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/")
                .addResourceLocations("classpath:/templates/")
                .addResourceLocations("classpath:/META-INF/resources/");
    }

    /**
     * 跨域支持
     *
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        //对哪些目录可以跨域访问
        registry.addMapping("/**")
                //允许哪些网站可以跨域访问
                .allowedOrigins("*")
                //允许哪些方法
                .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS", "HEAD")
                .maxAge(3600 * 24);
    }

}

这是一个基类,继承 WebMvcConfigurationSupport ,重写了 添加静态资源方法 addResourceHandlers 和 跨域支持方法 addCorsMappings

2)公共过滤器 BackendHttpServletRequestFilter

public abstract class BackendHttpServletRequestFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper((HttpServletResponse) response);

        try {

            HttpServletRequest httpRequest = (HttpServletRequest) request;

            String contentType = httpRequest.getContentType();

            if (StrUtil.isNotBlank(contentType) && contentType.contains(ContentType.MULTIPART.getValue())) {//不处理multipart/form-data类型的请求流复制

                doFilterMore(request, response, chain);

                chain.doFilter(request, responseWrapper);

            } else {

                CachedBodyHttpServletRequest cachedBodyHttpServletRequest =
                        new CachedBodyHttpServletRequest((HttpServletRequest) request);

                doFilterMore(request, response, chain);

                chain.doFilter(cachedBodyHttpServletRequest, responseWrapper);

            }

        } finally {

            responseWrapper.copyBodyToResponse();

        }
    }

    /**
     * 提供一个方法可以对请求进行更多的过滤操作
     *
     * 返回false过滤拦截
     *
     * @param request
     * @param response
     * @param chain
     */
    public abstract void doFilterMore(ServletRequest request, ServletResponse response, FilterChain chain);

}

定义为抽象类,并有一个抽象方法 doFilterMore,用于子类继续处理过滤的逻辑,如果不想请求链继续,就抛出异常即可。

过滤器已经处理并解决 输入流只能读取一次的问题,对于这个问题的处理,具体可查看这篇文章 HttpServletRequest输入流只能读取一次的问题

四、加载自动化配置

从 springboot 2.7 的时候,spring.factories 这种方式已经标记为过期的,所以从 springboot3 开始已经完全移除了。所以我们要创建 org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件。

 这里并没有将 web配置类 BackendWebMvcConfig 加入其中,因为可能每个业务系统对于 mvc 相关的配置都会有比较大的差异,所以相关的配置还是交由业务系统来处理。

五、打包

这时候执行 mvn package & mvn install ,这样就将这个 starter 安装到本地仓库中。

六、使用

可以看 gitee 仓库的 springboot-advance-demo 项目。

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

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

相关文章

mysql数据同步到elasticsearch数据解决方案

mysql数据同步到elasticsearch数据解决方案 问题场景 1.分库分表后多关联或者多条件查找效率低下&#xff0c;例如2b场景的查询&#xff0c;导出等需要多条件查询&#xff0c;继续用分库分表话效率低下。 2.数据量太多需要转移非关系型数据库elasticsearch存储 3.其他数据转…

AI 实战篇 |基于 AI开放平台实现 【植物识别】 功能,成为行走的百科全书

&#x1f3ac; 博客主页&#xff1a;https://xiaoy.blog.csdn.net &#x1f3a5; 本文由 呆呆敲代码的小Y 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;Unity系统学习专栏 &#x1f332; 游戏制作专栏推荐&#xff1a;游戏制作 &…

代码随想录刷题记录day40 爬楼梯+零钱兑换+完全平方数·

代码随想录刷题记录day40 爬楼梯零钱兑换完全平方数 参考&#xff1a;代码随想录 70. 爬楼梯 思想 当作完全背包问题&#xff0c;物品是1&#xff0c;2&#xff0c;可以无限次取用&#xff0c;背包的容量是n 1.dp[j]表示容量为j的背包&#xff0c;装满有dp[j]次 2.dp[j]dp…

【图像处理】opencv | 图像的二值化操作| cv2.threshold() | cv2.adaptiveThreshold()

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、cv2.threshold()阈值操作函数1.1、初见1.2、阈值不同时的效果二、cv2.adaptiveThreshold()自适应阈值操作函数2.1、初见2.2、固定blocksize,改变C值大小的实…

104-127-linux-vim-shell基础

104-linux-shell: 1.shell基础 分类&#xff1a;linux使用Bash&#xff0c;可通过vi /etc/shells查看linux支持的shell类型。 1、echo [rootlocalhost ~]#echo [选项] [输出内容] 选项:-e:支持反斜线控制的字符转换&#xff08;具体百度吧)-n:取消输出后行末的换行符号&…

Python实现ALO蚁狮优化算法优化支持向量机分类模型(SVC算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 蚁狮优化(Ant Lion Optimizer&#xff0c;ALO)算法是Mirjalili于2015提出的一种新型元启发式群智能算法…

185.基于Django的富文本编辑器安装与使用

1.DjangoUeditor 1.1 概述 富文本编辑器&#xff0c;在web开发中必不可少&#xff0c;但是django没有自带富文本编辑器&#xff0c;因此我们需要使用第三方库&#xff0c;这里使用DjangoUeditor DjangoUditor是百度开源的在线HTML编辑器&#xff0c;功能非常强大&#xff0c;像…

VUEElement简单介绍。

目录 一、VUE 1、基本介绍 2、Vue 指令 3、生命周期 二、Element 1、基本介绍 2、Element 布局 一、VUE 1、基本介绍 ▶ 概述 Vue 是一套前端框架&#xff0c;免除原生JavaScript中的DOM操作&#xff0c;简化书写。 我们之前也学习过后端的框架 Mybatis &#xff0c;My…

Linux下C/C++实现类似netstat命令(列出TCP和UDP连接)

网络连接一般包括最基本的五元组信息(源地址、目标地址、源端口、目标端口、协议号)再加上所属进程信息pid, exe, cmdline等。其中这两项数据大多可直接读取linux /proc目录下的网络状态连接文件/proc/net/tcp、/proc/net/udp), 进程状态目录(/proc/pid/xx)。 Linux 下的/proc…

Java基于JSP的报刊订阅管理系统

随着人类的发展&#xff0c;人们对信息的获取方式也越来越多&#xff0c;虽然很多时候人们习惯了通过手机来获取各类信息&#xff0c;但是手机也逐渐的成为了危害人类健康的杀手之一&#xff0c;为了能够让大家回归到健康的生活中来&#xff0c;我开发了本系统&#xff0c;旨在…

基于jsp+mysql+ssm医药进销存管理系统-计算机毕业设计

项目介绍 为了减少传统医药进销存管理的繁杂的工作量&#xff0c;提高医药进销存管理的效率而设计开发了此系统。本系统综合各方面的需求决定采用B/S架构&#xff0c;并利用clipse搭建java开发平台。从而共同完成整个医药的设计开发。系统实现的功能主要包括&#xff1a;用户在…

java 通过InetAddress获取ip 计算机名称操作

本文属于java网络编程部分 需要你的网络编程三要素 有所了解 如果您尚未了解 可以先查看我的文章 java网络编程三要素 而 为了更好的获取和使用IP地址 java提供了InetAddress类 来到文档 首先 他在 java的net包下 所以 想用它 是需要导包的 根据文档叙述 InetAddress就是一个…

python数据分析及可视化(十七)聚宽(双均线分析、因子选股策略、多因子选股策略、均值回归理论、布林带策略、PEG策略、权重收益策略)

聚宽 聚宽是一个做金融量化的网站&#xff0c;https://www.joinquant.com&#xff0c;登录注册&#xff0c;如果你写的文章、策略被别人采纳&#xff0c;增加积分&#xff0c;积分用于免费的回测时长。在我的策略&#xff0c;进入策略列表&#xff0c;里面有做好的策略模板可以…

Web前端105天-day40-GIT

git--版本控制系统(VCS) 目录 前言 一、版本控制系统(VCS) 二、Git中的常用概念 三、Git中的常用命令 四、分支 五、远程仓库(代码托管平台) 总结 前言 day40学习开始 一、版本控制系统(VCS) 用于项目中文件的存储、共享、历史回退、合并、代码追踪文件历史常用版本控制…

acwing基础课——spfa

由数据范围反推算法复杂度以及算法内容 - AcWing 常用代码模板3——搜索与图论 - AcWing 基本思想&#xff1a; 一般单源最短路我们都可以用spfa算法来做&#xff0c;如果过不了再尝试其他算法。 spfa算法就是在bellman-ford算法的基础上就行优化&#xff0c;bellman-算法是每…

微信小程序|小程序事件

首先,我们在index.wxml中与index.js中添加如下代码: <button bindtap="alert">bindtap</button>Page({data: {},alert: function (event) {wx.showToast({title: 触发成功, // 标题icon: success, // 图标类型,默认successduration: 1500 // 提示窗停…

贪吃蛇复现-CoCube

需要完成下面所提及博文中里面所有前序案例&#xff1a; 从开环到闭环的旅程-CoCube 在完成如上代码之后&#xff0c;添加一个彩蛋&#xff0c;贪吃蛇的案例。 蓝桥ROS之半自动贪吃龟turtlesim版 基本上就是上述代码复现一下&#xff0c;完全没有难度的。 贪吃蛇复现-CoCubep…

程序员最关心的问题,我都帮你们问AI了

前言 最近几天互联网刮起了一阵ChatGPT风&#xff0c;起因是OpenAI发布了一个全新的聊天机器人模型—— ChatGPT&#xff0c;可以像人类交谈般回答大部分问题甚至还能直接帮你写代码。 我们先来试试让它帮我们写个代码&#xff1a; 有一丝丝的恐惧&#xff0c;害怕过两年就会失…

redis—主从,哨兵,集群

redis常见的使用方式 Redis的几种常见使用方式包括: Redis单副本;Redis多副本(主从) ;Redis Sentinel (哨兵) ;Redis Cluster;Redis自研。 使用场景: 如果数据量很少&#xff0c;主要是承载高并发高性能的场景&#xff0c;比如缓存一般就几个G的话&#xff0c; 单机足够了。…

Qt扫盲-QPushButton 理论总结

QPushButton 理论总结一、简述二、常用要点1. 快捷键相关2. 信号相关3. 默认按钮3. 推荐使用4. 重复功能5. 菜单功能一、简述 PushButton 按钮或CommandButton 按钮应该是图形用户界面中最常用的小部件。按下&#xff08;单击&#xff09;一个按钮就可以命令计算机执行某些操作…