【SpringBoot】SpringBoot使用mail实现登录邮箱验证

427ef4152dbf4b6c92618a198935cb6c.png

  📝个人主页:哈__

期待您的关注 

1b7335aca73b41609b7f05d1d366f476.gif

目录

一、前期准备

1 开启邮箱服务

2 SpringBoot导入依赖

3 创建application.yml配置文件 

4 创建数据库文件

 5 配置redis服务

二、验证邮件发送功能 

三、注册功能实现邮箱验证

1 创建User实体类

2 创建UserParam

3 创建CaptchaService

4 创建EmailTemplateEnum

5 创建CaptchaServiceImpl

 6 创建CaptchaController

7 创建LoginController 

四、创建登陆页面 

1 login.html 

2 register.html


 

在实际的开发当中,不少的场景中需要我们使用更加安全的认证方式,同时也为了防止一些用户恶意注册,我们可能会需要用户使用一些可以证明个人身份的注册方式,如短信验证、邮箱验证等。

一、前期准备

为了实现邮箱认证服务,我们需要提供出来一个邮箱作为验证码的发送者,这里我使用的是QQ邮箱。

1 开启邮箱服务

首先打开QQ邮箱,然后找到设置,点击账号。

a5b0567d19f143a7acbdfa6c60ff09f0.png

然后找到下方的邮件协议服务,打开。因为这里我已经打开了,而且服务开启也较为简单,需要我们发送一个短信到指定的号码,可能会收取一定的费用,不过不是很多,好像是几毛钱。

31d8524967e54c389103fe92393c8b86.png 开启之后会给你一个授权码,一定要记住这个授权码,发邮件的时候需要这个。

2 SpringBoot导入依赖

核心的就是mail依赖,因为我这个项目东西不少,为了方便我就全拷贝过来了,可能有的用不到。

 <properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.7.6</spring-boot.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><mybatisplus.version>3.4.3.1</mybatisplus.version><mybatisplus.generator>3.3.2</mybatisplus.generator><mybatisplus.velocity>1.7</mybatisplus.velocity></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatisplus.version}</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>${mybatisplus.generator}</version></dependency><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity</artifactId><version>${mybatisplus.velocity}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.56</version></dependency></dependencies>

3 创建application.yml配置文件 

spring:#邮件服务配置mail:host: smtp.qq.com #邮件服务器地址protocol: smtp #协议username:  #发送邮件的邮箱也就是你开通服务的邮箱password:  #开通服务后得到的授权码default-encoding: utf-8 #邮件内容的编码redis:host: 127.0.0.1port: 6379datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mailusername: rootpassword:  # 数据库的密码

4 创建数据库文件

在上边的配置文件中你也看到了,我们用到了mysql还有redis。

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (`id` int(11) NOT NULL AUTO_INCREMENT,`account` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact;-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'admin', '123456');
INSERT INTO `user` VALUES (2, '123456', '123456');SET FOREIGN_KEY_CHECKS = 1;

 5 配置redis服务

在之前的文章当中我有说到过安装redis的事情,大家可以看看我这篇文章。【Spring】SpringBoot整合Redis,用Redis实现限流(附Redis解压包)_springboot 限流 redis-CSDN博客

二、验证邮件发送功能 

大家可以先看一下我的项目结构。其中的一些代码是我学习另一位大佬的文章,这篇文章也是对该大佬文章的一个总结。蒾酒-CSDN博客

d18a1b016999488794cb4594e46ab934.png

我们最重要的邮件发送工具 就是util包下的EmailApi。将以下代码导入后,创建一个测试方法。

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.util.Objects;/*** @author mijiupro*/
@Component
@Slf4j
public class EmailApi {@Resourceprivate JavaMailSender mailSender;@Value("${spring.mail.username}")private String from ;// 发件人/*** 发送纯文本的邮件* @param to 收件人* @param subject 主题* @param content 内容* @return 是否成功*/@SneakyThrows(Exception.class)public boolean sendGeneralEmail(String subject, String content, String... to){// 创建邮件消息SimpleMailMessage message = new SimpleMailMessage();message.setFrom(from);// 设置收件人message.setTo(to);// 设置邮件主题message.setSubject(subject);// 设置邮件内容message.setText(content);// 发送邮件mailSender.send(message);return true;}/*** 发送html的邮件* @param to 收件人* @param subject 主题* @param content 内容* @return 是否成功*/@SneakyThrows(Exception.class)public boolean sendHtmlEmail(String subject, String content, String... to){// 创建邮件消息MimeMessage mimeMessage = mailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);helper.setFrom(from);// 设置收件人helper.setTo(to);// 设置邮件主题helper.setSubject(subject);// 设置邮件内容helper.setText(content, true);// 发送邮件mailSender.send(mimeMessage);log.info("发送邮件成功");return true;}/*** 发送带附件的邮件* @param to 收件人* @param subject 主题* @param content 内容* @param filePaths 附件路径* @return 是否成功*/@SneakyThrows(Exception.class)public boolean sendAttachmentsEmail(String subject, String content, String[] to, String[] filePaths) {// 创建邮件消息MimeMessage mimeMessage = mailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);helper.setFrom(from);// 设置收件人helper.setTo(to);// 设置邮件主题helper.setSubject(subject);// 设置邮件内容helper.setText(content,true);// 添加附件if (filePaths != null) {for (String filePath : filePaths) {FileSystemResource file = new FileSystemResource(new File(filePath));helper.addAttachment(Objects.requireNonNull(file.getFilename()), file);}}// 发送邮件mailSender.send(mimeMessage);return true;}/*** 发送带静态资源的邮件* @param to 收件人* @param subject 主题* @param content 内容* @param rscPath 静态资源路径* @param rscId 静态资源id* @return 是否成功*/@SneakyThrows(Exception.class)public boolean sendInlineResourceEmail(String subject, String content, String to, String rscPath, String rscId) {// 创建邮件消息MimeMessage mimeMessage = mailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);// 设置发件人helper.setFrom(from);// 设置收件人helper.setTo(to);// 设置邮件主题helper.setSubject(subject);//html内容图片String contentHtml = "<html><body>这是邮件的内容,包含一个图片:<img src=\'cid:" + rscId + "\'>"+content+"</body></html>";helper.setText(contentHtml, true);//指定讲资源地址FileSystemResource res = new FileSystemResource(new File(rscPath));helper.addInline(rscId, res);mailSender.send(mimeMessage);return true;}}

 进行邮件发送的测试。

cce23808aa9c476eb51c0896c3ef38fa.png

 看看结果,成功的发过来了。

e582910586124e76b8b1baf429574f72.png

 接下来就要进行登录注册功能的开发了。

三、注册功能实现邮箱验证

1 创建User实体类

@Data
@EqualsAndHashCode(callSuper = false)
public class User implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Integer id;private String account;private String password;}

2 创建UserParam

@Data
@EqualsAndHashCode(callSuper = false)
public class UserParam implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Integer id;private String account;private String password;private String emailCode;private String email;}

3 创建CaptchaService

public interface CaptchaService {boolean sendCaptcha(String email);
}

4 创建EmailTemplateEnum

这一步我没有选择创建CaptchaServiceImpl,因为这个类中涉及到了一些核心的代码,而我们一些类还没有创建完,我们先创建这样的一个枚举类,这个枚举类的作用就是定义我们发送邮件的一个模板,我们在发送邮件的时候,直接向模板内插入内容就可以了。

ublic enum EmailTemplateEnum {// 验证码邮件VERIFICATION_CODE_EMAIL_HTML("<html><body>用户你好,你的验证码是:<h1>%s</h1>请在五分钟内完成注册</body></html>","登录验证"),// 用户被封禁邮件通知USER_BANNED_EMAIL("用户你好,你已经被管理员封禁,封禁原因:%s", "封禁通知");private final String template;private final String subject;EmailTemplateEnum(String template, String subject) {this.template = template;this.subject = subject;}public String getTemplate(){return this.template;}public String set(String captcha) {return String.format(this.template, captcha);}public String getSubject() {return this.subject;}
}

5 创建CaptchaServiceImpl

首先把我们前端传过来的邮箱加一个前缀,用于redis中的存储,因为我们不仅可以有邮箱认证还可以有手机认证。

 @ResourceStringRedisTemplate stringRedisTemplate;@ResourceEmailApi emailApi;@Overridepublic boolean sendCaptcha(String email) {sendMailCaptcha("login:email:captcha:"+email);return true;}

 在redis当中,验证码的存储使用的是Hash结构,Hash存储了验证码,验证次数,还有上一次的发送时间,因为我们要限制一分钟发送的次数。一分钟内我们只能发一条短信,验证码在redis中的过期时间为五分钟,在验证码未过期之前发送的认证,都会让这个发送次数加一,倘若发送的次数达到了5次还要发送,那么就封禁一天不让发送短信。

例如,在3:30:30的时候发送了一次短信,一分钟后,3:31:30的时候又发送了短信,直到3:35:30的时候又发了一次,此时的发送次数已经达到了5,这时候就会封一天,因为每次发送验证码的时候,redis都存储着上一次还没过期的验证码,所以发送次数会增加。

下边讲一下代码。

先从redis中找到这个hash结构,如果hash结构的值不为空并且达到了发送次数上限,就封禁一天,否则的话看一下上一次的发送时间是否存在,如果存在的话,判断一下当前时间和上一次的发送时间间隔是否大于60秒,如果小于60秒那么不让发送。如果正常发送短信,那么就把发送的次数加1,然后用随机数生成一个六位数的验证码,发送验证码,并且向redis中保存刚才的hash结构。

  private boolean sendMailCaptcha(String key){BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps(key);// 初始检查String lastSendTimestamp = hashOps.get("lastSendTimestamp");String sendCount = hashOps.get("sendCount");if(StringUtils.isNotBlank(sendCount)&&Integer.parseInt(sendCount)>=5){hashOps.expire(24, TimeUnit.HOURS);throw new  RuntimeException("验证码发送过于频繁");}if(StringUtils.isNotBlank(lastSendTimestamp)){long lastSendTime = Long.parseLong(lastSendTimestamp);long currentTime = System.currentTimeMillis();long elapsedTime = currentTime - lastSendTime;if(elapsedTime < 60 * 1000){throw new  RuntimeException("验证码发送过于频繁");}}int newSendCount = StringUtils.isNotBlank(sendCount) ? Integer.parseInt(sendCount) + 1 : 1;String captcha = RandomStringUtils.randomNumeric(6);try {sendCaptcha(key,captcha);} catch (Exception e) {return false;}hashOps.put("captcha", captcha);hashOps.put("lastSendTimestamp", String.valueOf(System.currentTimeMillis()));hashOps.put("sendCount", String.valueOf(newSendCount));hashOps.expire(5, TimeUnit.MINUTES); // 设置过期时间为5分钟return true;}

发送验证码调用的是下边的函数。

 private void sendCaptcha(String hashKey, String captcha) thorw Exception{// 根据hashKey判断是发送邮件还是短信,然后调用相应的发送方法if("email".equals(hashKey.split(":")[1])){if (!emailApi.sendHtmlEmail(EmailTemplateEnum.VERIFICATION_CODE_EMAIL_HTML.getSubject(),EmailTemplateEnum.VERIFICATION_CODE_EMAIL_HTML.set(captcha),hashKey.split(":")[3])) {throw new RuntimeException("发送邮件失败");}}}

 6 创建CaptchaController

@RestController
@RequestMapping("/captcha")
public class CaptchaController {@ResourceCaptchaService captchaService;@ResourceEmailApi emailApi;@RequestMapping("/getCaptcha")public Result sendCaptcha(String email){boolean res = captchaService.sendCaptcha(email);if(res){return new Result("发送成功",200,null);}return new Result("发送失败",500,null);}
}

7 创建LoginController 

UserSevice的东西都很简单,都是mybatisplus的内容,如果不太了解可以看我这篇文章【Spring】SpringBoot整合MybatisPlus的基本应用_简单的springboot+mybatisplus的应用程序-CSDN博客

我这里并没有用UserService封装认证的过程,直接写到controller中了,大家能看懂就好。仅供学习使用。 

@Controller
public class LoginController {@AutowiredUserService userService;@AutowiredStringRedisTemplate stringRedisTemplate;@GetMapping("/")public String Login(){return "redirect:/pages/login.html";}@RequestMapping("/register")@ResponseBodypublic Result registerUser( UserParam user ){String email = user.getEmail();String emailCode = user.getEmailCode();BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps("login:email:captcha:"+email);String code = hashOps.get("captcha");if(!Objects.equals(code, emailCode)){return new Result("验证码错误",400,null);}User user1 = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getAccount,user.getAccount()));//如果有这个用户的信息要拒绝注册if(user1!=null){return new Result("",100,null);}User saveUser = new User();BeanUtils.copyProperties(user,saveUser);System.out.println(user);System.out.println(saveUser);boolean save = userService.save(saveUser);if(!save){return new Result("注册失败",300,null);}return new Result("注册成功",200,null);}
}

到此为止,验证码的注册功能就已经实现完成了。

我们现在要做一个前端页面。

四、创建登陆页面 

在resources目录下创建static文件夹。在pages目录下添加login.html和register.html。至于jquery呢就要大家自己去找了。

6a71f75baaa54fae8b7c0bd5ab3e8d86.png

 

1 login.html 

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="../js/jquery.min.js"></script><script>$(function() {$(".btn").on("click", function () {var account = $(".account").val()var password = $(".password").val()console.log(account)console.log(password)$.ajax({type: "get",url: "/login?&account=" + account + "&password=" + password,success: function (data) {if (data.code === 200) {window.location.href = "../pages/index.html"}else{alert("登陆失败,请检查账号或者密码")}}})})});</script><style>*{padding: 0;margin: 0px;text-decoration: none;}body {background-image: url(../images2/1.jpg);background-size:cover;background-attachment:fixed;}.loginbox {height: 280px;width: 26%;margin: 340px auto;background-color: rgb(158, 151, 158);opacity: 0.7;text-align: center;}.loginbox .top {display: block;height: 42px;background: linear-gradient(90deg,pink,red,pink,yellow,red);padding-top: 15px;font-size: 20px;font-weight: 600;color: black;}.loginbox span {color: black;font-weight: 500;font-size: 18px;}.loginbox input[type=text],.loginbox input[type=password] {height: 20px;border: none;border-radius: 4px;outline: none;}.loginbox input[type=button] {width: 40%;height: 24px;border: none;border-radius: 4px;margin: 0 auto;outline: none;}.loginbox .a {display: flex;margin: 20px auto;width: 45%;justify-content: space-between;}.loginbox .a a {color: white;}.loginbox .a a:hover {color: rgb(228, 221, 228);}</style>
</head>
<body><div class="loginbox"><span class="top">邮箱简易发送系统</span><br><span>账号:</span><input type="text" placeholder="请输入账号" class="account"><br><br><span>密码:</span><input type="password" placeholder="请输入密码" class="password"><br><br><input type="button" value="登录" class="btn"><div class="a"><a href="register.html">注册</a><a href="">忘记密码</a></div></div>
</body>
</html>

2 register.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="../css/register.css"><script src="../js/jquery.min.js"></script><script>$(function(){// 注册按钮点击事件$(".btn").on("click",function(){var account = $(".account").val().trim();var password = $(".password").val().trim();var emailCode = $(".emailCode").val().trim();var email = $(".email").val().trim();$.ajax({type:"post",url:"/register",data:{"account":account,"password":password,"emailCode":emailCode,"email":email},success:function(data){if(data.code=== 100){alert("该账号已存在");}else if(data.code === 300){alert("账号注册失败");}else if(data.code === 400){alert("验证码错误");}else{alert("账号注册成功");window.location.href="../pages/login.html";}}});});// 发送验证码按钮点击事件$(".sendCode").on("click", function(){var email = $(".email").val().trim();if(email === "") {alert("请输入邮箱");return;}// 发送验证码请求$.ajax({type:"get",url:"/captcha/getCaptcha?email="+email,success:function(data){console.log(data)if(data.code === 200){alert("验证码已发送到邮箱");// 启动倒计时startCountdown(60);} else {alert("验证码发送失败,请重试");}}});});// 启动倒计时函数function startCountdown(seconds) {var timer = seconds;$(".sendCode").prop("disabled", true);var countdown = setInterval(function() {$(".sendCode").val(timer + "秒后可重新发送");timer--;if (timer < 0) {clearInterval(countdown);$(".sendCode").prop("disabled", false);$(".sendCode").val("发送验证码");}}, 1000);}});</script><style>*{padding: 0;margin: 0px;text-decoration: none;}body {background-image: url(../images2/1.jpg);background-size: cover;background-attachment: fixed;}.loginbox {height: 380px; /* 调整高度以适应新字段 */width: 26%;margin: 340px auto;background-color: rgb(158, 151, 158);opacity: 0.7;text-align: center;}.loginbox .top {display: block;height: 42px;background: linear-gradient(90deg, pink, red, pink, yellow, red);padding-top: 15px;font-size: 20px;font-weight: 600;color: black;}.loginbox span {color: black;font-weight: 500;font-size: 18px;}.loginbox input[type=text],.loginbox input[type=password] {height: 20px;border: none;border-radius: 4px;outline: none;}.loginbox input[type=button] {width: 40%;height: 24px;border: none;border-radius: 4px;margin: 0 auto;outline: none;}.loginbox .a {display: flex;margin: 20px auto;width: 45%;justify-content: space-between;}.loginbox .a a {color: white;}.loginbox .a a:hover {color: rgb(228, 221, 228);}</style>
</head>
<body>
<div class="loginbox"><span class="top">邮箱简易发送系统</span><br><span>账号:</span><input type="text" placeholder="请输入账号" class="account"><br><br><span>密码:</span><input type="password" placeholder="请输入密码" class="password"><br><br><span>邮箱:</span><input type="text" placeholder="请输入邮箱" class="email"><br><br><input type="button" value="发送验证码" class="sendCode"><br><br><span>验证码:</span><input type="text" placeholder="请输入验证码" class="emailCode"><br><br><input type="button" value="注册" class="btn"><div class="a"><a href="login.html">返回</a></div>
</div>
</body>
</html>

2625f728ad30493391e9248fc41e6579.png

 

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

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

相关文章

DEBOPIE框架:打造最好的ChatGPT交易机器人

本文介绍了如何利用 DEBOPIE 框架并基于 ChatGPT 创建高效交易机器人&#xff0c;并强调了在使用 AI 辅助交易时需要注意的限制以及操作步骤。原文: Build the Best ChatGPT Trading Bots with my “DEBOPIE” Framework 如今有大量文章介绍如何通过 ChatGPT 帮助决定如何以及在…

仓库管理系统12--供应商设置

1、添加供应商窗体 2、布局控件UI <UserControl x:Class"West.StoreMgr.View.SupplierView"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc"http://…

为什么前端传了token,后端一直获取不到?一直报跨域错误?

这是我的前端代码 这是我的后端拦截器 那就需要了解一下 预检请求 对于非简单请求&#xff08;如PUT、DELETE或包含自定义HTTP头的请求&#xff09;&#xff0c;浏览器会先发送一个OPTIONS请求到目标服务器&#xff0c;询问是否允许该跨域请求。这个过程称为预检请求。 当opt…

【爬虫实战】今日头条-关键词搜索-快速整理出1w条数据

快速整理头条关键词数据工具&#xff0c;学习效率妥妥翻倍&#xff01;&#xff01;&#xff01;本案例源码仅供学习参考&#xff01; 项目功能简介&#xff1a; 1.可视化式配置&#xff1b; 2.任意关键词&#xff1b; 3.自动翻页&#xff1b; 4.支持指定最大翻页页码&…

IP地址网络号:解读其构成与重要性

在数字化时代&#xff0c;IP地址已成为我们网络生活不可或缺的一部分。每个设备在网络中都有一个独特的IP地址&#xff0c;这个地址由网络号和主机号组成&#xff0c;它们共同构成了我们的网络身份。其中&#xff0c;网络号的作用尤为重要&#xff0c;它决定了设备所连接的网络…

【算法专题--栈】用栈实现队列 -- 高频面试题(图文详解,小白一看就懂!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐双栈 模拟 队列 &#x1f95d;栈 和 队列 的特性 &#x1f34d;具体思路 &#x1f34d;案例图解 四、总结与提炼 五、共勉 一、前言 用栈实现队列 这道题&#xff0c;可以说是--栈专题--&#xff0c;最经典的一道题&…

管理上的一些思考

1 前言 管理可分为自我管理、平级管理、向下管理和向上管理。 顾名思义&#xff0c;自我管理就是对自己工作、生活等各方面的规划和执行&#xff0c;不涉及与其他人互动、配合等。我们设定人生目标、年度计划、月计划等&#xff0c;都可以认为是自我管理。《增广贤文》有段很…

静态时序分析:ideal_clock、propagated_clock以及generated_clock的关系及其延迟计算规则(二)

相关阅读 静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html?spm1001.2014.3001.5482 生成时钟 上一节中&#xff0c;我们讨论了理想时钟和传播时钟的创建和使用&#xff0c;本节将讨论生成时钟及其与理想时钟和传播时钟的关系。 图1所示的是一个简…

Mysql基本知识点

1.数据库的基本操作 显示当前的数据库 show databases;创建一个数据库 直接创建数据库 create database 数据库名字;如果系统没有 test2 的数据库&#xff0c;则创建一个名叫 test2 的数据库&#xff0c;如果有则不创建 create database if not exists test2;如果系统没有 db…

【网络】计算机网络-基本知识

目录 概念计算机网络功能计算机网络的组成计算机网络的分类 网络地址网络地址的分类 计算机网络相关性能指标速率带宽吞吐量时延时延的种类&#xff1a; 时延带宽积往返时延RTT利用率 概念 计算机网络是指将多台计算机通过通信设备连接起来&#xff0c;实现数据和资源的共享。…

串口小工具(来源网络,源码修改)

从CSDN 中的一位博主的分享做了一些修改 QtSerial 的配和更稳定些 信号和槽 … … 更不容易崩 # This Python file uses the following encoding: utf-8 import sys import timefrom PySide6.QtGui import QIcon, QTextCursor from PySide6.QtWidgets import QApplication, QWi…

第3章_UART 开发基础

文章目录 第3章 UART 开发基础3.1 同步传输与异步传输3.1.1 概念与示例3.1.2 差别 3.2 UART 协议与操作方法3.2.1 UART 协议3.2.2 STM32H5 UART 硬件结构3.2.3 RS485 协议 3.3 UART 编程3.3.1 硬件连接3.3.2 三种编程方式3.3.3 查询方式3.3.4 中断方式3.3.5 DMA 方式 3.4 效率最…

扫描全能王的AI驱动创新与智能高清滤镜技术解析

目录 引言1、扫描全能王2、智能高清滤镜黑科技2.1、图像视觉矫正2.2、去干扰技术 3、实际应用案例3.1、打印文稿褶皱检测3.2、试卷擦除手写3.3、老旧文件处理3.4、收银小票3.5、从不同角度扫描文档 4、用户体验结论与未来展望 引言 在数字化时代背景下&#xff0c;文档扫描功能…

【JavaEE】JVM

文章目录 一、JVM 简介二、JVM 运行流程三、JVM 运行时数据区1、堆&#xff08;线程共享&#xff09;2、Java虚拟机栈&#xff08;线程私有&#xff09;3、本地方法栈&#xff08;线程私有&#xff09;4、程序计数器&#xff08;线程私有&#xff09;5、方法区&#xff08;线程…

如何有效保护生物医药企业隔离网数据导出的安全性?

生物医药企业的核心数据保护至关重要&#xff0c;企业为了保护内部的核心数据&#xff0c;会将网络进行物理隔离&#xff0c;将企业内⽹与外⽹隔离。⽹络隔离后&#xff0c;仍存在重要数据从内网导出至外网的隔离网数据导出需求。以下是一些需要特别保护的核心数据类型&#xf…

【快速排序】| 详解快速排序 力扣912

&#x1f397;️ 主页&#xff1a;小夜时雨 &#x1f397;️专栏&#xff1a;快速排序 &#x1f397;️如何活着&#xff0c;是我找寻的方向 目录 1. 题目解析2. 代码 1. 题目解析 题目链接: https://leetcode.cn/problems/sort-an-array/ 我们上道题讲过快速排序的核心代码&a…

围观AI大佬吴恩达教授开发的Agent智能体

最近 Agent 智能体很火&#xff0c;人工智能领域国际上最权威的学者之一吴恩达教授&#xff0c;不但总结了Agent设计模式&#xff0c;还亲自下场开发了一款翻译Agent。 这个翻译Agent在设计模式和提示词工程等方面都有许多值得学习的地方。老渡拆解一下&#xff0c;跟朋友们分…

你需要明白的JVM相关问题

1、说说内存溢出跟内存泄漏的区别&#xff1f; 内存泄露&#xff1a;申请的内存空间没有被正确释放&#xff0c;导致内存被白白占用。内存溢出&#xff1a;申请的内存超过了可用内存&#xff0c;内存不够了。可能是泄漏导致的。 2、如何判断对象仍然存活&#xff1f;jvm是怎么…

mysql数据库的主从复制

MySQL主从复制的应用场景 当只有一台MySQL服务器要负责读写时&#xff0c;对于安全性&#xff0c;高可用&#xff0c;高并发等需求就不能满足&#xff0c;因此就要建立集群&#xff0c;集群的基础就是主从复制。 原理&#xff08;过程&#xff09; MySQL支持的复制类型 基于语…

有关主流编程语言的几个问题及对比

参考&#xff1a;编程语言的历史&#xff08;https://blog.csdn.net/david_lv/article/details/104765347&#xff09; 静态与动态语言的优缺点分析 什么是强类型&#xff0c;什么是弱类型&#xff1f;哪种更好些&#xff1f;为什么? 强类型和弱类型的区别 几种常见的开发语言…