Spring实战项目【从0到1】:博客系统(上)

目录

  • 1. 项目介绍
  • 2. 项目准备
    • 2.1 数据库准备
    • 2.2 创建项目
    • 2.3 配置文件
    • 2.4 准备前端页面
    • 2.5 测试
  • 3. 项目公共模块
    • 3.1 实体类
    • 3.2 公共层
  • 4. 业务代码
    • 4.1 持久层代码
    • 4.2 实现博客列表
    • 4.3 实现博客详情

1. 项目介绍

使用SSM框架(Spring、Spring MVC、MyBatis框架)实现⼀个简单的博客系统共5个页面:

  1. 用户登录
  2. 博客发表页
  3. 博客编辑页
  4. 博客列表页
  5. 博客详情页

功能描述:
用户登录成功后,可以查看所有⼈的博客。点击 <<查看全文>> 可以查看该博客的正⽂内容。如果该博客作者为当前登录用户,可以完成博客的修改和删除操作,以及发表新博客。
页面预览:
用户登录页面:
image.png
博客列表页:
image.png
博客详情页:
image.png
博客编辑页:
image.png

2. 项目准备

2.1 数据库准备

-- 建表SQL
create database if not exists java_blog_spring charset utf8mb4;-- 用户表
DROP TABLE IF EXISTS java_blog_spring.user;
CREATE TABLE java_blog_spring.user(
`id` INT NOT NULL AUTO_INCREMENT,
`user_name` VARCHAR ( 128 ) NOT NULL,
`password` VARCHAR ( 128 ) NOT NULL,
`github_url` VARCHAR ( 128 ) NULL,
`delete_flag` TINYINT ( 4 ) NULL DEFAULT 0,
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now(),
PRIMARY KEY ( id ),
UNIQUE INDEX user_name_UNIQUE ( user_name ASC )) ENGINE = INNODB DEFAULT
CHARACTER
SET = utf8mb4 COMMENT = '用户表';-- 博客表
drop table if exists java_blog_spring.blog;
CREATE TABLE java_blog_spring.blog (
`id` INT NOT NULL AUTO_INCREMENT,
`title` VARCHAR(200) NULL,
`content` TEXT NULL,
`user_id` INT(11) NULL,
`delete_flag` TINYINT(4) NULL DEFAULT 0,
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now(),
PRIMARY KEY (id))
ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '博客表';-- 新增用户信息
insert into java_blog_spring.user (user_name,password,github_url)
values("zhangsan","123456","https://gitee.com/little-fishs-code-house/java-ee2");
insert into java_blog_spring.user (user_name,password,github_url)
values("lisi","123456","https://gitee.com/little-fishs-code-house/java-ee2");
insert into java_blog_spring.blog (title,content,user_id) 
values("第⼀篇博客","1我是博客正我是博客正文,这是我的第一篇博客",1);
insert into java_blog_spring.blog (title,content,user_id) 
values("第⼆篇博客","2我是博客正文我是博客正文,这是我的第二篇博客",2);

image.png
image.png

2.2 创建项目

创建SpringBoot项目, 添加Spring MVC 和MyBatis对应依赖。
image.png

2.3 配置文件

在application.yml文件中配置数据库相关的信息:

spring:datasource:url: jdbc:mysql://127.0.0.1:3306/java_blog_spring?characterEncoding=utf8&useSSL=falseusername: rootpassword: '0124'driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:configuration: # 配置打印 MyBatis日志log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #配置打印sql语句map-underscore-to-camel-case: true #配置驼峰自动转换mapper-locations: classpath:mapper/**Mapper.xml
#设置日志文件的文件名
logging:file:name: spring-blog.log

2.4 准备前端页面

把博客系统静态页⾯拷贝到static目录下:
image.png

2.5 测试

访问前端页面:http://127.0.0.1:8080/blog_login.html
image.png
前端页面可以正常显示,说明项目初始化成功。

3. 项目公共模块

项⽬分为控制层(Controller), 服务层(Service), 持久层(Mapper)。各层之间的调⽤关系如下:
image.png
我们先根据需求完成实体类和公共层代码的编写。

3.1 实体类

@Data
public class UserInfo {private Integer id;private String userName;private String password;private String githubUrl;private Integer deleteFlag;private Date createTime;private Date updateTime;
}
@Data
public class BlogInfo {private Integer id;private String title;private String content;private Integer userId;private Integer deleteFlag;private Date createTime;private Date updateTime;
}

3.2 公共层

统一功能处理:

  1. 拦截器
  2. 统一结果返回
  3. 统一异常处理

拦截器内容在后面用户登录时介绍,这部分我们先写统一结果返回和统一异常处理。

  1. 统一结果返回为实体类:
    1. code:业务状态码
      • 200:业务处理成功
      • -1:业务处理失败
      • -2:用户未登录
    2. errMsg:业务处理失败时,返回的错误信息
    3. data:业务返回的数据

定义业务状态码:

public class Constant {public final static Integer SUCCESS_CODE = 200;// 成功public final static Integer FAIL_CODE = -1;// 失败public final static Integer UNLOGIN_CODE = -2;// 未登录
}
/*** 定义接口的统一返回结果*/
@Data
public class Result {private int code;// 业务码  定义:200-成功  -1-失败  -2-未登录private String errMsg;// 接口发生错误的信息private Object data;// 接口的返回真实结果/*** 接口返回成功时*/public static Result success(Object data) {Result result = new Result();result.setCode(Constant.SUCCESS_CODE);result.setErrMsg("");result.setData(data);return result;}/*** 接口返回失败时*/public static Result fail(String errMsg) {Result result = new Result();result.setCode(Constant.FAIL_CODE);result.setErrMsg(errMsg);result.setData(null);return result;}public static Result fail(String errMsg, Object data) {Result result = new Result();result.setCode(Constant.FAIL_CODE);result.setErrMsg(errMsg);result.setData(data);return result;}/*** 用户未登录时*/public static Result unLogin(String errMsg) {Result result = new Result();result.setCode(Constant.UNLOGIN_CODE);result.setErrMsg("用户未登录");result.setData(null);return result;}
}
  1. 统一返回结果:
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {@Autowiredprivate ObjectMapper objectMapper;@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {// 对于哪些方法执行统一结果返回,可以自己定义,不写默认所有方法return true;}@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {// 统一结果返回的具体逻辑if (body instanceof Result) {return body;}// 如果接口返回为String类型,进行单独处理if (body instanceof String) {// 转为JSON类型
//            ObjectMapper objectMapper = new ObjectMapper();// 也可以直接注入进来return objectMapper.writeValueAsString(Result.success(body));}return Result.success(body);}
}
  1. 统一异常处理:
@ResponseBody
@ControllerAdvice
public class ErrorHandler {@ExceptionHandlerpublic Result handler(Exception e) {return Result.fail(e.getMessage());}// 两种写法
//    @ExceptionHandler(NullPointerException.class)
//    public Result handler(Exception e) {
//        return Result.fail(e.getMessage());
//    }
//
//    @ExceptionHandler
//    public Result handler(NullPointerException e) {
//        return Result.fail(e.getMessage());
//    }
}

4. 业务代码

4.1 持久层代码

根据需求, 先⼤致计算有哪些DB相关操作, 完成持久层初步代码, 后续再根据业务需求进⾏完善。
每个页面需要实现的接口:

  1. 用户登录页
    1. 用户登录:根据用户名和密码,判断是否正确。

具体实现:根据用户名,查找用户信息,对比密码是否正确。
DB操作:根据用户名,查询用户信息。

  1. 博客列表页
    1. 查询用户信息:根据用户ID,查询用户信息。

DB操作:根据用户ID,查询用户信息。

  1. 获取所有博客列表:查询所有博客。

DB操作:查询所有博客。

  1. 博客详情页
    1. 查询作者信息
      1. 根据博客,拿到作者ID
      2. 根据作者ID,获取作者信息

DB操作:根据用户ID,查询用户信息。

  1. 查询博客详情:根据博客ID,查询博客信息

DB操作:根据博客ID,查询博客信息。

  1. 删除博客:根据博客ID,删除博客(修改delete_flag=1)

DB操作:根据博客ID,修改博客信息。

  1. 博客编辑页
    1. 修改博客:根据博客ID,修改博客信息。

DB操作:根据博客ID,修改博客信息。

  1. 发表博客:添加新的博客信息。

DB操作:插入新的博客数据。
总结
用户表

  1. 根据用户名,查询用户信息
  2. 根据用户ID,查询用户信息

博客表

  1. 查询博客列表
  2. 根据博客ID,查询博客信息
  3. 根据博客ID,修改博客信息
  4. 插入博客

根据以上分析, 来实现持久层的代码:

@Mapper
public interface UserMapper {// 根据用户名,查找用户信息// *可以替换成具体字段@Select("select * from user where user_name = #{userName} and delete_flag = 0")UserInfo selectByName(String userName);// 根据用户ID,查询用户信息@Select("select * from user where id = #{userId} and delete_flag = 0")UserInfo selectById(Integer userId);
}
@Mapper
public interface BlogMapper {// 查询博客列表@Select("select * from blog where delete_flag = 0 order by create_time desc")List<BlogInfo> selectAllBlog();// 根据博客Id,查询博客信息@Select("select * from blog where id = #{blogId} and delete_flag = 0")BlogInfo selectById(Integer blogId);// 根据博客ID,修改博客信息(包含修改和删除,根据参数决定修改什么)Integer updateBlog(BlogInfo blogInfo);// 插入博客@Insert("insert into blog (title, content, user_id) values(#{title}, #{content}, #{userId})")Integer insertBlog(BlogInfo blogInfo);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiaoyu.blog.mapper.BlogMapper"><update id="updateBlog">update blog<set><if test="title != null">title = #{title},</if><if test="content != null">content = #{content},</if><if test="deleteFlag != null">delete_flag = #{deleteFlag}</if></set>where id = #{id}</update>
</mapper>

书写测试用例, 简单进行单元测试:

@SpringBootTest
class UserMapperTest {@Autowiredprivate UserMapper userMapper;@Testvoid selectByName() {System.out.println(userMapper.selectByName("zhangsan"));}@Testvoid selectById() {System.out.println(userMapper.selectById(2));}
}

运行程序,观察日志:

  1. selectByName接口返回结果:

image.png

  1. selectById接口返回结果:

image.png
接口返回均正确。

@SpringBootTest
class BlogMapperTest {@Autowiredprivate BlogMapper blogMapper;@Testvoid selectAllBlog() {System.out.println(blogMapper.selectAllBlog());}@Testvoid selectById() {System.out.println(blogMapper.selectById(1));}@Testvoid updateBlog() {BlogInfo blogInfo = new BlogInfo();blogInfo.setTitle("update更新标题");blogInfo.setContent("update更新内容");blogInfo.setId(1);System.out.println(blogMapper.updateBlog(blogInfo));}@Testvoid deleteBlog() {BlogInfo blogInfo = new BlogInfo();blogInfo.setId(2);//第二条数据blogInfo.setDeleteFlag(1);System.out.println(blogMapper.updateBlog(blogInfo));}@Testvoid insertBlog() {BlogInfo blogInfo = new BlogInfo();blogInfo.setTitle("insert第三篇博客");blogInfo.setContent("insert博客内容");blogInfo.setUserId(2);System.out.println(blogMapper.insertBlog(blogInfo));}
}
  1. 测试接口selectAllBlog,观察日志:

image.png

  1. 测试接口selectById,观察日志:

image.png

  1. 测试接口updateBlog,观察日志:

image.png
更新前数据库信息为:
image.png
更新后数据库信息为:
image.png

  1. 测试接口deleteBlog,观察日志:

image.png
更新后数据库信息为:
image.png

  1. 测试接口insertBlog,观察日志:

image.png
更新后数据库信息为:
image.png
接口均测试成功。

4.2 实现博客列表

约定前后端交互接口:

[请求]
/blog/getList
[响应]
{"code": 200,"errMsg": "","data": [{"id": 4,"title": "insert第三篇博客","content": "insert博客内容","userId": 2,"deleteFlag": 0,"createTime": "2024-04-26T10:49:52.000+00:00","updateTime": "2024-04-26T10:49:52.000+00:00"},...]
}

客户端给服务器发送⼀个 /blog/getList 这样的 HTTP 请求, 服务器给客户端返回了⼀个 JSON 格式的数据。
实现服务器代码:

@RestController
@RequestMapping("/blog")
public class BlogController {@Autowiredprivate BlogService blogService;@RequestMapping("/getList")public List<BlogInfo> queryBlogList() {return blogService.queryBlogList();}
}
@Service
public class BlogService {@Autowiredprivate BlogMapper blogMapper;public List<BlogInfo> queryBlogList() {return blogMapper.selectAllBlog();}
}

启动程序,验证服务器能否返回正确数据:访问http://127.0.0.1:8080/blog/getList,响应结果为:
image.png
实现客户端代码:
我们希望访问blog_list.html这个页面,在页面加载的时候就去调用后端接口,返回数据,并进行填充,所以修改blog_list.html,删除之前写死的博客内容(即 <divclass=“blog”> ),并新增 js 代码处理ajax 请求。

  • 使用 ajax 给服务器发送 HTTP 请求。
  • 服务器返回的响应是⼀个 JSON 格式的数据, 根据这个响应数据使用 DOM API 构造页⾯内容。
  • 响应中的 createTime 字段为 ms 级时间戳, 需要转成格式化日期。
  • 跳转到博客详情页的 url 形如 blog_detail.html?blogId=1 这样就可以让博客详情页知道当前是要访问哪篇博客。
<script>$.ajax({type: "get",url: "/blog/getList",success: function(result) {// 如果result.code == 200 && result.data == null,页面可以提示:当前还没有任何博客,快去写博客吧!并进行页面跳转if (result.code == 200 && result.data != null) {var finalHtml = "";// 页面展示// 循环拼接result.data里面的数据for (var blog of result.data) {finalHtml += '<div class="blog">';finalHtml += '<div class="title">'+ blog.title +'</div>';finalHtml += '<div class="date">'+ blog.createTime +'</div>';finalHtml += '<div class="desc">'+ blog.content +'</div>';finalHtml += '<a class="detail" href="blog_detail.html?blogId='+ blog.id +'">查看全文&gt;&gt;</a>';finalHtml += '</div>';}$(".right").html(finalHtml);}}});</script>

运行程序,通过url:http://127.0.0.1:8080/blog_list.html访问服务器:
image.png
我们发现页面的日期显示为ms 级时间戳, 需要转成格式化日期,从后端对日期进行处理:
SimpleDateFormat 格式参考官⽅⽂档:
image.png

/*** 日期工具类*/
public class DateUtils {public static String formatDate(Date date) {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");return simpleDateFormat.format(date);}
}

修改BlogInfo实体类:
image.png
重新启动程序,通过 URL http://127.0.0.1:8080/blog_list.html 访问服务器, 验证效果:
image.png

4.3 实现博客详情

⽬前点击博客列表⻚的 “查看全⽂” , 能进⼊博客详情⻚, 但是这个博客详情⻚是写死的内容。我们期望能够根据当前的博客 id 从服务器动态获取博客内容。
约定前后端交互接口:

[请求]
/blog/getBlogDetail?blogId=1
[响应]
{"code": 200,"msg": "","data": {"id": 1,"title": "第⼀篇博客","content": "111我是博客正⽂我是博客正⽂我是博客正⽂","userId": 1,"deleteFlag": 0,"createTime": "2023-10-21 16:56:57","updateTime": "2023-10-21T08:56:57.000+00:00"}
}

实现服务器代码
BlogController 中添加queryBlogDetail ⽅法:

@RequestMapping("/getBlogDetail")
public BlogInfo queryBlogDetail(Integer blogId) {return blogService.queryBlogDetail(blogId);
}

在BlogService 中添加queryBlogDetail⽅法:

public BlogInfo queryBlogDetail(Integer blogId) {return blogMapper.selectById(blogId);
}

运行程序,访问http://127.0.0.1:8080/blog/getBlogDetail?blogId=3,测试后端接口的返回结果:
image.png
实现客户端代码
修改 blog_detail.html

  • 根据当前页面 URL 中的 blogId 参数(使用 location.search 即可得到形如 ?blogId=3 的数据), 给服务器发送 GET /blog 请求。
  • 根据获取到的响应数据, 显示在页面上
  1. 修改html⻚⾯, 去掉原来写死的博客标题, ⽇期和正⽂部分:
<div class="content"><div class="title"></div><div class="date"></div><div class="detail"></div><div class="operating"><button onclick="window.location.href='blog_update.html'">编辑</button><button onclick="deleteBlog()">删除</button></div>
</div>
  1. 完善 js 代码, 从服务器获取博客详情数据:
// 获取博客详情
$.ajax({type: "get",url: "/blog/getBlogDetail"+location.search,success: function(result) {if (result.code == 200 && result.data != null) {var blog = result.data;$(".right .content .title").text(blog.title);$(".right .content .date").text(blog.createTime);$(".right .content .detail").text(blog.content);}}
});

运行程序,访问http://127.0.0.1:8080/blog_detail.html?blogId=3,观察页面返回的数据:
image.png
未完,后见下篇文章!

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

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

相关文章

电脑技巧:轻松查看笔记本电脑电池的使用情况

目录 方法一&#xff1a;手工执行cmd命令 方法二&#xff1a;直接封装为Bat脚本 电池损耗程度介绍 Battery report字段中英文对照表 在大家日常办公和生活当中&#xff0c;笔记本电脑已成为非常重要工具。然而&#xff0c;随着笔记本电脑用的越久&#xff0c;电池的损耗难以…

创新指南|人工智能行为预测如何改变营销

在我们现在工作的人工智能营销新世界中&#xff0c;人工智能行为预测不仅作为一个流行词出现&#xff0c;而且作为一股革命力量&#xff0c;有望重新定义营销格局。 这种创新方法利用人工智能 (AI)的强大功能 来预测消费者行为&#xff0c;利用庞大而复杂的数据集来收集以前无法…

企业级数据治理学习总结

1. 水在前面 “数据治理”绝对是吹过的牛里面最高大上的题目了&#xff0c;本来想直接以《企业级数据治理》为题来水的&#xff0c;码字前又跑去图书馆借了几本书&#xff0c;翻了几页才发现自己连半桶水都提不起&#xff0c;撑死只能在小屁孩跟前吹吹牛。 好吧&#xff0c;实在…

【前端】-【防止接口重复请求】

文章目录 需求实现方案方案一方案二方案三 需求 对整个的项目都做一下接口防止重复请求的处理 实现方案 方案一 思路&#xff1a;通过使用axios拦截器&#xff0c;在请求拦截器中开启全屏Loading&#xff0c;然后在响应拦截器中将Loading关闭。 代码&#xff1a; 问题&…

详详详解动归数组常见习题(C/C++)

文章目录 最长递增数组序列&#xff08;必须连续&#xff09;dp[i] dp[i - 1] 1;最长递归子序列&#xff08;不需要连续&#xff09;dp[i] max(dp[i], dp[j] 1);俩层循环总结一维dp最长重复子数组最长公共子序列总结二维dp最终目标[3692. 最长连续公共子序列 - AcWing题库]…

【C++庖丁解牛】C++11---lambda表达式 | 包装器

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1. lambda表达式1.1 C98中…

ip地址与硬件地址的区别是什么

在数字世界的浩瀚海洋中&#xff0c;每一台联网的设备都需要一个独特的标识来确保信息的准确传输。这些标识&#xff0c;我们通常称之为IP地址和硬件地址。虽然它们都是用来识别网络设备的&#xff0c;但各自扮演的角色和所处的层次却大相径庭。虎观代理小二将带您深入了解IP地…

主成分分析在R语言中的简单应用:使用mvstats包

在数据科学领域&#xff0c;主成分分析&#xff08;PCA&#xff09;是一种广泛使用的技术&#xff0c;主要用于数据降维和探索性数据分析。PCA可以帮助我们发现数据中的模式&#xff0c;减少数据集的复杂性&#xff0c;同时保持数据中最重要的特征。本文将介绍如何在R语言中使用…

PID详解汇总

一、参照文章 PID的各种算法优缺点 二、位置式PID 优点:静态误差小,溢出的影响小。 缺点:计算量很大&#x

【PCL】教程 example2 3D点云之间的精确配准(FPFH特征对应关系估计变换矩阵)

这段代码主要实现了点云之间的配准功能&#xff0c;旨在通过估计点云的特征并找到最佳的对应关系来计算一个变换矩阵&#xff0c;从而可以将源点云&#xff08;src&#xff09;变换到目标点云&#xff08;tgt&#xff09;的坐标系统中。 代码功能和方法总结如下&#xff1a; 估…

上位机开发PyQt5(二)【单行输入框、多行输入框、按钮的信号和槽】

目录 一、单行输入框QLineEdit QLineEdit的方法&#xff1a; 二、多行输入框QTextEdit QTextEdit的方法 三、按钮QPushButton 四、按钮的信号与槽 信号与槽简介&#xff1a; 信号和槽绑定&#xff1a; 使用PyQt的槽函数 一、单行输入框QLineEdit QLineEdit控件可以输入…

黑马点评项目个人笔记+项目优化调整

博客须知 本篇博客内容来源与黑马点评项目实战篇-16.用户签到-实现签到功能_哔哩哔哩_bilibili&#xff0c;作者对视频内容进行了整合&#xff0c;由于记笔记时图片使用的是本地路径&#xff0c;所以导致博客的图片无法正常显示&#xff0c;如果有图片需求可以下载上方的pdf须…

程序员老鸟的 Pascal 语言菜鸟教程 -- 快速体验 Pascal

有些程序设计语言和编译器教材会以pascal语言的程序为例&#xff0c;这里写一个快速掌握简单应用的介绍。 1&#xff0c;安装 free pascal 编译器 ubuntu 22.04 直接通过 apt 源安装&#xff0c;此时的版本号为 3.2.2 1.1 安装 sudo apt install fp-compiler 1.2 简单测试 fpc…

【maven】pom文件详解和延伸知识

【maven】pom文件详解 【一】maven项目的pom文件详解【1】maven项目的目录结构【2】根元素和必要配置【3】父项目和parent元素【4】项目构建需要的信息【5】项目依赖相关信息&#xff08;1&#xff09;依赖坐标&#xff08;2&#xff09;依赖类型&#xff08;3&#xff09;依赖…

JavaScript this 上下文深度探索:综合指南涵盖隐式与显式call、apply、bind、箭头函数、构造函数等用法于多样场景

JavaScript中的this关键字代表函数执行的上下文环境&#xff0c;核心在于确定函数内部访问的当前对象。它根据函数调用方式动态变化&#xff0c;对事件处理、对象方法调用等至关重要。通过.call(), .apply(), .bind()或箭头函数控制this&#xff0c;可确保代码逻辑正确绑定对象…

python可视化学习笔记折线图问题-起始点问题

问题描述&#xff1a; 起始点的位置不对 from pyecharts.charts import Line import pyecharts.options as opts # 示例数据 x_data [1,2,3,4,5] y_data [1, 2, 3, 4, 5] # 创建 Line 图表 line Line() line.add_xaxis(x_data) line.add_yaxis("test", y_data) li…

Redis---------缓存更新,缓存穿透\雪崩\击穿

三种更新策略 内存淘汰是Redis内存的自动操作&#xff0c;当内存快满了就会触发内存淘汰。超时剔除则是在存储Redis时加上其有限期(expire)&#xff0c;有限期一过就会自动删除掉。而主动更新则是自己编写代码去保持更新&#xff0c;所以接下来研究主动更新策略。 主动更新策略…

PS入门|网络报名证件照上传总提示审核失败是什么原因?

前言 之前小白遇到过有小伙伴报考了某个证书的考试&#xff0c;但在报名的过程出现了问题&#xff1a;证件照都是按照要求制作的&#xff0c;但为啥总是没有审核通过&#xff1f; 这个很简单&#xff1a;分辨率出现了问题。 啥&#xff1f;明明都是按照软件提示的分辨率要求制…

Python中的观察者模式及其应用

观察者模式是设计模式之一&#xff0c;实现一对多依赖&#xff0c;当主题状态变化时通知所有观察者更新。在Python中&#xff0c;通过自定义接口或内置模块实现观察者模式&#xff0c;可提高程序灵活性和扩展性&#xff0c;尤其适用于状态变化时触发操作的场景&#xff0c;如事…

Linux(ubuntu)—— 用户管理user 用户组group

一、用户 1.1、查看所有用户 cat /etc/passwd 1.2、新增用户 useradd 命令&#xff0c;我这里用的是2.4的命令。 然后&#xff0c;需要设置密码 passwd student 只有root用户才能用passwd命令设置其他用户的密码&#xff0c;普通用户只能够设置自己的密码 二、组 2.1查看…