【Spring】深入理解 Spring 事务及其传播机制
文章目录
- 一、Spring 事务是什么
- 二、Spring 中事务的实现方法
- 2.1 Spring 编程式事务(手动)
- 2.1.1 编程式事务的使用演示
- 2.1.2 编程式事务存在的问题
- 2.2 Spring 声明式事务(自动)
- 2.2.1 @Transactional 作用范围
- 2.2.2 @Transactional 参数说明
- 2.2.3 @Transactional 捕获异常时回滚失效问题
- 2.4.4 @Transactional 工作原理
- 2.3 Spring 事务失效场景
- 三、事务的隔离级别
- 3.1 事务的特性回顾
- 3.2 MySQL 的事务隔离级别
- 3.3 Spring 事务的隔离级别
- 四、Spring 事务的传播机制
- 4.1 为什么需要事务传播机制
- 4.2 事务传播机制的分类
- 4.3 Spring 事务传播机制使用案例
一、Spring 事务是什么
在 Spring 框架中,事务(Transaction)是一种用于管理数据库操作的机制,旨在确保数据的一致性、可靠性和完整性。事务可以将一组数据库操作(如插入、更新、删除等)视为一个单独的执行单元,要么全部成功地执行,要么全部回滚。这样可以确保数据库在任何时候都保持一致的状态,即使在发生故障或错误时也能保持数据的完整性。
Spring 框架通过提供事务管理功能,使开发者能够更轻松地管理事务的边界。Spring 主要提供了两种主要的事务管理方式:
-
编程式事务管理:通过编写代码显式地管理事务的开始、提交和回滚操作。这种方式提供了更大的灵活性,但也需要更多的代码维护。
-
声明式事务管理:通过在配置中声明事务的行为,由 Spring 框架自动处理事务的边界,减少了开发者的工作量,并提高了代码的可维护性。
二、Spring 中事务的实现方法
2.1 Spring 编程式事务(手动)
2.1.1 编程式事务的使用演示
在 Spring 中,编程式事务管理是一种手动控制事务边界的方式,与 MySQL 操作事务的方法类似,它涉及三个重要的操作步骤:
-
开启事务(获取事务):首先需要通过获取事务管理器(例如
DataSourceTransactionManager)来获取一个事务,从而开始一个新的事务。事务管理器是用于管理事务的核心组件。 -
提交事务:一旦一组数据库操作成功执行,并且希望将这些更改永久保存到数据库中,就可以调用事务对象的提交方法。这将使得事务中的所有操作都被应用到数据库。
-
回滚事务:如果在事务处理过程中发生错误或某种条件不满足,就可以调用事务对象的回滚方法,从而撤销事务中的所有操作,回到事务开始前的状态。
在 Spring Boot 中,可以利用内置的事务管理器 DataSourceTransactionManager 来获取事务,提交或回滚事务。此外,TransactionDefinition 是用来定义事务的属性的,当获取事务时需要将 TransactionDefinition 传递进DataSourceTransactionManager以获取一个事务状态 TransactionStatus。
例如,下面的代码演示了编程式事务:
@RestController
@RequestMapping("/user")
public class UserController {// 编程式事务@Autowiredprivate DataSourceTransactionManager dataSourceTransactionManager;@Autowiredprivate TransactionDefinition transactionDefinition;@Autowiredprivate UserService userService;@RequestMapping("/del")public int delById(@RequestParam("id") Integer id) {if (id == null || id < 0) return 0;// 1. 开启事务TransactionStatus transactionStatus = null;int res = 0;try {transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);// 2. 业务操作 —— 删除用户res = userService.delById(id);System.out.println("删除: " + res);// 3. 提交、回滚事务// 提交事务dataSourceTransactionManager.commit(transactionStatus);} catch (Exception e) {e.printStackTrace();// 回滚事务if (transactionStatus != null) {dataSourceTransactionManager.rollback(transactionStatus);}}return res;}
}
这段代码展示了如何通过编程式事务管理在Spring Boot中处理用户删除操作。编程式事务允许我们在代码中明确地控制事务的边界,以及在需要时手动提交或回滚事务。
2.1.2 编程式事务存在的问题
通过上面的示例代码可以发现,编程式事务虽然提供了更大的灵活性,但也存在一些问题和挑战:
-
代码冗余和可读性差: 编程式事务需要在代码中显式地添加事务管理的逻辑,导致代码变得冗余且难以维护。每次需要使用事务的地方都需要重复编写事务开启、提交和回滚的代码,降低了代码的可读性。
-
事务边界控制复杂: 开发者需要手动管理事务的边界,确保事务的开始、提交和回滚都在正确的位置。这可能会导致遗漏事务管理的代码,从而影响数据的一致性。
-
事务传播和嵌套问题: 在涉及多个方法调用的场景中,手动控制事务的传播和嵌套关系可能变得复杂。需要开发者确保事务在各个方法间正确传播,同时处理好嵌套事务的问题。
-
异常处理繁琐: 编程式事务需要在异常处理时手动进行回滚操作,如果异常处理不当,事务可能无法正确回滚,导致数据不一致。
-
可维护性差: 随着项目的发展,业务逻辑可能会变得更加复杂,可能需要频繁地修改事务管理的代码。这会增加代码维护的难度,可能导致错误的引入。
-
不利于横向扩展: 编程式事务难以支持横向扩展,因为事务管理的代码紧耦合在业务逻辑中,扩展时可能需要修改大量代码。
相比之下,声明式事务管理通过在方法上添加注解或在配置文件中进行声明,使事务管理与业务逻辑分离,提供了更好的代码组织和可维护性。声明式事务可以在切面中自动处理事务的开始、提交和回滚,从而减轻了开发者的工作负担。
所以,大多数情况下,建议使用声明式事务管理来处理事务,特别是在简化事务逻辑和提高代码可读性方面更加有效。
2.2 Spring 声明式事务(自动)
声明式事务的实现非常简单,只需要在需要的方法上添加 @Transactional 注解就可以轻松实现,无需手动开启或提交事务。
- 当进入被注解的方法时,Spring 会自动开启一个事务。
- 方法执行完成后,如果没有抛出未捕获的异常,事务会自动提交,保证数据的一致性。
- 然而,如果方法在执行过程中发生了未经处理的异常,事务会自动回滚,以确保数据库的完整性和一致性。
这种方式大大简化了事务管理的编码,减少了手动处理事务的繁琐操作,提高了代码的可读性和可维护性。例如下面的代码实现:
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;// 声明式事务@RequestMapping("/del")@Transactionalpublic int delById(Integer id) {if (id == null || id < 0) return 0;int result = userService.delById(id);return result;}
}
在这个示例中,delById 方法使用了 @Transactional 注解,表示该方法需要受到声明式事务的管理。在这个方法内部,首先检查了传入的 id,如果为负数则直接返回结果。然后,调用了 userService.delById(id) 方法,删除了指定用户。在方法结束时,事务会自动提交。
同时,如果在执行过程中发生了未处理的异常,事务将会自动回滚,以保持数据库的一致性。这种方式简化了事务管理,提高了代码的可读性和可维护性。
2.2.1 @Transactional 作用范围
@Transactional 注解可以被用来修饰方法或类:
-
当修饰方法时:需要注意它只能应用到
public访问修饰符的方法上,否则注解不会生效。通常推荐在方法级别使用@Transactional。 -
当修饰类时:表示该注解对于类中所有的
public方法都会生效。如果在类级别添加了@Transactional,那么该类中所有的公共方法都将自动应用事务管理。
一般来说,推荐将 @Transactional 注解应用在方法级别,以便更精确地控制事务的范围,从而避免不必要的事务开销。如果类中的所有方法都需要事务管理,那么将注解应用在类级别是一个更方便的选择。
2.2.2 @Transactional 参数说明
通过查看 @Transactional 的源码,可以发现它支持多个参数,用来配置事务的行为。

以下是对其中参数说明:
| 参数名称 | 类型 | 默认值 | 描述 |
|---|---|---|---|
value | String | “” | 事务管理器的名称,与 transactionManager 等效。 |
transactionManager | String | “” | 事务管理器的名称,与 value 等效。 |
label | String[] | 空数组 | 事务标签,暂无具体用途。 |
propagation | Propagation | Propagation.REQUIRED | 事务的传播行为,默认为 REQUIRED。 |
isolation | Isolation | Isolation.DEFAULT | 事务的隔离级别,默认为数据库默认隔离级别。 |
timeout | int | -1 | 事务的超时时间,单位为秒。-1 表示没有超时限制。 |
timeoutString | String | “” | 事务的超时时间的字符串表示,与 timeout 等效。 |
readOnly | boolean | false | 是否将事务设置为只读,默认为 false。 |
rollbackFor | Class<? extends Throwable>[] | 空数组 | 触发回滚的异常类型。 |
rollbackForClassName | String[] | 空数组 | 触发回滚的异常类型的类名字符串。 |
noRollbackFor | Class<? extends Throwable>[] | 空数组 | 不触发回滚的异常类型。 |
noRollbackForClassName | String[] | 空数组 | 不触发回滚的异常类型的类名字符串。 |
这些参数提供了对事务行为的灵活配置,可以根据具体业务需求来调整事务的传播、隔离、超时和回滚策略等。
2.2.3 @Transactional 捕获异常时回滚失效问题
针对于上述的实例代码,现在代码中间模拟实现一个异常,观察会出现什么情况:
@RequestMapping("/del")
@Transactional
public int delById(Integer id) {if (id == null || id < 0) return 0;int result = userService.delById(id);System.out.println(result);try {int num = 10 / 0;} catch (Exception e) {// 如果直接处理异常,则不会回滚e.printStackTrace();}return result;
}
通过浏览器访问,发现服务器成功捕获了异常:

但是事务却没有回滚,对应的用户数据还是被删除了:

其原因在于:
在异常处理中直接捕获了异常并进行了处理,从而导致事务回滚失效。默认情况下,@Transactional 注解会在方法内抛出 RuntimeException 及其子类异常时触发事务回滚。然而,当自己在 catch 块内捕获异常并处理时,Spring 无法感知到异常,从而无法触发事务回滚。
解决方法:
对于这个问题的解决方法大致可以分为两种:
- 将捕获的异常再次抛出:
e.printStackTrace();
throw e;
这种方法通过重新抛出异常,使得 Spring 能够捕获异常并触发事务回滚。在异常发生后,事务将被回滚,确保之前的数据库操作不会生效,从而保持数据的一致性。
- 使用
TransactionAspectSupport手动回滚事务:
e.printStackTrace();
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
这种方法利用了 Spring 提供的 TransactionAspectSupport 类来手动设置事务回滚状态。在捕获异常后,通过调用 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(),可以将当前事务设置为回滚状态,从而达到回滚事务的效果。这种方法更加灵活,可以在需要的时候手动控制事务的回滚。
无论选择哪种方法,都可以在异常发生时触发事务回滚,保障数据的完整性和一致性。选择哪种方法取决于具体的代码逻辑和需求。
2.4.4 @Transactional 工作原理
@Transactional 注解的工作原理基于 Spring AOP(面向切面编程)和事务管理器。它利用了 Spring 框架的代理机制来实现事务管理。
当一个被 @Transactional 注解修饰的方法被调用时,Spring 会创建一个代理对象来包装这个方法。代理对象会在方法执行之前和之后添加事务管理的逻辑,以确保事务的开始、提交和回滚。这个过程是通过 AOP 技术实现的。

具体来说,以下是 @Transactional 注解的工作流程:

-
事务代理的创建: Spring 在运行时会为每个被
@Transactional注解修饰的类创建一个代理对象。这个代理对象会包含事务管理的逻辑。 -
方法调用: 当调用一个被
@Transactional注解修饰的方法时,实际上是通过代理对象来调用。 -
事务切面的触发: 在代理对象中,事务切面会在方法执行前后被触发。在方法执行前,切面会开启一个事务;在方法执行后,切面会根据方法的执行情况决定是提交事务还是回滚事务。
-
事务管理器的使用: 切面会通过事务管理器来控制事务。事务管理器负责实际的事务管理操作,如开启、提交和回滚事务。
-
事务控制: 如果方法正常执行完毕,切面会通知事务管理器提交事务。如果方法在执行过程中抛出异常,切面会通知事务管理器回滚事务。
总体来说,@Transactional 注解的工作原理是通过代理和切面来实现事务管理,将事务的控制与业务逻辑分离,使代码更加模块化和可维护。这也是声明式事务管理的核心机制之一。
2.3 Spring 事务失效场景
在某些情况下,Spring 中的事务可能会失效,导致事务不生效或不按预期执行。以下是一些可能导致事务失效的场景:
-
非
public修饰的方法: 默认情况下,@Transactional注解只对public访问修饰符的方法起作用。如果你在非public方法上添加了@Transactional注解,事务可能不会生效。 -
timeout超时: 如果事务执行的时间超过了设置的timeout值,事务可能会被强制回滚。这可能会导致事务不按预期执行,特别是当事务需要执行较长时间的操作时。 -
代码中有
try/catch: 如果在方法内部捕获并处理了异常,Spring 将无法感知到异常,从而无法触发事务回滚。这可能导致事务在异常发生时不会回滚。 -
调用类内部带有
@Transactional的方法: 当一个类内部的方法被调用时,它的@Transactional注解可能不会生效。这是因为 Spring 默认使用基于代理的事务管理,直接在类内部调用方法不会经过代理,从而事务管理可能不会生效。
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;public int del(Integer id){return delById(id);}// 声明式事务@RequestMapping("/del")@Transactionalpublic int delById(Integer id) {if (id == null || id < 0) return 0;int result = userService.delById(id);return result;}
}
- 数据库不支持事务: 如果你的数据库不支持事务,例如使用了某些特殊的数据库引擎,事务可能无法正常工作。在这种情况下,应该确保使用支持事务的数据库引擎。
三、事务的隔离级别
3.1 事务的特性回顾
在数据库中,事务具有以下四个重要的特性,通常被称为 ACID 特性:
-
原子性(Atomicity): 事务被视为一个不可分割的操作单元,要么全部执行成功,要么全部失败回滚。
-
一致性(Consistency): 事务使数据库从一个一致的状态转变到另一个一致的状态,保证数据的完整性和一致性。
-
隔离性(Isolation): 并发执行的事务之间应该互不影响,每个事务都感觉自己在独立地操作数据。
-
持久性(Durability): 一旦事务提交,其对数据库的修改就应该是永久性的,即使发生系统崩溃也不应该丢失。
3.2 MySQL 的事务隔离级别
MySQL 支持以下四个事务隔离级别,用于控制多个事务之间的相互影响程度:
-
读未提交(Read Uncommitted): 允许一个事务读取另一个事务尚未提交的数据。这是最低的隔离级别,可能会导致脏读、不可重复读和幻读的问题。
-
读已提交(Read Committed): 允许一个事务只能读取另一个事务已经提交的数据。这可以避免脏读,但可能会出现不可重复读和幻读的问题。
-
可重复读(Repeatable Read): 保证在同一个事务中多次读取同样记录的结果是一致的,即使其他事务对该记录进行了修改。这可以避免脏读和不可重复读,但可能出现幻读。
-
串行化(Serializable): 最高的隔离级别,确保每个事务都完全独立运行,避免了脏读、不可重复读和幻读问题,但可能影响并发性能。
以下是事务四个隔离级别对应的脏读、不可重复读、幻读情况:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交 | √ | √ | √ |
| 读已提交 | × | √ | √ |
| 可重复读 | × | × | √ |
| 串行化 | × | × | × |
- √ 表示可能出现该问题。
- × 表示该问题不会出现。
3.3 Spring 事务的隔离级别
Spring 通过 @Transactional 注解中的 isolation 参数来支持不同的事务隔离级别。Isolation的源码如下:

可以使用这些枚举值来设置隔离级别:
Isolation.DEFAULT:使用数据库的默认隔离级别。Isolation.READ_UNCOMMITTED:读未提交。Isolation.READ_COMMITTED:读已提交。Isolation.REPEATABLE_READ:可重复读。Isolation.SERIALIZABLE:串行化。
例如,指定 Spring 事务的隔离级别为 DEFAULT:
@RequestMapping("/del")
@Transactional(isolation = Isolation.DEFAULT)
public int delById(Integer id) {if (id == null || id < 0) return 0;int result = userService.delById(id);return result;
}
通过选择合适的事务隔离级别,可以在并发环境中控制事务之间的相互影响程度,从而避免数据不一致的问题。不同的隔离级别在性能和数据一致性方面有不同的权衡,开发人员需要根据具体的业务需求来选择合适的隔离级别。
四、Spring 事务的传播机制
4.1 为什么需要事务传播机制
在复杂的应用场景中,一个事务操作可能会调用多个方法或服务。这些方法可能需要独立地进行事务管理,但又需要协同工作,以保持数据的一致性和完整性。这时就需要引入事务传播机制。
事务传播机制定义了多个事务方法之间如何协同工作,如何共享同一个事务,以及在嵌套事务中如何进行隔离和提交。通过事务传播机制,可以确保多个事务方法在执行时能够按照一定的规则进行协调,避免数据不一致的问题。
4.2 事务传播机制的分类
Spring 定义了七种事务传播行为,用于控制多个事务方法之间的交互。这些传播行为可以在 @Transactional 注解中的 propagation 参数中进行设置。以下是这些传播行为:
-
REQUIRED(默认): 如果当前存在事务,就加入到当前事务中;如果没有事务,就创建一个新的事务。这是最常用的传播行为。
-
SUPPORTS: 如果当前存在事务,就加入到当前事务中;如果没有事务,就以非事务方式执行。
-
MANDATORY: 如果当前存在事务,就加入到当前事务中;如果没有事务,就抛出异常。
-
REQUIRES_NEW: 无论当前是否存在事务,都创建一个新的事务。如果当前存在事务,则将当前事务挂起。
-
NOT_SUPPORTED: 以非事务方式执行,如果当前存在事务,就将当前事务挂起。
-
NEVER: 以非事务方式执行,如果当前存在事务,就抛出异常。
-
NESTED: 如果当前存在事务,就在一个嵌套的事务中执行;如果没有事务,就与 REQUIRED 一样。
以上 7 种传播行为,可以根据是否支持当前事务分为以下 3 类:

4.3 Spring 事务传播机制使用案例
REQUIRED 和 NESTED 传播机制的事务演示:
控制层 Controller 的 UserController :
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/add") // /add?username=lisi&password=123456@Transactional(propagation = Propagation.NESTED)// Transactional(propagation = Propagation.REQUIRED)//@Transactional(propagation = Propagation.REQUIRES_NEW)public int add(@RequestParam("username") String username, @RequestParam("password") String password) {if (null == username || null == password || "".equals(username) || "".equals(password)) {return 0;}int result = 0;// 用户添加操作UserInfo user = new UserInfo();user.setUsername(username);user.setPassword(password);result = userService.add(user);try {int num = 10 / 0; // 加入事务:外部事务回滚,内部事务也会回滚} catch (Exception e) {e.printStackTrace();TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return result;}
}
服务层Service的UserService:
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate LogService logService;public int delById(Integer id){return userMapper.delById(id);}@Transactional(propagation = Propagation.NESTED)// Transactional(propagation = Propagation.REQUIRED)//@Transactional(propagation = Propagation.REQUIRES_NEW)public int add(UserInfo user){// 添加用户信息int addUserResult = userMapper.add(user);System.out.println("添加用户结果:" + addUserResult);//添加日志信息Log log = new Log();log.setMessage("添加用户信息");logService.add(log);return addUserResult;}
}
服务层Service的LogService :
@Service
public class LogService {@Autowiredprivate LogMapper logMapper;@Transactional(propagation = Propagation.NESTED)// Transactional(propagation = Propagation.REQUIRED)//@Transactional(propagation = Propagation.REQUIRES_NEW)public int add(Log log){int result = logMapper.add(log);System.out.println("添加日志结果:" + result);// 模拟异常情况try {int num = 10 / 0;} catch (Exception e) {// 加入事务:内部事务回滚,外部事务也会回滚,并且会抛异常// 嵌套事务:内部事务回滚,不影响外部事务e.printStackTrace();TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return result;}
}
在事务传播机制中,REQUIRED 和 NESTED 是两种不同的传播行为,它们在事务的嵌套、回滚以及对外部事务的影响等方面有所不同。通过上面代码的演示,可以得出 REQUIRED 和 NESTED 之间的主要区别如下:
-
嵌套性质:
REQUIRED:内部方法与外部方法共享同一个事务,内部方法的事务操作是外部方法事务的一部分。NESTED:内部方法创建一个嵌套事务,它是外部事务的子事务,具有独立的事务状态,内部事务的回滚不会影响外部事务。
-
回滚行为:
REQUIRED:如果内部方法抛出异常或设置回滚,会导致整个外部事务回滚,包括内部方法和外部方法的操作。NESTED:如果内部方法抛出异常或设置回滚,只会回滚内部事务,而外部事务仍然可以继续执行。
-
影响外部事务:
REQUIRED:内部方法的事务操作会影响外部事务的状态,内部方法回滚会导致外部事务回滚。NESTED:内部方法的事务操作不会影响外部事务的状态,内部方法回滚不会影响外部事务的提交或回滚。
-
支持性:
REQUIRED:较为常用,适用于将多个方法的操作作为一个整体进行事务管理的情况。NESTED:在某些数据库中不支持,需要数据库支持保存点(Savepoint)的功能。
总的来说,REQUIRED 适用于需要将多个方法的操作作为一个整体事务管理的情况,而 NESTED 适用于需要在内部方法中创建嵌套事务的情况,保持内部事务的独立性,不影响外部事务。选择使用哪种传播行为取决于业务需求和数据库的支持情况。
相关文章:
【Spring】深入理解 Spring 事务及其传播机制
文章目录 一、Spring 事务是什么二、Spring 中事务的实现方法2.1 Spring 编程式事务(手动)2.1.1 编程式事务的使用演示2.1.2 编程式事务存在的问题 2.2 Spring 声明式事务(自动)2.2.1 Transactional 作用范围2.2.2 Transactional …...
eclipse常用设置
1、调整编辑页面字体大小 窗口 (Window)- 首选项(Preferences)- 常规(General)- 外观 (Appearence)- 颜色与字体 (Colors And Fonts),在右边的对话框里选择 Java - Java Editor Text Font,点击出现的修改&…...
ajax解析
Ajax(Asynchronous JavaScript and XML)是一种用于在不重新加载整个页面的情况下与服务器交换数据的技术。它通过异步的方式发送请求和接收响应,能够实现在后台与服务器进行数据交互,然后更新页面的部分内容,从而提升用…...
CSS3:图片边框
简介 图片也可以作为边框,以下是实例演示 注意 实现该效果必须添加border样式,且必须位于border-image-socure之前否则不会生效 实例 <html lang"en"><head><style>p {width: 600px;margin: 200px auto;border: 30px soli…...
(七)Unity VR项目升级至Vision Pro需要做的工作
Vision Pro 概述 定位为混合现实眼镜,对AR支持更友好 无手柄,支持手(手势)、眼(注视)、语音交互 支持空间音频,相比立体声、环绕声更有沉浸感和空间感 支持VR/AR应用,支持多种应用模…...
【计算机视觉|生成对抗】生成对抗网络(GAN)
本系列博文为深度学习/计算机视觉论文笔记,转载请注明出处 标题:Generative Adversarial Nets 链接:Generative Adversarial Nets (nips.cc) 摘要 我们提出了一个通过**对抗(adversarial)**过程估计生成模型的新框架…...
神经网络基础-神经网络补充概念-15-神经网络概览
概念 神经网络是一种机器学习模型,受到人脑神经元网络的启发而设计。它可以用来解决各种各样的问题,包括图像识别、自然语言处理、语音识别、游戏策略等。神经网络的核心思想是通过一系列的层次结构,从原始数据中自动地学习和提取特征&#…...
iOS Epub阅读器改造记录
六个月前在这个YHEpubDemo阅读器的基础上做了一些优化,这里做一下记录。 1.首行缩进修复 由于分页的存在,新的一页的首行可能是新的一行,则应该缩进;也可能是前面一页段落的延续,这时候不应该缩进。YHEpubDemo基于XDS…...
负载均衡搭建
LVS-DR部署 [客户端] node1 192.168.157.148 [lvs] node2 192.168.157.142 [web服务器] node3 192.168.157.145 node4 192.168.157.146(1)[lvs] yum install -y ipvsadm.x86_64 配置LVS负载均衡服务 (1)手动添加LVS转发1ÿ…...
form表单input标签的23种type类型值?
当你学了很多技术以后,再回头看来,竟然被一个被一个基础问题虐了,23个类型值说不全,不是少这个,就是少那个,那么23种类型都有什么呢? text 单行文本输入框 password 密码输入框 file …...
python selenium如何保存网站的cookie用于下次自动登录
## 一、python selenium如何保存网站的cookie 使用Selenium保存网站的Cookie非常简单。下面是一个示例,展示了如何使用Selenium打开网站,然后保存获取到的Cookie: from selenium import webdriver# 初始化浏览器 browser webdriver.Chrome…...
DHCP Server
简介 动态主机配置协议 DHCP(Dynamic Host Configuration Protocol,动态主机配置协议) 是 RFC 1541(已被 RFC 2131 取代)定义的标准协议,该协议允许服务器向客户端动态分配 IP 地址和配置信息。 使用UDP协…...
DMR+PoC宽窄融合互通解决方案
当前,Tetra、PDT、DMR、P25等专网通信的主流窄带技术,能够保障在紧急情况下可靠的关键语音通信对讲,但随着行业用户对图片、视频、数据等宽带业务的需求越来越强烈,原本单一的语音通信早已无法满足用户对新需求。 北峰BF-SCP810 D…...
Springboot定时执行任务
定时任务 TaskScheduler 任务调度者 TaskExecutor 任务执行者 EnableScheduling//开启定时功能的注释 Scheduled(cron"* * * * * * *")//什么时候执行~ cron表达式(秒 分 时 日 月 星期 ) Cron表达式 主启动类加上注解开启任务调度 package com.qf.sping09te…...
【Apollo】阿波罗自动驾驶:塑造自动驾驶技术的未来
前言 Apollo (阿波罗)是一个开放的、完整的、安全的平台,将帮助汽车行业及自动驾驶领域的合作伙伴结合车辆和硬件系统,快速搭建一套属于自己的自动驾驶系统。 开放能力、共享资源、加速创新、持续共赢是 Apollo 开放平台的口号。百度把自己所拥有的强大、…...
JavaEE初阶:多线程 - Thread 类的基本用法
上次我们了解了多线程的五种创建方法,今天来学习Thread的基本用法。 目录 run和start Thread常见的构造方法 Thread的几个常见属性 后台线程 是否存活 线程终止 1.使用标志位 2.使用Thread自带的标志 等待线程 run和start 首先需要理解Thread的run和star…...
编写 loading、加密解密 发布NPM依赖包,并实施落地使用
你的 Loading 开箱即可用的 loading, 说明:vue3-loading 是一个方便在 Vue 3 项目中使用的加载指示器组件的 npm 插件。它允许您轻松地在项目中添加加载动画,提升用户体验。 目录 你的 Loading🌍 安装🛹 演示地址&…...
【剑指Offer 57】和为s的连续正数序列,Java解密。
LeetCode 剑指Offer 75道练习题 文章目录 剑指Offer:和为s的连续正数序列示例:限制:解题思路:剑指Offer:和为s的连续正数序列 【题目描述】 输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。 序列内的数字由小到大排列,不同序列按照首…...
深度学习实战基础案例——卷积神经网络(CNN)基于SqueezeNet的眼疾识别|第1例
文章目录 前言一、数据准备1.1 数据集介绍1.2 数据集文件结构 二、项目实战2.1 数据标签划分2.2 数据预处理2.3 构建模型2.4 开始训练2.5 结果可视化 三、数据集个体预测 前言 SqueezeNet是一种轻量且高效的CNN模型,它参数比AlexNet少50倍,但模型性能&a…...
麦肯锡发布《2023年度科技报告》!
在经历了 2022 年技术投资和人才的动荡之后,2023 年上半年,人们对技术促进商业和社会进步的潜力重新燃起了热情。生成式人工智能(Generative AI)在这一复兴过程中功不可没,但它只是众多进步中的一个,可以推…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...
