基于飞算JavaAI实现布隆过滤器防止缓存穿透:原理、实践与全流程解析

引言:当缓存失效时,系统如何避免“雪崩式崩溃”?

在互联网高并发场景中(如电商秒杀、社交平台热点新闻),缓存是提升系统性能的核心手段——将频繁访问的数据(如商品详情、用户信息)存储在内存(如Redis)中,避免每次请求都查询数据库。但缓存并非万能,当出现**“缓存穿透”**时,系统可能面临严重风险:

什么是缓存穿透?

用户请求的数据既不在缓存中,也不在数据库中(例如恶意攻击者故意查询不存在的商品ID=999999),此时请求会直接穿透缓存层,打到数据库上。若这类请求量巨大(如每秒数千次),数据库会因无法承受负载而崩溃,最终导致整个服务不可用。

传统解决方案的局限性

常见的防穿透方案包括:

  1. 缓存空值:当数据库查询结果为空时,仍缓存一个特殊标记(如null),但会浪费内存且需处理缓存与空值的逻辑;
  2. 接口校验:对请求参数(如ID范围)做基础校验(如ID必须大于0),但无法拦截精心构造的非法请求;
  3. 布隆过滤器:通过概率型数据结构快速判断“某个数据一定不存在”,从根本上拦截无效请求,且内存占用极低。

其中,**布隆过滤器(Bloom Filter)**是业界公认的高效解决方案——它用极小的内存空间(例如存储1亿个数据仅需约100MB)记录“可能存在的数据集合”,当请求到来时,先通过布隆过滤器判断目标数据是否“可能存在于缓存/数据库中”,若判断为“一定不存在”,则直接拦截请求,避免无效查询。

本文将基于飞算JavaAI低代码平台,详细讲解如何快速实现布隆过滤器防穿透方案,包含原理通俗解析、系统设计流程图、关键代码逻辑(附注释)、飞算AI辅助生成的代码示例,以及如何通过可视化配置降低开发门槛。最后还会用饼图展示不同方案的优缺点对比,帮助读者直观理解技术选型依据。

一、布隆过滤器核心原理:用概率换效率的“黑魔法”

1.1 基础概念:它到底是什么?

布隆过滤器是一种空间效率极高的概率型数据结构,用于判断一个元素是否在一个集合中。它的特点是:

  • 如果判断“可能存在”(可能误判):实际不存在的元素可能被误判为存在(但概率极低,可调节);
  • 如果判断“一定不存在”(绝对准确):只要布隆过滤器说“不存在”,则该元素100%不在集合中;

核心优势:用极小的内存(例如存储1亿个URL仅需约114MB)实现高效查询(时间复杂度O(k),k为哈希函数个数),且支持快速插入和判断。

1.2 工作原理:通过多个哈希函数“标记”数据

布隆过滤器的底层是一个长度为m的二进制位数组(初始全为0),以及k个不同的哈希函数(如 MurmurHash、SHA-1 等)。当插入一个元素时,执行以下操作:

  1. 用k个哈希函数分别计算该元素的哈希值,得到k个位置(范围在0到m-1之间);
  2. 将位数组中这k个位置的值置为1;

当判断一个元素是否存在时:

  1. 同样用k个哈希函数计算该元素的k个位置;
  2. 检查位数组中这k个位置是否全部为1
    • 如果全部为1 → 元素“可能存在”(可能有其他元素哈希到了相同位置,导致误判);
    • 如果有任意一个位置为0 → 元素“一定不存在”(因为插入时所有位置都会被置为1);

1.3 关键参数:如何平衡内存与误判率?

布隆过滤器的性能由三个参数决定:

  • 预期元素数量(n):需要存储的数据总量(例如缓存中所有有效商品ID共100万个);
  • 误判率(p):可接受的“误判为存在”的概率(通常设为0.01%1%,即1%100个请求中可能有1个误判);
  • 位数组长度(m)和哈希函数个数(k):根据n和p通过公式计算得出:
    • 位数组长度:m=−nln⁡p(ln⁡2)2m = -\frac{n \ln p}{(\ln 2)^2}m=(ln2)2nlnp (例如n=100万,p=0.01时,m≈9.6MB);
    • 哈希函数个数:k=mnln⁡2k = \frac{m}{n} \ln 2k=nmln2 (通常为5~10个);

实际开发中无需手动计算,Java开源库(如Google的Guava)已提供封装好的布隆过滤器实现,只需指定预期元素数量和误判率即可。

二、系统设计:如何用飞算JavaAI集成布隆过滤器?

2.1 整体架构图(文字描述+简化流程)

我们将基于Spring Boot+Redis+飞算JavaAI构建一个防缓存穿透的商品查询服务,架构分为四层:

  1. 客户端层:用户发起请求(如查询商品ID=1001的详情);
  2. 网关/接口层:接收请求,调用业务服务;
  3. 业务服务层(核心)
    • 布隆过滤器层:先判断请求的商品ID是否“可能存在于有效数据集合中”;
      • 若布隆过滤器返回“一定不存在” → 直接拦截请求,返回“商品不存在”(避免查缓存和数据库);
      • 若布隆过滤器返回“可能存在” → 继续查询缓存;
    • 缓存层(Redis):若缓存命中,直接返回数据;
    • 数据库层(MySQL):若缓存未命中,查询数据库,并将结果写入缓存(同时更新布隆过滤器,若为新数据);
  4. 数据存储层:Redis(缓存)、MySQL(持久化数据)。

关键点:布隆过滤器需在服务启动时预热(加载所有有效商品ID),并在新增商品时动态更新(保证新数据能被正确判断)。

2.2 核心流程图(文字描述+步骤分解)

一定不存在
可能存在
缓存命中
缓存未命中
数据库命中
数据库未命中
用户请求商品ID
布隆过滤器判断
直接返回商品不存在
查询Redis缓存
返回缓存数据
查询MySQL数据库
写入Redis缓存
并更新布隆过滤器
返回商品不存在
返回数据库数据

三、基于飞算JavaAI的实现步骤:低代码如何简化开发?

3.1 飞算JavaAI的核心助力

飞算JavaAI作为低代码平台,可通过可视化配置和AI辅助编码,快速生成以下功能:

  • 布隆过滤器的初始化与预热(自动生成加载有效数据到布隆过滤器的代码);
  • 拦截逻辑的封装(自动生成判断“是否存在”的代码,并集成到请求处理流程中);
  • 缓存与数据库的交互逻辑(自动生成Redis查询、MySQL查询及缓存更新的代码);

业务人员只需通过界面配置参数(如预期商品数量、误判率),AI即可生成高可用的Java代码,无需手动编写复杂的位数组操作或哈希函数逻辑。

3.2 关键模块实现详解

模块1:布隆过滤器初始化与预热(服务启动时加载有效数据)
需求细节:

系统启动时,需要将数据库中所有有效的商品ID(例如状态为“上架”的商品)加载到布隆过滤器中,确保后续请求能正确判断“可能存在”。

飞算JavaAI的解决方案:

通过可视化配置工具定义“数据源(MySQL表)→ 过滤条件(如status=1)→ 布隆过滤器参数(预期数量=10万,误判率=0.01)”,平台自动生成初始化代码。

关键代码逻辑(飞算AI生成,简化注释版)
// 1. 布隆过滤器工具类(基于Guava库,由飞算AI封装)
@Component
public class BloomFilterUtil {private static BloomFilter<Long> productBloomFilter; // 商品ID布隆过滤器(假设商品ID为Long类型)private static final int EXPECTED_INSERTIONS = 100000; // 预期商品数量(根据业务配置)private static final double FPP = 0.01; // 误判率1%@PostConstruct // Spring启动时自动执行初始化public void init() {// 初始化布隆过滤器(Guava提供的高效实现)productBloomFilter = BloomFilter.create(Funnels.longFunnel(), // 指定数据类型为Long(商品ID)EXPECTED_INSERTIONS, FPP);// 从数据库加载所有有效商品ID(状态=1表示上架)List<Long> validProductIds = productService.loadValidProductIds(); for (Long productId : validProductIds) {productBloomFilter.put(productId); // 将有效ID加入过滤器}System.out.println("布隆过滤器预热完成,已加载 " + validProductIds.size() + " 个商品ID");}// 判断商品ID是否可能存在(供业务层调用)public static boolean mightContain(Long productId) {return productBloomFilter.mightContain(productId);}// 新增商品时更新布隆过滤器(如商品上架接口调用)public static void put(Long productId) {productBloomFilter.put(productId);}
}// 2. 商品服务:加载有效商品ID(由飞算AI生成数据库查询逻辑)
@Service
public class ProductService {@Autowiredprivate JdbcTemplate jdbcTemplate; // Spring Boot数据库操作工具public List<Long> loadValidProductIds() {// 查询状态为1(上架)的商品IDString sql = "SELECT id FROM product WHERE status = 1";return jdbcTemplate.queryForList(sql, Long.class); // 返回所有有效商品ID列表}
}
关键点说明:
  • 自动预热:通过@PostConstruct注解,服务启动时自动执行init()方法,从数据库加载有效商品ID并填充到布隆过滤器;
  • 参数可配置:预期数量(EXPECTED_INSERTIONS)和误判率(FPP)可通过飞算AI的可视化界面调整(如业务方告知“预计有50万商品”,则修改为500000,AI自动重新计算位数组大小);
  • 动态更新:当新增商品上架时(如调用productService.addProduct()),需同步调用BloomFilterUtil.put(productId)更新过滤器(后续会讲解)。
模块2:拦截无效请求(请求到来时先判断布隆过滤器)
需求细节:

用户请求商品详情时,先通过布隆过滤器判断该商品ID是否“可能存在于有效集合中”:

  • 若判断为“一定不存在”(即布隆过滤器返回false)→ 直接返回“商品不存在”,避免查询缓存和数据库;
  • 若判断为“可能存在” → 继续查询Redis缓存;
飞算JavaAI的解决方案:

通过可视化配置“拦截逻辑位置(Controller层)→ 判断条件(调用BloomFilterUtil.mightContain)”,平台自动生成请求拦截代码。

关键代码逻辑(飞算AI生成,简化注释版)
// 1. 商品详情Controller(接收用户请求)
@RestController
@RequestMapping("/api/product")
public class ProductController {@Autowiredprivate ProductService productService; // 业务服务@GetMapping("/{productId}")public ResponseEntity<Product> getProduct(@PathVariable Long productId) {// 第一步:通过布隆过滤器拦截无效请求(飞算AI自动生成判断逻辑)if (!BloomFilterUtil.mightContain(productId)) {return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null); // 直接返回404}// 第二步:查询缓存(由飞算AI生成Redis交互逻辑)Product cachedProduct = redisService.getProductFromCache(productId);if (cachedProduct != null) {return ResponseEntity.ok(cachedProduct); // 缓存命中}// 第三步:查询数据库(由飞算AI生成MySQL交互逻辑)Product dbProduct = productService.getProductFromDb(productId);if (dbProduct != null) {// 写入缓存并更新布隆过滤器(若为新商品)redisService.cacheProduct(dbProduct);if (!BloomFilterUtil.mightContain(productId)) { // 可能是新上架的商品BloomFilterUtil.put(productId); }return ResponseEntity.ok(dbProduct);}// 数据库也未找到(理论上不会发生,因为布隆过滤器已拦截)return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);}
}// 2. Redis服务(简化版,由飞算AI生成缓存操作逻辑)
@Service
public class RedisService {@Autowiredprivate RedisTemplate<String, Product> redisTemplate;public Product getProductFromCache(Long productId) {String key = "product:" + productId;return redisTemplate.opsForValue().get(key); // 从Redis获取缓存}public void cacheProduct(Product product) {String key = "product:" + product.getId();redisTemplate.opsForValue().set(key, product, 1, TimeUnit.HOURS); // 缓存1小时}
}
关键点说明:
  • 前置拦截:在Controller的最开始处调用BloomFilterUtil.mightContain(),无效请求直接返回,减少后续逻辑执行;
  • 动态更新处理:若查询数据库发现商品存在但布隆过滤器未包含(例如新上架商品未预热),则调用BloomFilterUtil.put()更新过滤器(避免后续请求被误拦截);
  • 缓存交互:Redis操作通过Spring Data Redis的RedisTemplate实现,飞算AI自动生成序列化/反序列化逻辑(如Product对象转JSON存储)。
模块3:缓存与数据库的完整交互流程
补充说明:

当请求的商品ID通过布隆过滤器拦截后,系统会按以下顺序查询数据:

  1. 查缓存(Redis):若命中,直接返回(最快路径);
  2. 查数据库(MySQL):若命中,写入缓存并更新布隆过滤器(若为新商品),再返回;
  3. 均未命中:返回“商品不存在”(理论上布隆过滤器已拦截,此分支极少触发)。
关键代码逻辑(飞算AI生成的数据库查询)
// 商品服务中的数据库查询方法(由飞算AI生成)
@Service
public class ProductService {@Autowiredprivate JdbcTemplate jdbcTemplate;public Product getProductFromDb(Long productId) {String sql = "SELECT id, name, price, status FROM product WHERE id = ?";try {return jdbcTemplate.queryForObject(sql, new Object[]{productId}, (rs, rowNum) -> {Product product = new Product();product.setId(rs.getLong("id"));product.setName(rs.getString("name"));product.setPrice(rs.getDouble("price"));product.setStatus(rs.getInt("status"));return product;});} catch (EmptyResultDataAccessException e) {return null; // 数据库未找到}}
}

四、飞算JavaAI的可视化配置示例:如何降低开发门槛?

4.1 可视化参数配置界面(模拟说明)

业务人员通过飞算平台的Web界面配置以下参数(无需写代码):

  • 布隆过滤器参数
    • 预期商品数量:输入“100000”(根据数据库统计);
    • 误判率:选择“1%”(平衡内存与准确性);
  • 数据源配置
    • 数据库类型:MySQL;
    • 表名:product;
    • 过滤条件:status=1(只加载上架商品);
  • 拦截规则
    • 请求路径:/api/product/{productId};
    • 拦截条件:布隆过滤器判断为“不存在”时返回404;

平台根据上述配置,自动生成对应的Java代码(包括布隆过滤器初始化、拦截逻辑、缓存交互等),业务人员只需预览生成的代码并确认即可。

4.2 生成的代码结构(简化目录)

src/main/java/
├── com/example/demo/
│   ├── config/               # 飞算AI生成的配置类
│   │   └── BloomFilterConfig.java  # 布隆过滤器参数配置
│   ├── controller/           # 控制器(自动生成拦截逻辑)
│   │   └── ProductController.java
│   ├── service/              # 业务服务(自动生成数据库/缓存操作)
│   │   ├── ProductService.java
│   │   └── RedisService.java
│   ├── util/                 # 工具类(自动生成布隆过滤器工具)
│   │   └── BloomFilterUtil.java
│   └── entity/               # 数据模型
│       └── Product.java

五、方案对比:布隆过滤器 vs 传统防穿透方法(饼图可视化)

5.1 不同防穿透方案的优缺点对比

方案核心原理优点缺点内存占用适用场景
缓存空值对空结果也缓存(如缓存null)实现简单,无需额外组件浪费内存(需缓存大量空标记);需处理空值逻辑高(尤其无效请求多时)无效请求极少的小型系统
接口校验校验参数合法性(如ID范围)实现简单,拦截明显非法请求无法拦截精心构造的合法但不存在的ID(如ID=999999)极低辅助手段(不能单独使用)
布隆过滤器概率型数据结构判断“可能存在”内存占用极低(1亿数据约100MB);绝对拦截“一定不存在”的请求有极低误判率(可调节);需预热和动态更新极低高并发、大流量系统首选

5.2 饼图:内存占用对比(假设存储100万条数据)

0%100%0%存储100万条数据时的内存占用对比布隆过滤器(误判率1%)缓存空值(每个空标记100字节)无防穿透(直接查库)

说明:布隆过滤器仅需约9.6MB存储100万条数据,而缓存空值(假设每个空标记占100字节)需约100MB(实际更大),无防穿透方案则无额外内存开销但数据库压力大。

六、项目落地流程总结:从需求到上线的关键步骤

6.1 阶段1:需求分析与参数确认(0-1天)

  • 与业务方确认:预期数据量(如商品总数)、可接受的误判率(通常1%)、无效请求的占比(用于评估防穿透必要性);

6.2 阶段2:飞算JavaAI配置与代码生成(0-1天)

  • 通过可视化界面配置布隆过滤器参数(预期数量、误判率)、数据源(数据库表及过滤条件);
  • 定义拦截规则(哪些接口需要防穿透,如商品查询、订单查询);
  • 平台自动生成初始化、拦截逻辑、缓存交互的Java代码;

6.3 阶段3:测试与优化(1天)

  • 压测验证:模拟大量无效请求(如查询不存在的ID=999999),确认是否被拦截;
  • 调整参数:若误判率过高(如业务要求更严格),通过飞算AI调整误判率并重新生成代码;
  • 动态更新测试:新增商品后,验证布隆过滤器是否自动包含新ID;

6.4 阶段4:上线与监控(持续)

  • 生产环境部署,监控布隆过滤器的命中率(通过日志统计拦截的无效请求数量);
  • 定期优化:根据业务增长调整预期数据量(如商品总数从100万增至500万时,通过飞算AI重新配置参数)。

结语:布隆过滤器+飞算JavaAI=高并发系统的“防护盾”

缓存穿透是高并发系统的常见“杀手”,而布隆过滤器通过概率型数据结构以极低的内存代价实现了高效的拦截能力。本文基于飞算JavaAI平台,展示了如何通过可视化配置和AI辅助编码,快速实现从布隆过滤器初始化、请求拦截到缓存交互的全流程防穿透方案。

对于企业而言,这种方案不仅降低了开发门槛(业务人员可参与配置),还显著提升了系统的稳定性和可维护性(代码由平台生成,减少人为错误)。未来,随着飞算JavaAI对更多AI算法(如动态调整布隆过滤器参数)的支持,防穿透方案将更加智能和高效。

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

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

相关文章

DeepResearch开源与闭源方案对比

在这个AI不再只是聊天工具的时代&#xff0c;"深度研究"已经成为大语言模型&#xff08;LLM&#xff09;的一项新能力。先进的LLM不再只是给出快速的一次性回答&#xff0c;而是可以像研究助手一样工作——搜索网上信息&#xff0c;调用各种工具&#xff08;比如搜索…

UniApp 页面传参方式详解

在 UniApp 开发中&#xff0c;页面间参数传递是核心功能之一。以下是 8 种常用的传参方式&#xff0c;每种方式都有其适用场景和特点&#xff1a;一、URL 拼接传参&#xff08;最常用&#xff09; 适用场景&#xff1a;简单数据传递&#xff0c;如 ID、状态值等基础类型数据 实…

音频分类标注工具

pyqt 分类标注工具&#xff1a;import glob import sys import json import os from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableWidget, QTableWidgetItem,QSplitter, QVBoxLayout, QWidget, QPushButton, QRadioButton,QButtonGroup, QLabel, QHBoxLayout, Q…

云计算-Kubernetes+Istio 实现金丝雀发布:流量管理、熔断、流量镜像、ingreess、污点及pv案例实战

介绍 在微服务架构中,如何安全、高效地实现服务发布与流量管理是保障业务稳定性的核心挑战。金丝雀发布(Canary Release)、灰度发布等策略通过精细化的流量控制,可有效降低新版本上线风险, Istio 作为主流的服务网格(Service Mesh)工具。 此次Istio 在 Kubernetes 集群…

12.web api 3

定时器-间歇函数

ComfyUI进阶:EchoMimic插件全解析,让静态肖像实现音频驱动的精准口型动画

在数字内容创作中&#xff0c;让静态肖像“开口说话”并做出自然表情&#xff0c;是提升交互感与沉浸感的关键。传统动画制作需专业人员逐帧调整口型与表情&#xff0c;成本高且效率低。ComfyUI的EchoMimic插件通过音频驱动技术&#xff0c;实现了“输入音频→自动生成匹配口型…

链式前向星、vector存图

场景设定 想象你是一个社交达人&#xff0c;要记录你和所有朋友的关系&#xff08;这就是“图”&#xff09;。每个朋友是一个节点&#xff0c;关系是一条边。你需要快速回答&#xff1a;“我有哪些朋友&#xff1f;”&#xff08;遍历邻居&#xff09;。方式1&#xff1a;链式…

YAML 中定义 List 的几种方式

在 YAML 配置文件中定义 List 并在 Spring 应用中注入是非常常见的操作&#xff0c;下面详细介绍具体写法和注入方式。一、YAML 中定义 List 的几种方式1. 缩进式写法&#xff08;推荐&#xff09;最常用的方式&#xff0c;通过短横线 - 加空格表示列表项&#xff1a;yaml# app…

C# 反射和特性(自定义特性)

自定义特性 你或许已经注意到了&#xff0c;应用特性的语法和之前见过的其他语法有很大不同。你可能会觉得特 性是一种完全不同的结构类型&#xff0c;其实不是&#xff0c;特性只是一种特殊的类。 有关特性类的一些要点如下。 用户自定义的特性类叫作自定义特性。所有特性类都…

科目二的四个电路

一.K21电动机单连续运转接线(带点动控制)1.电路图2.主线路这可很明了,是一条直线,从上接到下就OK了,然后从热继电器出来,接到SB3按钮的常闭触点上接着往下走一端接到SB2的常闭触点上,接着往下走&#xff0c;走到接触器的线圈上,从L2借一条火线出来,从熔断器的上端接入,另一端接…

【位运算】查询子数组最大异或值|2693

本文涉及知识点 位运算、状态压缩、枚举子集汇总 3277. 查询子数组最大异或值 给你一个由 n 个整数组成的数组 nums&#xff0c;以及一个大小为 q 的二维整数数组 queries&#xff0c;其中 queries[i] [li, ri]。 对于每一个查询&#xff0c;你需要找出 nums[li…ri] 中任…

HTML DOM 方法

HTML DOM 方法 引言 HTML DOM&#xff08;文档对象模型&#xff09;是HTML文档的编程接口&#xff0c;它允许开发者通过JavaScript来操作HTML文档中的元素。DOM 方法是DOM编程的核心&#xff0c;它提供了丰富的操作手段来改变网页的结构、样式和行为。本文将详细介绍HTML DOM中…

w嵌入式分享合集68

自己的原文哦~ https://blog.51cto.com/whaosoft/14133002 一、一键开关机电路的设计方案 方案一&#xff1a;电路图 一键开关机电路分析如下&#xff1a; 电路工作流程如下&#xff1a; Key按下瞬间&#xff0c;Q2、Q1导通&#xff0c;7805输入电压在8.9V左右&…

FFmpeg QoS 处理

FFmpeg 中的 QoS (服务质量) 处理主要关注于实时流媒体传输中的时序控制、丢帧策略和网络适应等方面。以下是 FFmpeg 中 QoS 相关的关键机制和配置方法。1. 基本 QoS 机制丢帧策略 (Frame Dropping)cAVDictionary *options NULL; av_dict_set(&options, "framedrop&q…

TexStudio中的Latex,PDFLatex,XeLatex和LuaLatex的区别

多种LaTeX编译器一、多种LaTeX编译器 1.1 PDFLaTeX&#xff08;1994年&#xff09; 默认、最常用的引擎。 输入文件通常是 ASCII 或 UTF-8 编码&#xff08;但中文需要 CJK 宏包或 ctex 宏包支持&#xff09;。 字体选择受限&#xff1a;只能使用 TeX 自带的字体或者 Type 1…

容器化部署:用Docker封装机器翻译模型与服务详解

文章目录一、机器翻译容器化的技术栈选型1.1 为什么需要容器化MT模型&#xff1f;1.2 基础镜像选择对比1.3 典型依赖分层方案1.4 性能对比&#xff08;容器化 vs 原生部署&#xff09;二、关键部署模式2.1 轻量级API服务封装2.2 模型热更新策略三、Docker镜像构建3.1 编写Docke…

leetcode_42 接雨水

1. 题意 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 2. 题解 这个题不会做&#xff0c;全部是看得题解捏。 不过能看懂题解感觉自己也很棒了&#xff01; 看完题解后感觉最难的是如何求出有多少…

Spring Boot 整合 Thymeleaf 模板引擎:从零开始的完整指南

引言&#xff1a;为什么选择 Thymeleaf&#xff1f; Thymeleaf 是一个现代化的服务器端 Java 模板引擎&#xff0c;专为 Web 开发而设计。与 JSP 不同&#xff0c;Thymeleaf 模板是纯 HTML 文件&#xff0c;可以直接在浏览器中预览&#xff0c;无需后端服务器支持。这种"…

pytest介绍(python测试框架)(@pytest.mark.parametrize、@pytest.fixtures)

文章目录**1. 核心特点**- **简洁易用**&#xff1a;无需复杂的配置&#xff0c;只需编写简单的函数或类即可进行测试。- **丰富的断言**&#xff1a;直接使用 Python 内置的 assert 语句&#xff0c;失败时提供详细的错误信息。- **自动发现测试**&#xff1a;通过约定的命名规…

[Python 基础课程]继承

在 Python 的面向对象编程&#xff08;OOP&#xff09;中&#xff0c;继承&#xff08;Inheritance&#xff09; 是一种重要的机制&#xff0c;它允许一个类&#xff08;称为子类或派生类&#xff09;从另一个类&#xff08;称为父类、基类或超类&#xff09;中继承属性和方法。…