实验:基于SpringBoot+MyBatis-Plus实现文章列表增删改查

article/2025/8/13 20:42:40

目录

  • 实验内容
  • 前言
  • 一、添加新的依赖
  • 二、配置连接MySQL数据库
  • 三、创建实体类以及Mapper、Service和Controller三层架构
    • POJO
    • Mapper
    • Service
      • IService
      • ServiceImpl
    • Controller
  • 四、添加配置类、响应类和全局异常处理类
  • 五、根据接口文档编写控制器方法并测试接口
    • 1.新增文章接口
      • 1.1 基本信息
      • 1.2 请求参数
      • 1.3 响应数据
      • 处理请求的方法
      • 测试接口
    • 2.文章列表(条件分页)接口
      • 1.1 基本信息
      • 1.2 请求参数
      • 1.3 响应数据
      • 处理请求的方法
      • 测试接口
    • 2.获取文章详情接口
      • 1.1 基本信息
      • 1.2 请求参数
      • 1.3 响应数据
      • 处理请求的方法
      • 测试接口
    • 4.更新文章接口
      • 1.1 基本信息
      • 1.2 请求参数
      • 1.3 响应数据
      • 处理请求的方法
      • 测试接口
    • 5.删除文章接口
      • 1.1 基本信息
      • 1.2 请求参数
      • 1.3 响应数据
      • 处理请求的方法
      • 测试接口
  • 总结
  • 参考
    • 官方文档
    • 用到的软件

实验内容

  1. 搭建Spring Boot后台,使用MapperServiceController三层架构。
  2. 使用Mybatis-Plus访问数据库。
  3. 必须用到Mybatis-Plus自带的增删改查功能,还有分页查询功能。
  4. 查询不能直接写SQL语句,必须用到条件构造器(QueryWrapperLambdaQueryWrapper)。
  5. 必须用到参数校验。
  6. 根据接口文档实现处理请求的方法。

前言

实验内容的前三个和上一个实验:搭建Spring Boot+MyBatis-Plus后台一样,具体如何创建SpringBoot项目、添加依赖和创建MapperServiceController,还有添加分页和配置类就不详细说明了,还有数据库连接参数的设置。
不一样的地方就是实体类由Person换成了Article,大差不差的。

一、添加新的依赖

跟上次实验不一样的是,添加了新的依赖,首先是lombok用于自动生成GetterSetter方法

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

然后是参数校验用的依赖

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

二、配置连接MySQL数据库

修改了src/main/resources文件夹下的application.properties文件的扩展名为层级分明的.yml.yaml文件,添加/修改内容为如下配置。

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driver # 连接操作MySQL数据库的类url: jdbc:mysql://localhost:3306/exp2 # 最后为数据库的名字、再前一个是端口username: root # 数据库用户名password: 123456 # 数据库密码

接下来就是在数据库中创建后面会用到的表,根据接口文档中获取文章详情的响应数据样例中返回的文章data内容可以推断出article表所拥有的字段,顺便再创建一个数据库,所生成的SQL如下。

DROP DATABASE IF EXISTS exp2;
CREATE DATABASE exp2 CHARACTER SET utf8;USE exp2;CREATE TABLE article (id INT AUTO_INCREMENT PRIMARY KEY,title VARCHAR(255) NOT NULL,content TEXT NOT NULL,cover_img VARCHAR(255),state VARCHAR(50),category_id INT,create_date DATETIME DEFAULT CURRENT_TIMESTAMP,update_date DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) CHARSET=utf8;

三、创建实体类以及Mapper、Service和Controller三层架构

POJO

实体类Article相对于上次实验手动一个一个地添加GetterSetter方法,这次就用了lombok来简化代码,让其自动生成,另外使用了@JsonFormat注解规定了Date在转换成Json后的格式。。

package org.peanut.exp2.pojo;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;@Data
public class Article {@TableId(type = IdType.AUTO)private Integer id;private String title;private String content;private String coverImg;private String state;private Integer categoryId;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private Date createDate;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private Date updateDate;
}

Mapper

package org.peanut.exp2.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.peanut.exp2.pojo.Article;public interface ArticleMapper extends BaseMapper<Article> {
}

Service

IService

package org.peanut.exp2.service;import com.baomidou.mybatisplus.extension.service.IService;
import org.peanut.exp2.pojo.Article;import java.util.List;public interface IArticleService extends IService<Article> {
}

ServiceImpl

package org.peanut.exp2.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.peanut.exp2.mapper.ArticleMapper;
import org.peanut.exp2.pojo.Article;
import org.peanut.exp2.service.IArticleService;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article>implements IArticleService {
}

Controller

package org.peanut.exp2.controller;import org.peanut.exp2.service.IArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
public class ArticleController {@AutowiredIArticleService articleService;
}

四、添加配置类、响应类和全局异常处理类

配置类主要是Mybatis-Plus的,用于添加它的分页插件以及相关配置,如Mapper扫描配置。

package org.peanut.exp2.config;import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@MapperScan("org.peanut.exp2.mapper") // 记得改成自己Mapper所在的路径
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}
}

然后根据接口文档的响应数据样例,可以发现返回的内容有很高的相似性,为此创建了响应类Result<T>,而且用到了泛型,为了提高通用性,因为里面的数据类型是多样的,以及创建了快速生成成功或失败响应的静态方法,添加的@Getter注解是为了SpringBoot在响应时能够正常地生成Json格式的内容。

package org.peanut.exp2.common;import lombok.Getter;@Getter
public class Result<T> {Integer code;String message;T data;public Result(Integer code, String message, T data) {this.code = code;this.message = message;this.data = data;}public static <T> Result<T> success(T data) {return new Result<>(0, "操作成功", data);}public static <T> Result<T> error(String message) {return new Result<>(1, message, null);}
}

为了让接口的调用者能够知道SpringBoot发出的所有异常,并通过Result中的message来响应,所以创建了GlobalExceptionHandler类。

package org.peanut.exp2.handler;import org.peanut.exp2.common.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public Result<Void> exception(Exception e) {e.printStackTrace();return Result.error(e.getMessage());}
}

五、根据接口文档编写控制器方法并测试接口

1.新增文章接口

1.1 基本信息

接口描述:该接口用于新增文章(发布文章)
请求路径:/article
请求方式: POST

1.2 请求参数

请求参数格式:application/json
请求参数说明:

参数名称说明类型是否必须备注
title文章标题string1~10个非空字符
content文章正文string-
coverImg封面图像地址string必须是url地址
state发布状态string已发布 | 草稿
categoryId文章分类IDnumber-

请求参数样例:

{"title": "陕西旅游攻略","content": "兵马俑,华清池,法门寺,华山...爱去哪去哪...","coverImg": "https://big-event-gwd.oss-cn-asdbeijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png","state": "草稿","categoryId": 2
}

1.3 响应数据

响应数据类型:application/json
响应参数说明:

名称类型是否必须默认值备注其他信息
codenumber必须响应码, 0-成功,1-失败
messagestring非必须提示信息
dataobject非必须返回的数据

响应数据样例:

{"code": 0,"message": "操作成功","data": null
}

处理请求的方法

根据接口文档可以得知请求路径为/article,且请求方法为POST,那么用的就是@PostMapping注解,然后根据请求格式application/json以及请求样例,可以确定传的参数为实体类,所以参数使用了@RequestBody注解,只是在数据库创建一条数据,所以直接用MyBatis-Plus写好的save方法就行。最后根据响应样例,判断是否返回数据,然后返回相应处理结果的响应。

    @PostMapping("/article")public Result<Void> addArticle(@RequestBody Article article) {judgeArticleValid(article);if(articleService.save(article)) {return Result.success(null);}return Result.error("操作失败");}

而调用的judgeArticleValid方法是用来校验传过来的实体类参数,根据文档的参数说明进行校验,否则抛出带有message的校验异常。

    private void judgeArticleValid(Article article) {if(article.getTitle().isEmpty() || article.getTitle().length() > 10 || article.getTitle().isBlank()) {throw new ValidationException("文章标题不允许为空或只有空字符,且长度需要在1~10内!");}if (article.getContent() == null) {throw new ValidationException("文章正文不允许为空!");}if (article.getCoverImg() == null|| !article.getCoverImg().matches("^(https?|ftp)://[\\w.-]+(?:/[\\w.-]*)*")) {throw new ValidationException("封面图像地址不允许为空,且必须为URL地址!");}if (article.getState() == null || !article.getState().matches("已发布|草稿")) {throw new ValidationException("发布状态不能为空,且只能是“已发布”或“草稿”!");}if(article.getCategoryId() == null) {throw new ValidationException("文章分类ID不允许为空!");}}

测试接口

然后就是使用Postman进行接口测试,首先是成功请求的效果,如下图所示,选择POST方法,填入请求链接并添加JSON的参数。
在这里插入图片描述
其他就再演示一个使用正则校验的封面图像地址校验效果,如下图所示。
在这里插入图片描述

2.文章列表(条件分页)接口

1.1 基本信息

接口描述:该接口用于根据条件查询文章,带分页
请求路径:/article
请求方式: GET

1.2 请求参数

请求参数格式:queryString
请求参数说明:

参数名称说明类型是否必须备注
pageNum当前页码number
pageSize每页条数number
categoryId文章分类IDnumber
state发布状态string已发布|草稿

请求参数样例:

?pageNum=1&pageSize=3&categoryId=2&state=草稿

1.3 响应数据

响应数据类型:application/json
响应参数说明:

名称类型是否必须默认值备注其他信息
codenumber必须响应码, 0-成功,1-失败
messagestring非必须提示信息
dataobject必须返回的数据
|-totalnumber必须总记录数
|-itemsarray必须数据列表
|-idnumber非必须主键ID
|-titlestring非必须文章标题
|-contentstring非必须文章正文
|-coverImgstring非必须文章封面图像地址
|-statestring非必须发布状态已发布 | 草稿
|-categoryIdnumber非必须文章分类ID
|-createTimestring非必须创建时间
|-updateTimestring非必须更新时间

响应数据样例:

{"code": 0,"message": "操作成功","data": {"total": 1,"items": [{"id": 5,"title": "陕西旅游攻略","content": "兵马俑,华清池,法门寺,华山...爱去哪去哪...","coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png","state": "草稿","categoryId": 2,"createTime": "2025-06-01 11:55:30","updateTime": "2025-06-01 11:55:30"}]}
}

处理请求的方法

请求路径一样为/article,且请求方法为GET,那么用的就是@GetMapping注解,然后根据请求格式queryString以及请求样例,可以确定需要传的参数,并且参数使用需要用@RequestParam注解,且非必要的参数设置requiredfalse。其中用到了@Pattern注解来对参数进行校验,用了这个就得在控制类前添加@Validated注解。最后根据响应样例,判断是否返回数据,然后返回相应处理结果的响应。

    @GetMapping("/article")public Result<ArticleList> getArticleList(@RequestParam Integer pageNum,@RequestParam Integer pageSize,@RequestParam(required = false) Integer categoryId,@RequestParam(required = false) @Pattern(regexp = "已发布|草稿", message = "发布状态只能是“已发布”或“草稿”")  String state) {Page<Article> page = new Page<>(pageNum, pageSize);ArticleList al = new ArticleList(articleService.getArticleList(page, categoryId, state));return Result.success(al);}

其中不仅到了分页,还有自定义条件查询,为此在IArticleService接口创建了一个带条件的分页查询的抽象方法,来获取满足条件的文章列表。

    List<Article> getArticleList(Page<Article> page, Integer categoryId, String state);

然后是在ArticleServiceImpl实现这个新添加的方法,里面用到了条件构造器LambdaQueryWrapper来进行条件查询,只有在条件参数不为空才作为其中一个过滤条件。

    @Overridepublic List<Article> getArticleList(Page<Article> page, Integer categoryId, String state) {LambdaQueryWrapper<Article> lqw = new LambdaQueryWrapper<>();// 当条件不为空才添加到生成的 SQL 中lqw.eq(categoryId != null, Article::getCategoryId, categoryId);lqw.eq(state != null, Article::getState, state);return baseMapper.selectList(page, lqw);}

根据响应数据样例,返回的数据不能只是普通的List,还多了一个属性,为此创建了一个新类ArticleList来作为响应的data类型。

package org.peanut.exp2.pojo;import lombok.Data;
import java.util.List;@Data
public class ArticleList {int total;List<Article> items;public ArticleList(List<Article> list) {this.total = list.size();this.items = list;}
}

测试接口

接下来就是测试这个接口了,首先是成功请求的效果,选择POST方法,填入请求链接并添加查询参数。
在这里插入图片描述
然后是缺少查询参数的效果。
在这里插入图片描述

2.获取文章详情接口

1.1 基本信息

接口描述:该接口用于根据ID获取文章详细信息
请求路径:/article/detail
请求方式: GET

1.2 请求参数

请求参数格式:queryString
请求参数说明:

参数名称说明类型是否必须备注
id主键IDnumber

请求参数样例:

?id=1

1.3 响应数据

响应数据类型:application/json
响应参数说明:

名称类型是否必须默认值备注其他信息
codenumber必须响应码, 0-成功,1-失败
messagestring非必须提示信息
dataobject必须返回的数据
|-idnumber非必须主键ID
|-titlestring非必须文章标题
|-contentstring非必须文章正文
|-coverImgstring非必须文章封面图像地址
|-statestring非必须发布状态已发布 | 草稿
|-categoryIdnumber非必须文章分类ID
|-createTimestring非必须创建时间
|-updateTimestring非必须更新时间


响应数据样例:

{"code": 0,"message": "操作成功","data": {"id": 4,"title": "北京旅游攻略","content": "天安门,颐和园,鸟巢,长城...爱去哪去哪...","coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png","state": "已发布","categoryId": 2,"createTime": "2025-06-01 11:57:30","updateTime": "2025-06-01 11:57:30"}
}

处理请求的方法

请求路径为/article/detail,且请求方法也为GET,那么用的就是@GetMapping注解,然后根据请求格式queryString以及请求样例,可以确定需要传的参数,并且为参数添加@RequestParam注解。根据响应样例确定data的数据类型。

    @GetMapping("/article/detail")public Result<Article> getArticle(@RequestParam Integer id) {return Result.success(articleService.getById(id));}

测试接口

测试查询成功效果

在这里插入图片描述
无论是否查询到对应的数据,都会是成功的,除非是没有传入查询参数。
在这里插入图片描述

4.更新文章接口

1.1 基本信息

接口描述:该接口用于更新文章信息
请求路径:/article
请求方式: PUT

1.2 请求参数

请求参数格式:application/json
请求参数说明:

参数名称说明类型是否必须备注
id主键IDnumber
title文章标题string
content文章正文string
coverImg封面图像地址string
state发布状态string已发布 | 草稿
categoryId文章分类IDnumber

请求参数样例:

{"id": 4,"title": "北京旅游攻略","content": "天安门,颐和园,鸟巢,长城...爱去哪去哪...","coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png","state": "已发布","categoryId": 2
}

1.3 响应数据

响应数据类型:application/json
响应参数说明:

名称类型是否必须默认值备注其他信息
codenumber必须响应码, 0-成功,1-失败
messagestring非必须提示信息
dataobject非必须返回的数据

响应数据样例:

{"code": 0,"message": "操作成功","data": null
}

处理请求的方法

请求路径为/article,且请求方法为PUT,那么用的就是@PutMapping注解,根据请求格式以及请求样例,可以确定传的参数为实体类,虽然参数说明中没有明确地对参数进行内容限制,不过应该还是要和新建文章的限制保持一致,所以也调用了judgeArticleValid进行参数校验,另外还需要校验id是否为空,因为是靠id来更新数据库内容的。

    @PutMapping("/article")public Result<Void> updateArticle(@RequestBody Article article) {if (article.getId() == null) {throw new ValidationException("文章id不允许为空!");}judgeArticleValid(article);if(articleService.updateById(article)) {return Result.success(null);}return Result.error("操作失败");}

测试接口

更新文章成功效果。
在这里插入图片描述
缺少文章id参数的校验效果。
在这里插入图片描述

5.删除文章接口

1.1 基本信息

接口描述:该接口用于根据ID删除文章
请求路径:/article
请求方式: DELETE

1.2 请求参数

请求参数格式:queryString
请求参数说明:

参数名称说明类型是否必须备注
id主键IDnumber

请求参数样例:

?id=1

1.3 响应数据

响应数据类型:application/json

名称类型是否必须默认值备注其他信息
codenumber必须响应码, 0-成功,1-失败
messagestring非必须提示信息
dataobject非必须返回的数据

响应数据样例:

{"code": 0,"message": "操作成功","data": null
}

处理请求的方法

请求路径为/article,且请求方法也为DELETE,那么用的就是@DeleteMapping注解,然后根据请求格式queryString以及请求样例,可以确定需要传的参数,并且为参数添加@RequestParam注解。

    @DeleteMapping("/article")public Result<Void> deleteArticle(@RequestParam Integer id) {if (articleService.removeById(id)) {return Result.success(null);}return Result.error("操作失败");}

测试接口

删除文章成功效果。
在这里插入图片描述
当对应id的文章不存在时,会响应失败。
在这里插入图片描述
缺少id参数也是不会成功的。
在这里插入图片描述

总结

项目文件结构如下图所示。
在这里插入图片描述
有新增内容的代码总览。
IArticleService

package org.peanut.exp2.service;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.peanut.exp2.pojo.Article;import java.util.List;public interface IArticleService extends IService<Article> {List<Article> getArticleList(Page<Article> page, Integer categoryId, String state);
}

ArticleServiceImpl

package org.peanut.exp2.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.peanut.exp2.mapper.ArticleMapper;
import org.peanut.exp2.pojo.Article;
import org.peanut.exp2.service.IArticleService;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article>implements IArticleService {@Overridepublic List<Article> getArticleList(Page<Article> page, Integer categoryId, String state) {LambdaQueryWrapper<Article> lqw = new LambdaQueryWrapper<>();// 当条件不为空才添加到生成的 SQL 中lqw.eq(categoryId != null, Article::getCategoryId, categoryId);lqw.eq(state != null, Article::getState, state);return baseMapper.selectList(page, lqw);}
}

ArticleController

package org.peanut.exp2.controller;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.validation.ValidationException;
import jakarta.validation.constraints.Pattern;
import org.peanut.exp2.pojo.Article;
import org.peanut.exp2.common.Result;
import org.peanut.exp2.pojo.ArticleList;
import org.peanut.exp2.service.IArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;@RestController
@Validated
public class ArticleController {@AutowiredIArticleService articleService;@PostMapping("/article")public Result<Void> addArticle(@RequestBody Article article) {judgeArticleValid(article);if(articleService.save(article)) {return Result.success(null);}return Result.error("操作失败");}@GetMapping("/article")public Result<ArticleList> getArticleList(@RequestParam Integer pageNum,@RequestParam Integer pageSize,@RequestParam(required = false) Integer categoryId,@RequestParam(required = false) @Pattern(regexp = "已发布|草稿", message = "发布状态只能是“已发布”或“草稿”")  String state) {Page<Article> page = new Page<>(pageNum, pageSize);ArticleList al = new ArticleList(articleService.getArticleList(page, categoryId, state));return Result.success(al);}@GetMapping("/article/detail")public Result<Article> getArticle(@RequestParam Integer id) {return Result.success(articleService.getById(id));}@PutMapping("/article")public Result<Void> updateArticle(@RequestBody Article article) {if (article.getId() == null) {throw new ValidationException("文章id不允许为空!");}judgeArticleValid(article);if(articleService.updateById(article)) {return Result.success(null);}return Result.error("操作失败");}@DeleteMapping("/article")public Result<Void> deleteArticle(@RequestParam Integer id) {if (articleService.removeById(id)) {return Result.success(null);}return Result.error("操作失败");}private void judgeArticleValid(Article article) {if(article.getTitle().isEmpty() || article.getTitle().length() > 10 || article.getTitle().isBlank()) {throw new ValidationException("文章标题不允许为空或只有空字符,且长度需要在1~10内!");}if (article.getContent() == null) {throw new ValidationException("文章正文不允许为空!");}if (article.getCoverImg() == null|| !article.getCoverImg().matches("^(https?|ftp)://[\\w.-]+(?:/[\\w.-]*)*")) {throw new ValidationException("封面图像地址不允许为空,且必须为URL地址!");}if (article.getState() == null || !article.getState().matches("已发布|草稿")) {throw new ValidationException("发布状态不能为空,且只能是“已发布”或“草稿”!");}if(article.getCategoryId() == null) {throw new ValidationException("文章分类ID不允许为空!");}}
}

参考

官方文档

安装 | MyBatis-Plus
分页插件 | MyBatis-Plus
条件构造器 | MyBatis-Plus

用到的软件

IntelliJ IDEA
Postman API Platform


http://www.hkcw.cn/article/cwoQYyZiEP.shtml

相关文章

CS144 - Lecture 2

CS144 - Lecture 1 TCP 这里就简单讲了一下它的基本性质&#xff0c;没啥好说的 UDP 提供不可靠的传输服务&#xff0c;我们的 DNS 服务和 DHCP 都是用的 UDP 协议。 对于 DNS 我们只是单纯地向 DNS 服务器发送域名&#xff0c;然后返回一个 IP&#xff0c;如果还需要建立…

Go中MAP底层原理分析

MAP底层原理分析 参考 https://golang.design/go-questions/map/principalmap | Golang 中文学习文档 先来看一下map结构体&#xff0c;&#xff08;runtime.hmap结构体就是代表着 go 中的map&#xff0c;与切片一样map的内部实现也是结构体&#xff09; type hmap struct {/…

第十六章 EMQX黑名单与连接抖动检测

系列文章目录 第一章 总体概述 第二章 在实体机上安装ubuntu 第三章 Windows远程连接ubuntu 第四章 使用Docker安装和运行EMQX 第五章 Docker卸载EMQX 第六章 EMQX客户端MQTTX Desktop的安装与使用 第七章 EMQX客户端MQTTX CLI的安装与使用 第八章 Wireshark工具的安装与使用 …

构建系统maven

1 前言 说真的&#xff0c;我是真的不想看构建了&#xff0c;因为真的太多了。又多又乱。Maven、Gradle、Make、CMake、Meson、Ninja&#xff0c;Android BP。。。感觉学不完&#xff0c;根本学不完。。。 但是没办法最近又要用一下Maven&#xff0c;所以咬着牙再简单整理一下…

java CountDownLatch‌

CountDownLatch是用于线程同步的工具类&#xff0c;主要作用是让当前线程等待其他线程完成操作后再继续执行。 示例代码&#xff1a; import java.util.concurrent.CountDownLatch;private static void testCountDownLatch() {int taskNum 5;CountDownLatch latch new Count…

[yolov11改进系列]基于yolov11引入上下文锚点注意力CAA的python源码+训练源码

【CAA介绍】 本文记录的是基于CAA注意力模块的RT-DETR目标检测改进方法研究。在远程遥感图像或其他大尺度变化的图像中目标检测任务中&#xff0c;为准确提取其长距离上下文信息&#xff0c;需要解决大目标尺度变化和多样上下文信息时的不足的问题。CAA能够有效捕捉长距离依赖…

【Java基础】Java入门教程

文章目录 一、Java开发环境概述☕ Java开发全景架构&#x1f4e6; JDK (Java Development Kit)&#x1f5a5;️ IDE (集成开发环境)&#x1f504; 工作流关系 二、JDK下载与安装2.1 下载JDK2.2 安装JDK 三、环境变量配置3.1 Windows配置3.2 macOS/Linux配置为当前用户配置环境变…

通过openpyxl在excel中插入散点图

实现代码 # -*- coding: utf-8 -*- """ Created on Sat May 31 23:30:12 2025author: anyone """from openpyxl import load_workbook from openpyxl.chart import ScatterChart, Reference, Series from openpyxl.chart.series import SeriesL…

零基础安装 Python 教程:从下载到环境配置一步到位(支持 VSCode 和 PyCharm)与常用操作系统操作指南

零基础安装 Python 教程&#xff1a;从下载到环境配置一步到位&#xff08;支持 VSCode 和 PyCharm&#xff09;与常用操作系统操作指南 本文是一篇超详细“Python安装教程”&#xff0c;覆盖Windows、macOS、Linux三大操作系统的Python安装方法与环境配置&#xff0c;包括Pyt…

数据结构第6章 图(竟成)

第 6 章 图 【考纲内容】 1.图的基本概念 2.图的存储及基本操作&#xff1a;(1) 邻接矩阵法&#xff1b;(2) 邻接表法&#xff1b;(3) 邻接多重表、十字链表 3.图的遍历&#xff1a;(1) 深度优先搜索&#xff1b;(2) 广度优先搜索 4.图的基本应用&#xff1a;(1) 最小 (代价) 生…

Microsoft Fabric - 尝试一下Data Factory一些新的特性(2025年5月)

1.简单介绍 Microsoft Fabric是微软提供的一个数据管理和分析的统一平台&#xff0c;感觉最近的新特性也挺多的。 Data Factory是Microsoft Fabric的一个功能模块&#xff0c;也是一个cloud service。Data Factory可以和多种数据源进行连接&#xff0c;同时提供了data movemen…

思科设备网络实验

一、 总体拓扑图 图 1 总体拓扑图 二、 IP地址规划 表格 1 接口地址规划 设备名称 接口/VLAN IP 功能 PC0 VLAN580 10.80.1.1 访问外网 PC1 VLAN581 10.80.2.1 访问外网 PC2 Fa0 20.80.1.100 端口镜像监控流量 PC3 VLAN585 10.80.6.1 远程登陆多层交换机0…

《机器学习数学基础》补充资料:韩信点兵与拉格朗日插值法

本文作者&#xff1a;卓永鸿 19世纪的伟大数学家高斯&#xff0c;他对自己做的数学有非常高的要求&#xff0c;未臻完美不轻易发表。于是经常有这样的情况&#xff1a;其他也很厉害的数学家提出自己的工作&#xff0c;高斯便拿出自己的文章说他一二十年前就做出来了&#xff0…

Go 即时通讯系统:日志模块重构,并从main函数开始

重构logger 上次写的logger.go过于繁琐&#xff0c;有很多没用到的功能&#xff1b;重构后只提供了简洁的日志接口&#xff0c;支持日志轮转、多级别日志记录等功能&#xff0c;并采用单例模式确保全局只有一个日志实例 全局变量 var (once sync.Once // 用于实现…

力扣面试150题--二叉树的锯齿形层序遍历

Day 56 题目描述 思路 锯齿形就是一层是从左向右&#xff0c;一层是从右向左&#xff0c;那么我们可以分析样例&#xff0c;对于第奇数层是从左向右&#xff0c;第偶数层是从右向左&#xff0c;于是可以采取一个计数器&#xff0c;采取链表方式&#xff0c;从左向右就是正常插…

uni-app学习笔记二十一--pages.json中tabBar设置底部菜单项和图标

如果应用是一个多 tab 应用&#xff0c;可以通过 tabBar 配置项指定一级导航栏&#xff0c;以及 tab 切换时显示的对应页。 在 pages.json 中提供 tabBar 配置&#xff0c;不仅仅是为了方便快速开发导航&#xff0c;更重要的是在App和小程序端提升性能。在这两个平台&#xff…

Vue3+SpringBoot全栈开发:从零实现增删改查与分页功能

前言 在现代化Web应用开发中&#xff0c;前后端分离架构已成为主流。本文将详细介绍如何使用Vue3作为前端框架&#xff0c;SpringBoot作为后端框架&#xff0c;实现一套完整的增删改查(CRUD)功能&#xff0c;包含分页查询、条件筛选等企业级特性。 技术栈介绍 前端&#xff1…

用户资产化视角下开源AI智能名片链动2+1模式S2B2C商城小程序的应用研究

摘要&#xff1a;在数字化时代&#xff0c;平台流量用户尚未完全转化为企业的数字资产&#xff0c;唯有将其沉淀至私域流量池并实现可控、随时触达&#xff0c;方能成为企业重要的数字资产。本文从用户资产化视角出发&#xff0c;探讨开源AI智能名片链动21模式S2B2C商城小程序在…

用dayjs解析时间戳,我被提了bug

引言 前几天开发中突然接到测试提的一个 Bug&#xff0c;说我的时间组件显示异常。 我很诧异&#xff0c;这里初始化数据是后端返回的&#xff0c;我什么也没改&#xff0c;这bug提给我干啥。我去问后端&#xff1a;“这数据是不是有问题&#xff1f;”。后端答&#xff1a;“…

适配器模式:让不兼容接口协同工作

文章目录 1. 适配器模式概述2. 适配器模式的分类2.1 类适配器2.2 对象适配器 3. 适配器模式的结构4. C#实现适配器模式4.1 对象适配器实现4.2 类适配器实现 5. 适配器模式的实际应用场景5.1 第三方库集成5.2 遗留系统集成5.3 系统重构与升级5.4 跨平台开发 6. 类适配器与对象适…