掌握 Spring 事务管理:深入理解 @Transactional 注解
在业务方法上使用@Transactional
开启声明式事务时,很有可能由于使用方式有误,导致事务没有生效。
环境准备
表结构
CREATE TABLE `admin` (`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,`username` varchar(255) DEFAULT NULL,`password` varchar(255) DEFAULT NULL,`name` varchar(255) DEFAULT NULL,`phone` int(11) DEFAULT NULL,`power` int(11) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB;
实体类
@Entity
@Data
public class Admin {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String username;private String password;private String name;private Integer phone;private Integer power;
}
DAO 层
@Repository
public interface AdminRepository extends JpaRepository<Admin,Long> {List<Admin> findByName(String name);
}
上面这些类都是不变的,主要是 service
类。
事务失效
非 public
当被@Transactional
注解修饰的方法为非public
时,事务将失效。
@Service
@Slf4j
public class AdminService {@Autowiredprivate AdminRepository adminRepository;@Transactionalprotected void saveAdmin(Admin admin) {adminRepository.save(admin);if (admin.getName().contains("@")) {throw new RuntimeException("不合法");}}
}
在同包下新建一个测试类。
@Autowired
private AdminService adminService;@GetMapping("/addAdminWrong")
public void add(@RequestParam("name") String name) {Admin admin = new Admin();admin.setName(name);adminService.saveAdmin(admin);
}
测试接口发现,即使用户名不合法,用户也能创建成功。
@Transactional 生效原则(一):只有定义在 public 方法上的 @Transactional 才能生效。原因是,Spring 默认通过动态代理的方式实现 AOP,对目标方法进行增强,private 方法无法代理到,Spring 自然也无法动态增强事务处理逻辑。
所以,将 saveAdmin
方法,修改为 public,就可以了。
自调用
当saveAdmin
方法是 public时,事务一定能生效吗?
答案是不一定,比如下面这个例子。
@Service
@Slf4j
public class AdminService {@Autowiredprivate AdminRepository adminRepository;public int addAdminWrong(String name) {Admin admin = new Admin();admin.setName(name);try {/*** 一些其他业务处理*/this.saveAdmin(admin);} catch (Exception e) {log.error("添加失败:{}",e);}return adminRepository.findByName(name).size();}@Transactionalpublic void saveAdmin(Admin admin) {adminRepository.save(admin);if (admin.getName().contains("@")) {throw new RuntimeException("不合法");}}
}
在上面代码中,我们新定义了一个addAdminWrong
方法,并在它内部调用了本类的saveAdmin
方法。
测试代码如下:
@GetMapping("/addAdminWrong")
public void add(@RequestParam("name") String name) {adminService.addAdminWrong(name);
}
测试后发现,不合法的用户,还是被创建成功了。
Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
观察日志发现,自调用因为没有走代理,事务没有在 saveAdmin
方法上生效,只在 SimpleJpaRepository
上的 save 方法层面生效。
最简单的修改方案是,在AdminService
类中,自己注入自己,比如:
@Autowired
private AdminService self;
然后通过 self 实例去调用 self.saveAdmin(admin)
还有一种优雅的方案,是通过AopContext
在代理对象中获取自身。
比如:
AdminService adminService = (AdminService) AopContext.currentProxy();
adminService.saveAdmin(admin);
然后就会发现一个异常:
Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context.
它的意思是:没有开启一个 exposeProxy
的属性,导致无法暴露出代理对象,从而无法获取。
所以我们在启动类上加上这个注解 @EnableAspectJAutoProxy(exposeProxy=true)
即可。
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy=true) //暴露代理对象
public class StarterDemoApplication {public static void main(String[] args) {SpringApplication.run(StarterDemoApplication.class, args);}
}
然后,再观察日志,发现事务在AdminService.saveAdmin
方法上生效了
Creating new transaction with name [com.starter.demo.controller.AdminService.saveAdmin]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
@Transactional 生效原则(二):需要确保方法调用是通过Spring的代理对象进行的,而不是直接在类内部调用。
异常处理不当
上面的两个例子,是由于事务失效导致回滚失败。
接下来,我们来看下,即使事务生效也会回滚失败的场景。
(一):被@Transactional
注解标记的方法抛出了异常,事务才会回滚。
意思就是说,得把异常抛出来才行。
在 Spring 的 TransactionAspectSupport.invokeWithinTransaction
方法中,可以找到处理事务的逻辑,可以看到只有捕获到异常才能进行后续事务处理。
比如这段代码,虽然在方法中抛出了异常,但又被它自己给捕获了。
@Transactionalpublic void saveAdminWrong1(String name) {Admin admin = new Admin();admin.setName(name);try {adminRepository.save(admin);throw new RuntimeException("模拟错误");} catch (Exception e) {log.error("save admin error:",e);}}
同时再次观察日志可以发现,虽然事务在AdminService.saveAdminWrong1
上是生效的,但由于异常没有被传播出去,所以无法回滚。
Creating new transaction with name [com.starter.demo.controller.AdminService.saveAdminWrong1]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
我们可以手动回滚当前事务,在 catch 代码块中加上TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
通过日志可以看到回滚的信息。
2024-11-22 17:13:08.537 DEBUG 7219 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Transactional code has requested rollback
2024-11-22 17:13:08.537 DEBUG 7219 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Initiating transaction rollback
2024-11-22 17:13:08.537 DEBUG 7219 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Rolling back JPA transaction on EntityManager [SessionImpl(1495642341<open>)]
(二):默认情况下,出现 RuntimeException 或 Error 的时候,Spring 才会回滚事务。
追踪completeTransactionAfterThrowing
方法,可以看到,它是根据异常的类型来决定是否回滚的。
点进 rollbackOn
方法,可以看到,它只会在RuntimeException
或 Error
的时候返回 true。
比如这段代码,我们希望保存用户的时候,同时去加载一个文件,如果加载文件失败,则事务需要回滚。
@Transactional
public void saveAdminWrong2(String name) throws IOException {Admin admin = new Admin();admin.setName(name);adminRepository.save(admin);otherTask(); //额外的操作}private void otherTask() throws IOException{Files.readAllLines(Paths.get("admin.txt"));}
同时观察日志可以发现,虽然事务在AdminService.saveAdminWrong2
上是生效的,也没有去捕获异常,但是由于传播出去的是 checked exception
,所以事务也不会回滚。
Creating new transaction with name [com.starter.demo.controller.AdminService.saveAdminWrong2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
可以在注解中声明,希望遇到所有的 Exception
都回滚事务。
@Transactional(rollbackFor = Exception.class)
public void saveAdminWrong2(String name) throws IOException {Admin admin = new Admin();admin.setName(name);adminRepository.save(admin);otherTask(); //额外的操作}private void otherTask() throws IOException{Files.readAllLines(Paths.get("test.sql"));
}
同时观察日志可以发现,事务在AdminService.saveAdminWrong2
上是生效的,还看到了回滚的日志信息。
2024-11-22 17:29:24.846 DEBUG 7560 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [com.starter.demo.controller.AdminService.saveAdminWrong2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception
2024-11-22 17:29:24.948 DEBUG 7560 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Initiating transaction rollback
2024-11-22 17:29:24.948 DEBUG 7560 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Rolling back JPA transaction on EntityManager [SessionImpl(1844074856<open>)]
事务传播行为不当
比如,在插入用户信息的时候,也插入一份扩展信息,但由于扩展信息不是很重要,即使它失败了,也不要影响到我们的主逻辑(把插入扩展信息的操作当成一个独立的事务)。
@Service
@Slf4j
public class AdminService {@Autowiredprivate AdminRepository adminRepository;@Autowiredprivate AddressService addressService;@Transactionalpublic void saveAdminWrong3(String name) {//1.保存用户信息saveAdmin(name);//2.保存扩展信息addressService.saveAddress(name);}private void saveAdmin(String name) {Admin admin = new Admin();admin.setName(name);adminRepository.save(admin);log.info("save admin success");}
}
@Service
@Slf4j
public class AddressService {@Autowiredprivate AddressRepository addressRepository;@Transactionalpublic void saveAddress(String name) {Address address = new Address();address.setName(name);log.info("saveAddress start");addressRepository.save(address);throw new RuntimeException("模拟 save address 失败");}
}
可以看到,saveAddress
的操作是失败的,按照我们的期望,saveAdmin
方法不能受到影响,能够正常插入成功。
测试执行后发现,saveAdmin
方法出现了回滚,不符合我们的预期。
2024-11-22 18:02:18.171 DEBUG 8251 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Rolling back JPA transaction on EntityManager [SessionImpl(809976208<open>)]
2024-11-22 18:02:18.188 DEBUG 8251 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Not closing pre-bound JPA EntityManager after transaction
2024-11-22 18:02:18.188 ERROR 8251 --- [nio-8080-exec-1] c.s.demo.controller.TestController : 模拟 save address 失败
2024-11-22 18:02:18.205 DEBUG 8251 --- [nio-8080-exec-1] o.j.s.OpenEntityManagerInViewInterceptor : Closing JPA EntityManager in OpenEntityManagerInViewInterceptor
我们来猜想一下,是不是因为saveAddress
抛出的异常,没有在saveAdminWrong3
中捕获,而saveAdminWrong3
也会接着往上层抛,导致被回滚了呢?
所以呢,我们先在saveAdminWrong3
方法中捕获一下saveAddress
抛出的异常试试。
@Transactional
public void saveAdminWrong3(String name) {//1.保存用户信息saveAdmin(name);//2.保存扩展信息try {addressService.saveAddress(name);} catch (Exception e) {log.error("save address error:{}",e.getMessage());}
}
运行程序,再次观察日志:
2024-11-22 18:39:41.741 DEBUG 8496 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [com.starter.demo.service.AdminService.saveAdminWrong3]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT2024-11-22 18:39:41.850 DEBUG 8496 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Participating transaction failed - marking existing transaction as rollback-only2024-11-22 18:39:41.851 ERROR 8496 --- [nio-8080-exec-1] com.starter.demo.service.AdminService : save address error:模拟 save address 失败2024-11-22 18:39:41.851 DEBUG 8496 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Committing JPA transaction on EntityManager [SessionImpl(98155953<open>)]2024-11-22 18:39:41.884 ERROR 8496 --- [nio-8080-exec-1] c.s.demo.controller.TestController : org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only
通过日志,可以发现:
- 在
AdminService.saveAdminWrong3
上开启了事务处理; - 当前事务被标记为了回滚;
- 在
saveAdminWrong3
中打印出了saveAddress
的异常信息; - 主方法已经提交了事务;
- 在
TestController
中打印了一个UnexpectedRollbackException
,提示这个事务要静默回滚了。
UnexpectedRollbackException
是 Spring 框架抛出的一个异常,表明事务由于某些原因被静默地标记为只能回滚(rollback-only),意味着事务不会正常提交,而是会在结束时被回滚。
在saveAdminWrong3
方法中并没有出现异常,所以在事务提交时,发现当前事务已经被子方法设置成了回滚,导致无法正常提交,进而证实了saveAdminWrong3
和saveAddress
使用了同一个事务。
在@Transactional
注解中,propagation
属性决定了事务的传播行为,默认是REQUIRED
。
REQUIRED
:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
所以,这也说明了saveAddress
方法不会开启一个新事务,而是会加入到saveAdminWrong3
的事务中。
所以,需要将propagation
设置为 REQUIRES_NEW
。
REQUIRES_NEW
:它会创建一个新事务,如果当前存在事务,把当前事务挂起,直到新事务完成。这种传播行为适用于需要独立于当前事务的场景。
修改AddressService
的代码,其他不变。
@Service
@Slf4j
public class AddressService {@Autowiredprivate AddressRepository addressRepository;@Transactional(propagation = Propagation.REQUIRES_NEW)public void saveAddress(String name) {Address address = new Address();address.setName(name);log.info("saveAddress start");addressRepository.save(address);throw new RuntimeException("模拟 save address 失败");}
}
再次执行,查看日志:
2024-11-22 19:13:56.643 DEBUG 9201 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [com.starter.demo.service.AdminService.saveAdminWrong3]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT2024-11-22 19:13:56.734 INFO 9201 --- [nio-8080-exec-1] com.starter.demo.service.AdminService : save admin success2024-11-22 19:13:56.734 DEBUG 9201 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Suspending current transaction, creating new transaction with name [com.starter.demo.service.AddressService.saveAddress]2024-11-22 19:13:56.781 DEBUG 9201 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Initiating transaction rollback2024-11-22 19:13:56.833 DEBUG 9201 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Resuming suspended transaction after completion of inner transaction2024-11-22 19:13:56.833 ERROR 9201 --- [nio-8080-exec-1] com.starter.demo.service.AdminService : save address error:模拟 save address 失败2024-11-22 19:13:56.834 DEBUG 9201 --- [nio-8080-exec-1] o.s.orm.jpa.JpaTransactionManager : Committing JPA transaction on EntityManager [SessionImpl(1073120187<open>)]
通过日志可以看到
- 在
AdminService.saveAdminWrong3
上开启了事务处理; - admin 创建完成;
- 主事务挂起了,在
AddressService.saveAddress
上开启了一个新的子事务; - 子事务回滚了;
- 子事务完成,继续被挂起的主事务;
- 捕获到了
saveAddress
的异常; - 主事务提交了,没有看到静默回滚的异常。
相关文章:

掌握 Spring 事务管理:深入理解 @Transactional 注解
在业务方法上使用Transactional开启声明式事务时,很有可能由于使用方式有误,导致事务没有生效。 环境准备 表结构 CREATE TABLE admin (id bigint(20) unsigned NOT NULL AUTO_INCREMENT,username varchar(255) DEFAULT NULL,password varchar(255) …...

字符三角形
字符三角形 C语言代码C语言代码Java语言代码Python语言代码 💐The Begin💐点点关注,收藏不迷路💐 给定一个字符,用它构造一个底边长5个字符,高3个字符的等腰字符三角形。 输入 输入只有一行, …...

【LLM】一文学会SPPO
博客昵称:沈小农学编程 作者简介:一名在读硕士,定期更新相关算法面试题,欢迎关注小弟! PS:哈喽!各位CSDN的uu们,我是你的小弟沈小农,希望我的文章能帮助到你。欢迎大家在…...

如何通过ChatGPT提高自己的编程水平
在编程学习的过程中,开发者往往会遇到各种各样的技术难题和学习瓶颈。传统的学习方法依赖书籍、教程、视频等,但随着技术的不断发展,AI助手的崛起为编程学习带来了全新的机遇。ChatGPT,作为一种强大的自然语言处理工具,…...

NVR管理平台EasyNVR多品牌NVR管理工具的流媒体视频融合与汇聚管理方案
随着信息技术的飞速发展,视频监控已经成为现代社会安全管理和业务运营不可或缺的一部分。无论是智慧城市、智能交通、还是大型企业、校园安防,视频监控系统的应用都日益广泛。NVR管理平台EasyNVR,作为功能强大的流媒体服务器软件,…...

python之使用django框架开发web项目
本问将对django框架在python的web项目中的使用进行介绍,有不对之处,烦请指正。 首先使用创建一个django工程(本示例中使用pycharm2024+python3.12),名称和项目保存路径根据自己的需要自行修改,新手直接默认本机环境就好(关于conda将会另开一篇进行讲解。),最后点击cre…...

ChatGPT 桌面版发布了,如何安装?
本章教程教大家如何进行安装。 一、下载安装包 官网地址地址:https://openai.com/chatgpt/desktop/ 支持Windows和MacOS操作系统 二、安装步骤 Windows用户下载之后,会有一个exe安装包,点击运行安装即可。 注意事项,如果Windows操…...

ubuntu 配置 多个 git 客户端 账户
Git配置两个或多个账户 https://blog.csdn.net/mainking2003/article/details/134711865 git 提交 不用输入用户名、密码的方法(GIT免密提交) https://blog.csdn.net/wowocpp/article/details/125797263 git config 用法 https://blog.csdn.net/blueb…...

React Native的界面与交互
React Native (RN) 是一个由 Facebook 开发的开源框架,用于构建跨平台的移动应用程序。它允许开发者使用 JavaScript 和 React 来创建原生 iOS 和 Android 应用。RN 的出现极大地简化了移动应用的开发过程,使得开发者可以更快速、更高效地构建高质量的应…...

autogen+ollama+litellm实现本地部署多代理智能体
autogen 是一个专门为大语言模型 (LLMs) 驱动的自治代理 (autonomous agents) 设计的 Python 库,由 Microsoft 开发和维护。它通过高度模块化和可扩展的架构,支持用户快速构建和运行多代理系统,这些代理可以在没有明确人类干预的情况下协作完成复杂任务。AutoGen 支持以最少…...

InstantStyle容器构建指南
一、介绍 InstantStyle 是一个由小红书的 InstantX 团队开发并推出的图像风格迁移框架,它专注于解决图像生成中的风格化问题,旨在生成与参考图像风格一致的图像。以下是关于 InstantStyle 的详细介绍: 1.技术特点 风格与内容的有效分离 &a…...

百度主动推送可以提升抓取,它能提升索引量吗?
站长在建站SEO的时候,需要用到百度站长平台(资源平台)的工具,在站长工具中【普通收录】-【资源提交】-【API提交】这个功能,对网站的抓取进行一个提交。 这里估计很多站长就有疑问,如果我主动推送…...

A045-基于spring boot的个人博客系统的设计与实现
🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹 赠送计算机毕业设计600…...

JavaEE 【知识改变命运】02 多线程(1)
文章目录 线程是什么?1.1概念1.1.1 线程是什么?1.1.2 为什么要有线程1.1.3 进程和线程的区别1.1.4 思考:执行一个任务,是不是创建的线程或者越多是不是越好?(比如吃包子比赛)1.1.5 ) Java 的线程…...

Pytorch使用手册-Transforms(专题四)
Transforms(变换) 在 PyTorch 数据处理中的重要性和使用方法,特别是如何通过 torchvision.transforms 模块对数据进行预处理和变换,使其适合用于训练机器学习模型。以下是具体的内容解读: 什么是 Transforms? 数据通常在收集后并非直接适合用于训练机器学习模型,需要通…...

【Android】ARouter的使用及源码解析
文章目录 简介介绍作用 原理关系 使用添加依赖和配置初始化SDK添加注解在目标界面跳转界面不带参跳转界面含参处理返回结果 源码基本流程getInstance()build()navigation()_navigation()Warehouse ARouter初始化init帮助类根帮助类组帮助类 completion 总结 简介 介绍 ARouter…...

ValueError: bbox_params must be specified for bbox transformations
错误 ValueError: bbox_params must be specified for bbox transformations 是因为使用了需要处理边界框(bboxes)的增强操作,但在 albumentations.Compose 中没有正确设置bbox_params 参数。 bbox_params 是用来指定如何处理边界框的配置。…...

挂壁式空气净化器哪个品牌的质量好?排名top3优秀产品测评分析
随着挂壁式空气净化器市场的不断扩大,各类品牌与型号琳琅满目。但遗憾的是,一些跨界网红品牌过于追求短期效益,导致产品在净化效果与去除异味方面表现平平,使用体验不佳,甚至可能带来二次污染风险,影响人体…...

钉钉数据如何高效集成到金蝶云星空系统
钉钉数据集成到金蝶云星空的技术案例分享 在企业日常运营中,办公用品采购流程的高效管理至关重要。为了实现这一目标,我们采用了轻易云数据集成平台,将钉钉中的采购申请单数据无缝对接到金蝶云星空系统中。本次案例将详细解析【办公用品采购…...

躺平成长-腾讯云数据库(又消失了一次)
开源竞争: 当你无法彻底掌握技术的时候,你就开源这个技术,形成更多的技术依赖,你会说 这不就是在砸罐子吗?一个行业里面总会有人砸罐子的,你不如先砸罐子,还能听个响声。 数据库的里面清洁的数据…...

初学 flutter 问题记录
windows搭建flutter运行环境 一、运行 flutter doctor遇到的问题 Xcmdline-tools component is missingRun path/to/sdkmanager --install "cmdline-tools;latest"See https://developer.android.com/studio/command-line for more details.1)cmdline-to…...

Hadoop的MapReduce详解
文章目录 Hadoop的MapReduce详解一、引言二、MapReduce的核心概念1、Map阶段1.1、Map函数的实现 2、Reduce阶段2.1、Reduce函数的实现 三、MapReduce的执行流程四、MapReduce的使用实例Word Count示例1. Mapper类2. Reducer类3. 执行Word Count 五、总结 Hadoop的MapReduce详解…...

全新配置ubuntu18.04深度学习环境
1、下载显卡驱动 1.1、驱动下载 连接:显卡驱动 手动驱动搜索-》查找-》查看-》下载 下载可使用指令 wget https://us.download.nvidia.com/XFree86/Linux-x86_64/535.216.01/NVIDIA-Linux-x86_64-535.216.01.run 2、下载安装cuda12.0 wget https://developer.do…...

持续集成与持续部署:CI/CD实现教程
以下是一个基于常见工具实现 CI/CD 的基本教程示例,这里以 Git、Jenkins、Maven(用于 Java 项目构建和管理依赖,其他语言项目可替换为对应构建工具)以及 Docker(用于容器化部署,非必需但很常用)…...

深度学习实验十二 卷积神经网络(3)——基于残差网络实现手写体数字识别实验
目录 一、模型构建 1.1残差单元 1.2 残差网络的整体结构 二、统计模型的参数量和计算量 三、数据预处理 四、没有残差连接的ResNet18 五、带残差连接的ResNet18 附:完整的可运行代码 实验大体步骤: 先前说明: 上次LeNet实验用到的那…...

Linux系统如何排查端口占用
如何在Linux系统中排查端口占用 在Linux系统中,当您遇到网络服务无法启动或响应异常的情况时,可能是因为某个特定的端口已经被其他进程占用。这时,您需要进行端口占用情况的排查来解决问题。本文将介绍几种常用的命令行工具和方法࿰…...

Linux常用命令之id命令详解
id命令详解 id 命令在 Linux 和 Unix 系统中用于显示用户的标识信息,包括用户ID(UID)、组ID(GID)以及用户所属的附加组。这个命令对于系统管理员和开发者来说非常有用,因为它能帮助他们确认运行命令或脚本…...

WGCLOUD如何部署在ARM平台
WGCLOUD是一款开源免费的运维平台,非常强大方便,可以帮我们提高运维效率 我们项目中,大部分是ARM的服务器,那么如何部署WGCLOUD呢,其实挺简单的 首先是部署服务端server 我们只要安装好对应ARM版本的JDK,…...

K8S + Jenkins 做CICD
前言 这里会做整体CICD的思路和流程的介绍,会给出核心的Jenkins pipeline脚本,最后会演示一下 实验/实操 结果 由于整体内容较多,所以不打算在这里做每一步的详细演示 - 本文仅作自己的实操记录和日后回顾用 要看保姆式教学的可以划走了&…...

HarmonyOS4+NEXT星河版入门与项目实战(11)------Button组件
文章目录 1、控件图解2、案例实现1、代码实现2、代码解释3、运行效果4、总结1、控件图解 这里我们用一张完整的图来汇整 Button 的用法格式、属性和事件,如下所示: 按钮默认类型就是胶囊类型。 2、案例实现 这里我们实现一个根据放大和缩小按钮来改变图片大小的功能。 功…...