JAVA AOP简单实践(基于SpringBoot)
天行健,君子以自强不息;地势坤,君子以厚德载物。
每个人都有惰性,但不断学习是好好生活的根本,共勉!
文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。
床前明月光,疑是地上霜。
举头望明月,低头思故乡。
——《静夜思》
文章目录
- JAVA AOP简单实践(基于SpringBoot)
- 1. 依赖引入
- 2. 完整依赖
- 3. 项目包结构
- 4. 项目启动类
- 5. 切面类
- 5.1 类创建
- 5.2 切点实现
- 5.3 前置通知
- 5.4 环绕通知
- 5.5 后置通知
- 5.6 返回通知
- 5.7 报错通知
- 5.8 完整切面类代码
- 6. 请求接口的实现
- 6.1 AopTestController.java
- 6.2 AopTest2Controller.java
- 6.3 AopTest3Controller.java
- 7. 测试接口
- 7.1 AopTestController.java的方法
- 7.2 AopTest2Controller.java的方法
- 7.3 AopTest3Controller.java的方法
- 8. 输出打印
- 8.1 test1输出结果
- 8.2 test2输出结果
- 8.3 test3输出结果
- 9. 总结
- 加分加分
- 1. 加分
- 2. 加分
- 3. 加分
- 4. 加分
- 4.1 加分1
- 4.2 加分2
- 4.3 加分3
- 4.4 加分4
关于aop的完整详细版可跳转到链接:Java AOP 介绍与实践
JAVA AOP简单实践(基于SpringBoot)
以下为AOP的简单实现
本项目demo以springboot框架实现
1. 依赖引入
引入aop的依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>2.7.18</version></dependency>
当然了,除了aop的依赖,本demo中还用到了其他依赖
2. 完整依赖
项目的所有依赖如下
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.hslb</groupId><artifactId>aop_demo</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.7.18</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>2.7.18</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.32</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.34</version></dependency></dependencies></project>
3. 项目包结构
该demo的包结构如下,可自行创建,主要为了测试aop匹配不同层级的包的情况

4. 项目启动类
AopApplication.java
package com.aop;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;/*** @ClassDescription: 项目启动类* 使用@EnableAspectJAutoProxy(proxyTargetClass = true)开启aop* @JdkVersion: 1.8* @Author: 李白* @Created: 2024/12/21 14:33*/
@EnableAspectJAutoProxy(proxyTargetClass = true)
@SpringBootApplication
public class AopApplication {public static void main(String[] args) {SpringApplication.run(AopApplication.class, args);}}
5. 切面类
以下切面类分多个部分进行拆分实现,最后有完整的切面类代码
5.1 类创建
创建切面类AopAspectJ.java
并在类上使用注解@Aspect标记该类为切面类,同时需要使用@Component注解将其标记为Bean
package com.aop.common;import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;/*** @ClassDescription: aop切面类* @JdkVersion: 1.8* @Author: 李白* @Created: 2024/12/21 14:35*/
@Aspect
@Component
public class AopAspectJ {}
5.2 切点实现
在AopAspectJ.java类中定义一个切点方法
方法修饰符为public
返回值为void
方法名自定义
在方法上使用@Pointcut注解将其标记为切点方法
同时在注解中将匹配条件填入,以此来进行拦截满足条件的方法
/*** 定义切入点方法,方法名自定义,无参数,无内容,定义的方法名后续注解需要使用,以此方法切入*/
// @Pointcut(value = "execution(* com.aop..*(..))")
// @Pointcut(value = "execution(* com.aop..*.*(..))")
// @Pointcut(value = "execution(* com.aop.controller.*.*(..))")@Pointcut(value = "execution(* com.aop.*.*.*(..))")public void pointcut(){}
5.3 前置通知
在AopAspectJ.java类中定义前置通知,进行方法执行前的逻辑实现
/*** 定义前置通知@Beafore*/@Before(value = "pointcut()")public void before(){System.out.println("前置通知@Before------------------>");}
5.4 环绕通知
在AopAspectJ.java类中定义环绕通知,进行方法执行前、中、后的逻辑实现
/*** 定义环绕置通知@Around* @param proceedingJoinPoint 连接点* @throws Throwable 抛错*/@Around(value = "pointcut()")public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {//前置逻辑System.out.println("环绕通知@Around 执行前------------------>");//执行被通知的方法proceedingJoinPoint.proceed();//后置逻辑System.out.println("环绕通知@Around 执行后------------------>");}
5.5 后置通知
在AopAspectJ.java类中定义后置通知,进行方法执行后的逻辑实现
/*** 定义后置通知@After*/@After(value = "pointcut()")public void after(){System.out.println("后置通知@After------------------>");}
5.6 返回通知
在AopAspectJ.java类中定义返回通知,进行方法执行后并成功返回后的逻辑实现
/*** 定义返回通知@AfterReturning* @param joinPoint 连接点* @param result 返回结果* @return 返回*/@AfterReturning(value = "pointcut()", returning = "result")public JSONObject afterReturning(JoinPoint joinPoint, JSONObject result){System.out.println("返回通知@AfterReturning------------------>");return null;}
5.7 报错通知
在AopAspectJ.java类中定义报错通知,进行方法执行失败后的逻辑实现
/*** 定义报错通知@AfterThrowing*/@AfterThrowing(value = "pointcut()")public void afterThrowing(){System.out.println("报错通知@AfterThrowing------------------>");}
5.8 完整切面类代码
以下为切面类的完整代码实现
AopAspectJ.java
package com.aop.common;import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;/*** @ClassDescription: aop切面类* @JdkVersion: 1.8* @Author: 李白* @Created: 2024/12/21 14:35*/
@Aspect
@Component
public class AopAspectJ {/*** 定义切入点方法,方法名自定义,无参数,无内容,定义的方法名后续注解需要使用,以此方法切入*/
// @Pointcut(value = "execution(* com.aop..*(..))")
// @Pointcut(value = "execution(* com.aop..*.*(..))")
// @Pointcut(value = "execution(* com.aop.controller.*.*(..))")@Pointcut(value = "execution(* com.aop.*.*.*(..))")public void pointcut(){}/*** 定义前置通知@Beafore*/@Before(value = "pointcut()")public void before(){System.out.println("前置通知@Before------------------>");}/*** 定义环绕置通知@Around* @param proceedingJoinPoint 连接点* @throws Throwable 抛错*/@Around(value = "pointcut()")public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {//前置逻辑System.out.println("环绕通知@Around 执行前------------------>");//执行被通知的方法proceedingJoinPoint.proceed();//后置逻辑System.out.println("环绕通知@Around 执行后------------------>");}/*** 定义后置通知@After*/@After(value = "pointcut()")public void after(){System.out.println("后置通知@After------------------>");}/*** 定义返回通知@AfterReturning* @param joinPoint 连接点* @param result 返回结果* @return 返回*/@AfterReturning(value = "pointcut()", returning = "result")public JSONObject afterReturning(JoinPoint joinPoint, JSONObject result){System.out.println("返回通知@AfterReturning------------------>");return null;}/*** 定义报错通知@AfterThrowing*/@AfterThrowing(value = "pointcut()")public void afterThrowing(){System.out.println("报错通知@AfterThrowing------------------>");}}
6. 请求接口的实现
在不同的包中创建请求接口,接口类中的请求路径不同且输出也不同,然后通过表达式不同的拦截条件进行测试不同的接口
查看输出内容即可验证
6.1 AopTestController.java
com.aop.controller.AopTestController.java类
package com.aop.controller;import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;/*** @ClassDescription: 请求测试类* @JdkVersion: 1.8* @Author: 李白* @Created: 2024/12/21 14:34*/
@RestController
@RequestMapping(value = "/aop")
public class AopTestController {@GetMapping(value = "test1")public JSONObject test1(@RequestHeader("token")String token,@RequestParam("username")String username){JSONObject jsonObject = new JSONObject();jsonObject.put("token", token);jsonObject.put("username", username);System.out.println("aop test1 token: "+token+" username: "+username);return jsonObject;}}
6.2 AopTest2Controller.java
com.aop.common.AopTest2Controller.java类
package com.aop.common;import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;/*** @ClassDescription: 请求测试类* @JdkVersion: 1.8* @Author: 李白* @Created: 2024/12/21 14:34*/
@RestController
@RequestMapping(value = "/aop2")
public class AopTest2Controller {@GetMapping(value = "test2")public JSONObject test1(@RequestHeader("token")String token,@RequestParam("username")String username){JSONObject jsonObject = new JSONObject();jsonObject.put("token", token);jsonObject.put("username", username);System.out.println("aop test2 token: "+token+" username: "+username);return jsonObject;}}
6.3 AopTest3Controller.java
com.aop.common.test.AopTest3Controller.java类
package com.aop.common.test;import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;/*** @ClassDescription: 请求测试类* @JdkVersion: 1.8* @Author: 李白* @Created: 2024/12/21 14:34*/
@RestController
@RequestMapping(value = "/aop3")
public class AopTest3Controller {@GetMapping(value = "test3")public JSONObject test1(@RequestHeader("token")String token,@RequestParam("username")String username){JSONObject jsonObject = new JSONObject();jsonObject.put("token", token);jsonObject.put("username", username);System.out.println("aop test3 token: "+token+" username: "+username);return jsonObject;}}
7. 测试接口
启动项目
使用postman进行请求接口测试
依次调用三个接口
7.1 AopTestController.java的方法
请求方式
Get
请求地址
127.0.0.1:8080/aop/test1
请求头参数

请求参数

7.2 AopTest2Controller.java的方法
请求方式
Get
请求地址
127.0.0.1:8080/aop2/test2
请求参数和值都与上一个接口一样
7.3 AopTest3Controller.java的方法
请求方式
Get
请求地址
127.0.0.1:8080/aop3/test3
请求参数和值都与上一个接口一样
8. 输出打印
在postman中分别调用三个接口
控制台输出内容依次如下
8.1 test1输出结果
AopTestController.java中test1的输出结果如下
环绕通知@Around 执行前------------------>
前置通知@Before------------------>
aop test1 token: 13579 username: libai
返回通知@AfterReturning------------------>
后置通知@After------------------>
环绕通知@Around 执行后------------------>

8.2 test2输出结果
AopTest2Controller.java中test1的输出结果如下
环绕通知@Around 执行前------------------>
前置通知@Before------------------>
aop test2 token: 13579 username: libai
返回通知@AfterReturning------------------>
后置通知@After------------------>
环绕通知@Around 执行后------------------>

8.3 test3输出结果
AopTest3Controller.java中test1的输出结果如下
aop test3 token: 13579 username: libai

9. 总结
因为我们的表达式是execution(* com.aop.*.*.*(..))
该表达式拦截的是com.aop包下一级包中的所有类的所有方法,不包含下一级包的子包
test1和test2都是aop下一级包的类中的方法
test1是aop.controller包下的类的方法
test2是aop.common包下的类的方法
而test3是aop.common.test包下类的方法,已经是aop包下一级包common包的子包test包下的方法,多了一级,因此拦截不到
感谢阅读,祝君暴富!
版权声明:
- 作者:寒山李白
- 博客地址:https://hanshan.blog.csdn.net/
- 版权:本作品采用《创作共享许可证》进行许可,根据该许可授权的内容可在符合本许可证条款的前提下自由使用、、修改和创作衍生作品。
版权许可介绍:
本文采用CC BY-NC-SA许可证
此许可允许在使用者仅出于非商业目的以任何媒体或格式分发、重新混合、改编和构建材料,并且前提是注明创作者。如果您重新混合、改编或基于该材料进行构建,则必须按照相同的条款对修改后的材料进行许可。
更多信息请访问以下网址查看:
版权官网 https://creativecommons.org/licenses/by-nc-sa/4.0/
中文翻译 https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans
文章结束
--------------------------------------------------------------------------------------------------------------------------------------
以下内容增加文章评分用,可忽略
加分加分
黄河之水天上来,奔流到海不复回
高堂明镜悲白发,朝如青丝暮成雪
1. 加分
黄河之水天上来,奔流到海不复回
高堂明镜悲白发,朝如青丝暮成雪
2. 加分
黄河之水天上来,奔流到海不复回
高堂明镜悲白发,朝如青丝暮成雪
3. 加分
寒山李白
寒山李白
4. 加分
账号密码
| 名称 | 密码 |
|---|---|
| 李白 | 123 |
4.1 加分1
账号密码
| 名称 | 密码 |
|---|---|
| 李白 | 123 |
4.2 加分2
账号密码
| 名称 | 密码 |
|---|---|
| 李白 | 123 |
4.3 加分3
账号密码
| 名称 | 密码 |
|---|---|
| 李白 | 123 |
4.4 加分4
账号密码
| 名称 | 密码 |
|---|---|
| 李白 | 123 |
相关文章:
JAVA AOP简单实践(基于SpringBoot)
天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…...
java agent的使用【通俗易懂版】
一、静态代理Agent 1.生成Agent的jar包 (1)创建Agent项目,引入javassist.jar包 (2)编写premain方法 import java.lang.instrument.Instrumentation;public class Agent1 {public static void premain(Stri…...
大模型学习指南
随着人工智能的迅猛发展,大模型成为了技术前沿的璀璨明星。踏入大模型学习领域,需要在多个关键方面下功夫。 扎实的数学功底是基石。线性代数为理解多维数据、矩阵运算提供支撑,像大模型中权重矩阵的处理就离不开它;概率论与数理…...
单片机:实现定时器中断(数码管读秒+LED闪烁)(附带源码)
单片机实现定时器中断:数码管读秒与LED闪烁 在单片机项目中,定时器中断是一个常见的应用,用于实现定时任务,例如定时更新显示或控制周期性事件。本文将介绍如何使用定时器中断实现数码管读秒和LED闪烁功能。通过使用定时器中断&a…...
STM32单片机芯片与内部33 ADC 单通道连续DMA
目录 一、ADC DMA配置——标准库 1、ADC配置 2、DMA配置 二、ADC DMA配置——HAL库 1、ADC配置 2、DMA配置 三、用户侧 1、DMA开关 (1)、标准库 (2)、HAL库 2、DMA乒乓 (1)、标准库 ÿ…...
【0376】Postgres内核 分配 last safe MultiXactId
上一篇: 【0375】Postgres内核 XLOG 之 设置下一个待分配 MultiXactId 和 offset 文章目录 1. 最后一个安全的 MultiXactId1.1 计算 multi wrap limit1.2 计算 multi stop limit1.3 计算 multi warn limit1.4 计算 multi vacuum limit2. 初始化 MultiXactState 成员3. 完成 mu…...
php时间strtotime函数引发的问题 时间判断出错
在 PHP 中,strtotime 函数能处理的最大时间范围取决于您的系统和 PHP 版本。 一般来说,它可以处理的时间范围从 1901 年 12 月 13 日到 2038 年 1 月 19 日。超过这个范围可能会导致不可预测的结果或错误。 如果您需要处理更大范围的时间,可能…...
Kibana:LINUX_X86_64 和 DEB_X86_64两种可选下载方式的区别
最近需要在vm(操作系统是 Ubuntu 22.04.4 LTS,代号 Jammy。这是一个基于 x86_64 架构的 Linux 发行版)上安装一个7.17.8版本的Kibana,并且不采用docker方式。 在下载的时候发现有以下两个选项,分别是 LINUX_X86_64 和 …...
【LeetCode每日一题】 LeetCode 151.反转字符串中的单词
LeetCode 151.反转字符串中的单词 题目描述 给你一个字符串 s ,请你反转字符串中单词的顺序。 单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。 返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。 注意:…...
gitlab克隆仓库报错fatal: unable to access ‘仓库地址xxxxxxxx‘
首次克隆仓库,失效了,上网查方法,都说是网络代理的问题,各种清理网络代理后都无效,去问同事: 先前都是直接复制的网页url当做远端url,或者点击按钮‘使用http克隆’ 这次对于我来说有效的远端u…...
在已有vue cli项目中添加单元测试配置
使用的是vue cli ^4.0.0的脚手架,项目采用的vue2进行编写,项目本身是没有使用单元测试的。应该挺多项目还是使用的vue2的项目进行开发的,自己在开发中过程中,还是发生了挺多需要记录原来功能的情况,这个时候去翻文档明…...
企业级NoSql数据库REDIS集群
1.1数据库主要分为两大类:关系型数据库与 NoSQL数据库 关系型数据库,是建立在关系模型基础上的数把库,其借助于集合代数等数学概念和方法来处理数据库中的数掘主流的 MySQLOracle、Ms sOLSerer和 DB2 都属于这类传统数据库 NoSQL数据库,全称…...
HTML与数据抓取:GET与POST方法详解
讲GET和POST就不能只讲GET和POST 你要讲HTTP请求的基本概念: HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网上应用最为广泛的一种网络协议,主要用于Web浏览器与Web服务器之间的数据通信。HTTP是一个基于…...
【es6复习笔记】模板字符串(3)
介绍 模板字符串是 ES6 引入的一种新的字符串声明方式,它使用反引号()来定义字符串,而不是单引号()或双引号(")。模板字符串可以包含变量、表达式和换行符,这使得它…...
cursor保存更改操作技巧
1. 当我们在agent模式时,要求cursor更改代码时,cursor回答后,就已经更改了代码了,这时候就可以对程序进行编译和测试, 不一定先要点” accept“, 先测试如果没有问题再点“accept”,这样composer就会多一条…...
ASP.NET |日常开发中定时任务详解
ASP.NET |日常开发中定时任务详解 前言一、定时任务的概念与用途1.1 定义1.2 应用场景 二、在ASP.NET中实现定时任务的方式2.1 使用System.Timers.Timer2.2 使用Quartz.NET 三、定时任务的部署与管理3.1 部署考虑因素3.2 管理与监控 结束语优质源码分享 ASP.NET &am…...
【零基础保姆级教程】制作自己的数据集(二)——Labelme的安装与使用及常见的报错解决方法
前段时间安装了Labelimg,网上有些博客写着Labelme能进行语义分割的标注,但UI窗口就那么大找不着选项,只能打矩形框,为了能够标注自己的分割数据集,遂写下该教程以供参考。 采用Labelimg进行目标检测标注的教程如下。 …...
Move AI技术浅析(二):输入与预处理
一、视频输入模块 1.1 视频输入步骤详解 视频输入模块的主要任务是接收视频数据,并将其转换为后续处理所需的格式。具体步骤: 1.1.1 视频读取 步骤:从文件系统、网络流或摄像头读取视频数据。技术:使用 OpenCV 的 cv2.VideoCa…...
实践KDTS-WEB从mysql迁移到kingbasev9
数据库国产化替代数据迁移是一个复杂且关键的过程。这涉及到将原有数据库中的数据准确、完整地迁移到新的国产数据库中,同时确保数据的完整性和一致性。人大金仓提供了强大的数据库迁移工具(KDTS)对同构、异构数据库数据迁移; 数…...
WebGIS实战开源项目:智慧机场三维可视化(学习笔记)
From:新中地 1.简介 智慧机场解决方案,基于数字化大平台,融合AI、大数据、IoT、视频云、云计算等技术,围绕机场“运控、安防、服务”三大业务领域,构建“出行一张脸”及“运行一张图”两大场景化解决方案。 https://…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
