- 👏作者简介:大家好,我是爱敲代码的小黄,独角兽企业的Java开发工程师,CSDN博客专家,阿里云专家博主
- 📕系列专栏:Java设计模式、数据结构和算法、Kafka从入门到成神、Kafka从成神到升仙、Spring从成神到升仙系列
- 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
- 🍂博主正在努力完成2023计划中:以梦为马,扬帆起航,2023追梦人
- 📝联系方式:hls1793929520,加我进群,大家一起学习,一起进步👀

文章目录
对于Java开发者而言,关于 Spring ,我们一般当做黑盒来进行使用,不需要去打开这个黑盒。
但随着目前程序员行业的发展,我们有必要打开这个黑盒,去探索其中的奥妙。
本期 Spring 源码解析系列文章,将带你领略 Spring 源码的奥秘
本期源码文章吸收了之前 Kafka 源码文章的错误,将不再一行一行的带大家分析源码,我们将一些不重要的部分当做黑盒处理,以便我们更快、更有效的阅读源码。
废话不多说,发车!
本篇目录如下:

本文流程图可关注公众号:爱敲代码的小黄,回复:事务 获取
贴心的小黄为大家准备的文件格式为 POS文件,方便大家直接导入 ProcessOn 修改使用
数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。
事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。
一个逻辑工作单元要成为事务,必须满足所谓的 ACID(原子性、一致性、隔离性和持久性)属性。事务是数据库运行中的逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理。

我们来看一下在 JDBC 中对事务的操作处理:
public class JDBCTransactionExample {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
try {
// 加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");
// 关闭自动提交,开启事务
conn.setAutoCommit(false);
// 创建SQL语句
String sql1 = "UPDATE account SET balance = balance - ? WHERE id = ?";
String sql2 = "UPDATE account SET balance = balance + ? WHERE id = ?";
// 创建PreparedStatement对象
pstmt1 = conn.prepareStatement(sql1);
pstmt2 = conn.prepareStatement(sql2);
// 设置参数
pstmt1.setDouble(1, 1000);
pstmt1.setInt(2, 1);
pstmt2.setDouble(1, 1000);
pstmt2.setInt(2, 2);
// 执行更新操作
int count1 = pstmt1.executeUpdate();
int count2 = pstmt2.executeUpdate();
if (count1 > 0 && count2 > 0) {
System.out.println("转账成功");
// 提交事务
conn.commit();
} else {
System.out.println("转账失败");
// 回滚事务
conn.rollback();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
try {
if (conn != null) {
// 回滚事务
conn.rollback();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
try {
if (pstmt1 != null) {
pstmt1.close();
}
if (pstmt2 != null) {
pstmt2.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
上面的代码,我相信大部分的人都应该接触过,这里也就不多说了
主要我们看几个重点步骤:
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password")conn.setAutoCommit(false)conn.commit()conn.rollback()我们在日常生产项目中,项目由 Controller、Serivce、Dao 三层进行构建。

我们从上图中可以了解到:
对于 addUser 方法实际对于数据调用来说,分别调用了 insertUser()、insertLog 方法,对数据库的操作为两次
我们要保证 addUser 方法是符合事务定义的。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
<!-- 开启扫描 -->
<context:component-scan base-package="com.dpb.*"></context:component-scan>
<!-- 配置数据源 -->
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
<property name="url" value="jdbc:oracle:thin:@localhost:1521:orcl"/>
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="username" value="pms"/>
<property name="password" value="pms"/>
</bean>
<!-- 配置JdbcTemplate -->
<bean class="org.springframework.jdbc.core.JdbcTemplate" >
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
<!--
Spring中,使用XML配置事务三大步骤:
1. 创建事务管理器
2. 配置事务方法
3. 配置AOP
-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="advice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="fun*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- aop配置 -->
<aop:config>
<aop:pointcut expression="execution(* *..service.*.*(..))" id="tx"/>
<aop:advisor advice-ref="advice" pointcut-ref="tx"/>
</aop:config>
</beans>
首先必须要添加 @EnableTransactionManagement 注解,保证事务注解生效
@EnableTransactionManagement
public class AnnotationMain {
public static void main(String[] args) {
}
}
其次,在方法上添加 @Transactional 代表注解生效
@Transactional
public int insertUser(User user) {
userDao.insertUser();
userDao.insertLog();
return 1;
}
上面的操作涉及两个重点:
事务的传播属性
事务的隔离级别
本次剖析源码我们会尽量挑重点来讲,因为事务源码本身就是依靠 AOP 实现的,我们之前已经很详细的讲过 IOC 和 AOP 的源码实现了,这次带大家一起过一遍事务即可。
因为从博主本身而言,我感觉 Spring 事务其实没有那么的重要,面试也不常考,所以不会花大量的时间去剖析细节源码。
首先我们看一下这个接口的一些组成配置:
****
这里我们重点看 PlatformTransactionManager 的实现,其实现一共三个方法:
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)void commit(TransactionStatus status)void rollback(TransactionStatus status)我们分别看一下其如何实现的
我们想一下,在获取事务这一阶段,我们会做什么功能呢?
参考上述我们 JDBC 的步骤,这个阶段应该会 创建连接并且开启事务
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition){
// PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED都需要新建事务
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
//没有当前事务的话,REQUIRED,REQUIRES_NEW,NESTED挂起的是空事务,然后创建一个新事务
SuspendedResourcesHolder suspendedResources = suspend(null);
try {
// 看这里重点:开始事务
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
// 恢复挂起的事务
resume(null, suspendedResources);
throw ex;
}
}
}
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
// 是否需要新同步
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 创建新的事务
DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 【重点】开启事务和连接
doBegin(transaction, definition);
// 新同步事务的设置,针对于当前线程的设置
prepareSynchronization(status, definition);
return status;
}
protected void doBegin(Object transaction, TransactionDefinition definition) {
// 判断事务对象没有数据库连接持有器
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
// 【重点】通过数据源获取一个数据库连接对象
Connection newCon = obtainDataSource().getConnection();
// 把我们的数据库连接包装成一个ConnectionHolder对象 然后设置到我们的txObject对象中去
// 再次进来时,该 txObject 就已经有事务配置了
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// 【重点】获取连接
con = txObject.getConnectionHolder().getConnection();
// 为当前的事务设置隔离级别【数据库的隔离级别】
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
// 设置先前隔离级别
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// 设置是否只读
txObject.setReadOnly(definition.isReadOnly());
// 关闭自动提交
if (con.getAutoCommit()) {
//设置需要恢复自动提交
txObject.setMustRestoreAutoCommit(true);
// 【重点】关闭自动提交
con.setAutoCommit(false);
}
// 判断事务是否需要设置为只读事务
prepareTransactionalConnection(con, definition);
// 标记激活事务
txObject.getConnectionHolder().setTransactionActive(true);
// 设置事务超时时间
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// 绑定我们的数据源和连接到我们的同步管理器上,把数据源作为key,数据库连接作为value 设置到线程变量中
if (txObject.isNewConnectionHolder()) {
// 将当前获取到的连接绑定到当前线程
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
}
到这里,我们的 获取事务 接口完成了 数据库连接的创建 和 关闭自动提交(开启事务),将 Connection 注册到了缓存(resources)当中,便于获取。
public final void commit(TransactionStatus status) throws TransactionException {
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
// 如果在事务链中已经被标记回滚,那么不会尝试提交事务,直接回滚
if (defStatus.isLocalRollbackOnly()) {
// 不可预期的回滚
processRollback(defStatus, false);
return;
}
// 设置了全局回滚
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
// 可预期的回滚,可能会报异常
processRollback(defStatus, true);
return;
}
// 【重点】处理事务提交
processCommit(defStatus);
}
// 处理提交,先处理保存点,然后处理新事务,如果不是新事务不会真正提交,要等外层是新事务的才提交,
// 最后根据条件执行数据清除,线程的私有资源解绑,重置连接自动提交,隔离级别,是否只读,释放连接,恢复挂起事务等
private void processCommit(DefaultTransactionStatus status) throws TransactionException {;
// 如果是独立的事务则直接提交
doCommit(status);
//根据条件,完成后数据清除,和线程的私有资源解绑,重置连接自动提交,隔离级别,是否只读,释放连接,恢复挂起事务等
cleanupAfterCompletion(status);
}
这里比较重要的有两个步骤:
doCommit:提交事务(直接使用 JDBC 提交即可)
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
try {
// JDBC连接提交
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}
cleanupAfterCompletion:数据清除,与线程中的私有资源解绑,方便释放
// 线程同步状态清除
TransactionSynchronizationManager.clear();
// 清除同步状态【这些都是线程的缓存,使用ThreadLocal的】
public static void clear() {
synchronizations.remove();
currentTransactionName.remove();
currentTransactionReadOnly.remove();
currentTransactionIsolationLevel.remove();
actualTransactionActive.remove();
}
// 如果是新事务的话,进行数据清除,线程的私有资源解绑,重置连接自动提交,隔离级别,是否只读,释放连接等
doCleanupAfterCompletion(status.getTransaction());
// 此方法做清除连接相关操作,比如重置自动提交啊,只读属性啊,解绑数据源啊,释放连接啊,清除链接持有器属性
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// 将数据库连接从当前线程中解除绑定
TransactionSynchronizationManager.unbindResource(obtainDataSource());
// 释放连接
Connection con = txObject.getConnectionHolder().getConnection();
// 恢复数据库连接的自动提交属性
con.setAutoCommit(true);
// 重置数据库连接
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel(), txObject.isReadOnly());
// 如果当前事务是独立的新创建的事务则在事务完成时释放数据库连接
DataSourceUtils.releaseConnection(con, this.dataSource);
// 连接持有器属性清除
txObject.getConnectionHolder().clear();
}
这就是我们提交事务的操作了,总之来说,主要就是 调用JDBC的commit提交 和 清除一系列的线程内部数据和配置
public final void rollback(TransactionStatus status) throws TransactionException {
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
processRollback(defStatus, false);
}
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
// 回滚的擦欧洲哦
doRollback(status);
// 回滚完成后回调
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// 根据事务状态信息,完成后数据清除,和线程的私有资源解绑,重置连接自动提交,隔离级别,是否只读,释放连接,恢复挂起事务等
cleanupAfterCompletion(status);
}
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
// jdbc的回滚
con.rollback();
}
回滚事务,简单来说 调用JDBC的rollback 和 清除数据
我们来思考一下,利用 TransactionManager 重写一下我们第一步的 JDBC 的功能
@Autowired
private UserDao userDao;
@Autowired
private PlatformTransactionManager txManager;
@Autowired
private LogService logService;
@Transactional
public void insertUser(User u) {
// 1、创建事务定义
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
// 2、根据定义开启事务
TransactionStatus status = txManager.getTransaction(definition);
try {
this.userDao.insert(u);
Log log = new Log(System.currentTimeMillis() + "", System.currentTimeMillis() + "-" + u.getUserName());
// this.doAddUser(u);
this.logService.insertLog(log);
// 3、提交事务
txManager.commit(status);
} catch (Exception e) {
// 4、异常了,回滚事务
txManager.rollback(status);
throw e;
}
}
大家看到上述代码及思考一下 AOP 的作用和源码,有没有一丝丝想法
比如我现在是面试官,问你一个问题:你怎么去设计 Spring 的事务?
如果一点点想法都没有的话,也不用着急,我们来慢慢的分析
我们想,如果我们要实现事务,在每一个方法里面都需要进行以下三个步骤:
是不是显得我们的代码很臃肿,那么我们能不能把这三个步骤搞成一个通用化的东西
一些代码在方法前执行,一些代码方法后执行
这个时候,你是不是就想到了 AOP(切面编程)了
当然,Spring 中也是如此做的,利用 AOP 来对事务进行了统一的包装实现
我们知道了使用 AOP 技术实现,那到底是如何实现的呢?
我们从 @EnableTransactionManagement 注解聊起,我们点进该注解:
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
很明显,TransactionManagementConfigurationSelector 类是我们主要关注的内容
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
/**
* 此处是AdviceMode的作用,默认是用代理,另外一个是ASPECTJ
*/
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}
}
一共注册了两个:
AutoProxyRegistrar.class:注册AOP处理器
ProxyTransactionManagementConfiguration.class:代理事务配置,注册事务需要用的一些类,而且Role=ROLE_INFRASTRUCTURE都是属于内部级别的
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
// 【重点】注册了 BeanFactoryTransactionAttributeSourceAdvisor 的 advisor
// 其 advice 为 transactionInterceptor
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
}
到这里,看到 BeanFactoryTransactionAttributeSourceAdvisor 以 advisor 结尾的类,AOP 的 DNA 应该动了,其 advice 为 transactionInterceptor
到这里,我们思考一下,利用我们之前学习到的 AOP 的源码,猜测其运行逻辑:
@EnableTransactionManagement 注解,Spring 会注册一个 BeanFactoryTransactionAttributeSourceAdvisor 的类Bean 时,会和 AOP 一样,利用该 Advisor 类生成对应的代理对象我们从上面看到其 advice 正是 TransactionInterceptor,那自然不用多说
从上篇 AOP 得知,代理对象运行时,会拿到所有的 advice 并进行排序,责任链递归运行
所以,我们直接看 TransactionInterceptor 这个类即可
这里先看一下 TransactionInterceptor 类图

然后看其源码内容:
这里的 invoke 方法怎么执行到的,我就不多介绍了,看过上篇 AOP 的文章应该都懂,不熟悉的小伙伴可以去看一下
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor {
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// 获取我们的代理对象的class属性
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
/**
* 以事务的方式调用目标方法
* 在这埋了一个钩子函数 用来回调目标方法的
*/
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
}
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation){
// 获取我们的事务属性源对象
TransactionAttributeSource tas = getTransactionAttributeSource();
// 通过事务属性源对象获取到当前方法的事务属性信息
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 获取我们配置的事务管理器对象
final TransactionManager tm = determineTransactionManager(txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// 【重点】创建TransactionInfo
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
try {
// 执行被增强方法,调用具体的处理逻辑【我们实际的方法】
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 异常回滚
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
//清除事务信息,恢复线程私有的老的事务信息
cleanupTransactionInfo(txInfo);
}
//成功后提交,会进行资源储量,连接释放,恢复挂起事务等操作
commitTransactionAfterReturning(txInfo);
return retVal;
}
}
// 创建连接 + 开启事务
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// 获取TransactionStatus事务状态信息
status = tm.getTransaction(txAttr);
// 根据指定的属性与status准备一个TransactionInfo,
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
// 存在异常时回滚事务
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
// 进行回滚
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
// 调用事务管理器的提交方法
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo){
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
然后…没有然后了
事务就是这样简单、朴实无华的实现了
这里稍微讲下 xml 配置的,xml 也一样,在我们进行 xml 文件解析的时候,将 BeanFactoryTransactionAttributeSourceAdvisor 组装起来注册到 BeanDefinitionMap 中
这里可以参考上篇 AOP 的解析和生成
生成后也一样,调用的是代理方法,判断改方法有没有被代理,然后递归+责任链执行 Advice 即可
没什么困难的
大家有没有感觉事务有点水,就是用了 Spring AOP 的功能包装了一下
根本的实现还是依靠的 JDBC,但有一些不得不记的小知识点:事务的传播属性 和 事务的隔离级别
这两个关于配置的还是要去看一下,我们本篇就不再多叙述了,网上好多资料都有的
我们画一下基本的流程图

整个流程也相较于简单,有兴趣的可以去更细的看一下
记得校招时候,当时对 Spring 懵懂无知,转眼间也被迫看了源码
本文主要从 JDBC 组装事务过度到 Spring 的事务注解,最终通过 AOP 的技术去进行切面处理
通过这篇文章,我相信,99% 的人应该都可以理解了 Spring 事务 的来龙去脉
那么如何证明你真的理解了 Spring 事务呢,我这里出个经典的题目,大家可以想一下:如果让你设计Spring中事务的流程,你会如何设计?
如果你能看到这,那博主必须要给你一个大大的鼓励,谢谢你的支持!
喜欢的可以点个关注,后续会更新 Spring 循环依赖 的源码文章
我是爱敲代码的小黄,独角兽企业的Java开发工程师,CSDN博客专家,Java领域新星创作者,喜欢后端架构和中间件源码。
我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的
我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.
我有一个涉及多台机器、消息队列和事务的问题。因此,例如用户点击网页,点击将消息发送到另一台机器,该机器将付款添加到用户的帐户。每秒可能有数千次点击。事务的所有方面都应该是容错的。我以前从未遇到过这样的事情,但一些阅读表明这是一个众所周知的问题。所以我的问题。我假设安全的方法是使用两阶段提交,但协议(protocol)是阻塞的,所以我不会获得所需的性能,我是否正确?我通常写Ruby,但似乎Redis之类的数据库和Rescue、RabbitMQ等消息队列系统对我的帮助不大——即使我实现某种两阶段提交,如果Redis崩溃,数据也会丢失,因为它本质上只是内存。所有这些让我开始关注erlang和
转自:spring.profiles.active和spring.profiles.include的使用及区别说明下文笔者讲述spring.profiles.active和spring.profiles.include的区别简介说明,如下所示我们都知道,在日常开发中,开发|测试|生产环境都拥有不同的配置信息如:jdbc地址、ip、端口等此时为了避免每次都修改全部信息,我们则可以采用以上的属性处理此类异常spring.profiles.active属性例:配置文件,可使用以下方式定义application-${profile}.properties开发环境配置文件:application-dev
我已经开始使用mysql2gem。我试图弄清楚一些基本的事情——其中之一是如何明确地执行事务(对于批处理操作,比如多个INSERT/UPDATE查询)。在旧的ruby-mysql中,这是我的方法:client=Mysql.real_connect(...)inserts=["INSERTINTO...","UPDATE..WHEREid=..",#etc]client.autocommit(false)inserts.eachdo|ins|beginclient.query(ins)rescue#handleerrorsorabortentirelyendendclient.commi
保存成功后可以回滚吗?让我有一个带有属性名称、电子邮件等的用户模型。例如u=User.newu.name="test_name"u.email="test@email.com"u.save现在记录将成功保存在数据库中,之后我想回滚我的事务(不是销毁或删除)。有什么想法吗? 最佳答案 您可以通过交易来做到这一点,请参阅http://markdaggett.com/blog/2011/12/01/transactions-in-rails/例子:User.transactiondoUser.create(:username=>'Nemu
我无法运行Spring。这是错误日志。myid-no-MacBook-Pro:myid$spring/Users/myid/.rbenv/versions/1.9.3-p484/lib/ruby/gems/1.9.1/gems/spring-0.0.10/lib/spring/sid.rb:17:in`fiddle_func':uninitializedconstantSpring::SID::DL(NameError)from/Users/myid/.rbenv/versions/1.9.3-p484/lib/ruby/gems/1.9.1/gems/spring-0.0.10/li
目录0专栏介绍1平面2R机器人概述2运动学建模2.1正运动学模型2.2逆运动学模型2.3机器人运动学仿真3动力学建模3.1计算动能3.2势能计算与动力学方程3.3动力学仿真0专栏介绍?附C++/Python/Matlab全套代码?课程设计、毕业设计、创新竞赛必备!详细介绍全局规划(图搜索、采样法、智能算法等);局部规划(DWA、APF等);曲线优化(贝塞尔曲线、B样条曲线等)。?详情:图解自动驾驶中的运动规划(MotionPlanning),附几十种规划算法1平面2R机器人概述如图1所示为本文的研究本体——平面2R机器人。对参数进行如下定义:机器人广义坐标
网站的日志分析,是seo优化不可忽视的一门功课,但网站越大,每天产生的日志就越大,大站一天都可以产生几个G的网站日志,如果光靠肉眼去分析,那可能看到猴年马月都看不完,因此借助网站日志分析工具去分析网站日志,那将会使网站日志分析工作变得更简单。下面推荐两款网站日志分析软件。第一款:逆火网站日志分析器逆火网站日志分析器是一款功能全面的网站服务器日志分析软件。通过分析网站的日志文件,不仅能够精准的知道网站的访问量、网站的访问来源,网站的广告点击,访客的地区统计,搜索引擎关键字查询等,还能够一次性分析多个网站的日志文件,让你轻松管理网站。逆火网站日志分析器下载地址:https://pan.baidu.
一、机器人介绍 此处是基于MATLABRVC工具箱,对ABB-IRB-1200型号的微型机械臂进行正逆向运动学分析,并利Simulink工具实现对机械臂进行具有动力学参数的末端轨迹规划仿真,最后根据机械模型设计Simulink-Adams联合仿真。 图1.ABBIRB 1200尺寸参数示意图ABBIRB 1200提供的两种型号广泛适用于各作业,且两者间零部件通用,两种型号的工作范围分别为700 mm 和 900 mm,大有效负载分别为 7 kg 和5 kg。 IRB 1200 能够在狭小空间内能发挥其工作范围与性能优势,具有全新的设计、小型化的体积、高效的性能、易于集成、便捷的接