前言
上篇文章介绍了 Spring AOP 的实现原理,掌握了 AOP,再理解 Spring 事务会简单很多,值得注意的是,提到事务,我们通常说的是关系型数据库的事务,Spring 只是一个 Java 开源框架,它不存在事务的说法,Spring 是通过一系列代码逻辑来管理数据库事务,实现业务场景中事务的嵌套处理。本篇文章先介绍 Spring 事务管理涉及的相关类
本篇文章使用的 SpringBoot 版本是 3.4.1 ,对应 Spring 版本 6.2.1。
SpringBoot & Spring 架构图示概览
这里我以 SpringBoot 源码入口为起点,画了一个相关的流程图,包含了 SpringBoot、Spring 事务、Spring AOP、Spring 事件、BeanFactoryPostProcessor、BeanPostProcessor 等所有 Spring 知识,以及相关模块之间的交互联系,后续也会持续更新此图(因为我自己还没有学完),我试了下作者侧这边更新后,分享的协作链接也会实时变更,希望对大家有帮助
SpringBoot & Spring 架构图 持续更新 对于即将需要面试的同学应该会比较有帮助!
基本用法
注解方式
直接在方法上标注 @Transactional 即可,这样 test1() 方法就会以存在事务的方式运行,抛出相关异常会回滚事务
java
@Transactional
public void test1(){}
@Transactional 内部有很多属性用来控制事务的行为
java
public @interface Transactional {
//指定事务管理器
@AliasFor("value")
String transactionManager() default "";
//传播行为
Propagation propagation() default Propagation.REQUIRED;
//指定哪些异常要回滚
Class<? extends Throwable>[] rollbackFor() default {};
//指定超时时间
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
//...
}
包括选择事务管理器、指定传播行为、要回滚的异常、超时时间等等。
编程方式 TransactionTemplate
TransactionTemplate 是 spring-tx 模块给我们提供的一个操作事务的类,针对有返回值和无返回值提供了两种 API,在 SpringBoot 中提供了自动配置,直接使用即可
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnSingleCandidate(PlatformTransactionManager.class)
public static class TransactionTemplateConfiguration {
@Bean
@ConditionalOnMissingBean(TransactionOperations.class)
public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
return new TransactionTemplate(transactionManager);
}
}
- 无返回值
java
@Autowired
private TransactionTemplate transactionTemplate;
public void test2(){
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
//业务行为
} catch (Exception e){
status.setRollbackOnly();
throw new RuntimeException("创建订单失败", e);
}
}
});
}
- 有返回值
当业务操作需要结果时可以使用
java
public void test3(){
Object obj = transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
try {
//业务操作
return null;
} catch (Exception e){
// 标记事务为回滚
status.setRollbackOnly();
throw new RuntimeException("创建用户失败,事务已回滚", e);
}
}
});
}
编程式事务可以和声明式事务结合使用,TransactionTemplate 的默认传播行为是 REQUIRED,这意味着没有外层声明式事务的影响下,每一次 transactionTemplate.execute() 都是一个新事物,使用编程式事务让我们可以 更灵活的控制事务粒度。
事务的传播行为
Spring 事务管理提供了以下几种传播行为,每一种传播行为会对当前事务的嵌套有不同的处理逻辑。
| 传播行为 Propagation | 翻译 | 如果当前有事务 | 如果当前无事务 | 异常回滚影响 | 典型应用场景 |
|---|---|---|---|---|---|
| REQUIRED | 必需(默认) | 加入当前事务 | 创建新事务 | 全部回滚 | 大多数业务方法 |
| SUPPORTS | 支持 | 加入当前事务 | 非事务运行 | 看情况 | 查询方法,可事务可非事务 |
| MANDATORY | 强制 | 加入当前事务 | 抛出异常 | 全部回滚 | 必须被事务方法调用 |
| REQUIRES_NEW | 新建事务 | 挂起当前事务,创建新事务 | 创建新事务 | 各自独立回滚 | 独立业务,如日志记录 |
| NOT_SUPPORTED | 不支持 | 挂起当前事务,非事务运行 | 非事务运行 | 不影响事务 | 非核心业务,避免事务锁 |
| NEVER | 从不 | 抛出异常 | 非事务运行 | 不适用 | 强制非事务环境 |
| NESTED | 嵌套 | 创建嵌套子事务(保存点) | 创建新事务 | 子事务回滚不影响主事务 | 部分失败不影响整体的场景 |
Spring 注解事务的核心就是对不同的传播行为或者说对事务嵌套进行处理,因为关系型数据库 MySQL 是不支持事务嵌套的,假如我们在 MySQL 客户端开启事务之后,再次执行 Begin,那么它会先提交当前事务然后重新开启新事务
sql
select * from Cash_repay_apply
BEGIN;
update Cash_repay_apply set repay_status = 'TEST' where id = 1
BEGIN; -- 会先提交事务,再开启新事务
update Cash_repay_apply set repay_status = 'TEST' where id = 2
COMMIT;
上述代码无法嵌套 BEGIN,执行第二个 BEGIN 时,会先提交上一次 BEGIN 的事务。
事务管理器
事务管理器 TransactionManager 是 Spring 操作数据库事务的核心顶层接口,它的子接口 PlatformTransactionManager 定义了三个核心方法
java
public interface PlatformTransactionManager extends TransactionManager {
/**
* 获取事务
* */
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
/**
* 提交事务
* */
void commit(TransactionStatus status) throws TransactionException;
/**
* 回滚事务
* */
void rollback(TransactionStatus status) throws TransactionException;
}
无论应用程序玩的有多花哨,最终无非是对数据库事务进行操作,那么数据库事务其实就三个点,开启事务、提交、回滚。所以事务管理器提供了三个方法来对应
它把事务定义封装到 TransactionDefinition 中,根据它获取一个数据库事务对象,事务结果信息封装到 TransactionStatus 对象中,然后提供了提交事务和回滚事务两个方法由具体子类实现。在 SpringBoot 项目中通常我们会引入 spring-boot-starter-jdbc
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
引入之后会启用 JdbcTransactionManagerConfiguration,这时候具体的实现就是 JdbcTransactionManager 。不过有意思的是具体的实现在抽象事物管理器 AbstractPlatformTransactionManager 中,因为这一套开启事务、提交、回滚的操作其实都是通用的,无非就是用 jdbc 驱动的 Connection 对象来和 MySQL 通信,执行客户端命令罢了
回想我们之前学习的 jdbc 知识,事务的操作一定离不开 jdbc 规范,我们跟踪源码深入会发现,事务的获取、提交、回滚的确都是通过 Connection 对象来操作的
java
public interface Connection extends Wrapper, AutoCloseable {
void commit() throws SQLException;
void rollback() throws SQLException;
}
SpringBoot 默认数据库连接池是 HikariCP 。它的 Connection 实现类是 HikariProxyConnection,不过实际上它几乎啥也没干,包装了一层,最后还是调用 jdbc 驱动的 Connection 实现类 ConnectionImpl 。
JdbcTransactionManager是DataSourceTransactionManager的子类,一个事务管理器对应一个数据源,Spring对事务的管理,本质上是管理数据库连接Connection,事务最终是用Connection来开启和提交的·
TransactionDefinition
从这个类的名字可以看出,它是事务的定义抽象,结合事务管理器 PlatformTransactionManager.getTransaction(TransactionDefinition definition) 的入参我们可以知道 TransactionDefinition 定义了事务的相关属性,根据它的定义通过事务管理器获取一个数据库事务。
在设计上源码中抽象出了几个继承关系如下图 。使用声明式事务时候最终实现类是 RuleBasedTransactionAttribute 公共属性的定义基本上还是在 DefaultTransactionDefinition 中

TransactionStatus
TransactionStatus 描述了事务开启后运行过程中状态。默认实现类是 DefaultTransactionStatus,结合事务管理器的 commit(TransactionStatus status) 和 rollback(TransactionStatus status) 的方法入参可以看出,TransactionStatus 封装了最终的事务结果(提交/回滚),提交到数据库中。
java
public class DefaultTransactionStatus extends AbstractTransactionStatus {
private final String transactionName; // 事务名称
private final Object transaction; // 底层事务对象
private final boolean newTransaction; // 是否是新事务(通常可以理解为是否是当前事务方法自己创建的事务)
private final boolean newSynchronization; // 是否新的事务同步(同上)
private final boolean nested; // 是否是嵌套事务
private final boolean readOnly; // 是否只读
private final Object suspendedResources; // 当前事务方法挂起的事务资源
}
了解了 PlatformTransactionManager、TransactionDefinition、TransactionStatus, 结合 PlatformTransactionManager 的三个方法
java
public interface PlatformTransactionManager extends TransactionManager {
/**
* 获取事务
* */
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
/**
* 提交事务
* */
void commit(TransactionStatus status) throws TransactionException;
/**
* 回滚事务
* */
void rollback(TransactionStatus status) throws TransactionException;
}
前面我们说 Spring 对事务的管理,本质上是对 Connection 进行管理,就是调用这三个方法来管理 Connection 对象。
思考除了
TransactionTemplate我们是否可以使用 PlatformTransactionManager 来实现编程式事务?
TransactionStatus 和 TransactionDefinition 的区别
下面我们用表格的方式对比一下这两个类·
| 维度 | TransactionDefinition | TransactionStatus |
|---|---|---|
| 角色 | 事务的定义(配置时) | 事务的状态(运行时) |
| 生命周期 | 事务 开始前 定义,全局共享 | 事务 开始后 创建,事务结束销毁 |
| 可变性 | 事务获取后 不可变(配置固定) | 可变(状态会随业务代码变化) |
| 存储内容 | 事务的配置属性 | 当前事务的运行状态 |
| 创建时机 | 在获取事务前由调用者提供 | 事务开始时由事务管理器创建 |
Spring 事务管理图示
这里我们举几个常见的事务场景
普通事务
java
@Transactional
public void test(){
//...db 操作
}
例如这段简单的代码,事务的开启流程图如下

其实就是代理方法用 AOP 实现了一个环绕通知的效果,在目标方法执行前开启事务,在目标方法执行后提交或者回滚事务,我们暂时可以不关注开启事务和回滚事务内部还有哪些细节,后面会详细介绍。
嵌套事务
以下面这段嵌套事务的代码为例
java
@Transactional
public void test(){
//db操作
test2Service.testNewTx();
}
@Service
public class Test2Service{
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testNewTx(){
//db 操作...
}
}
这段示例代码是一个已存在的事务中,嵌套了一个需要新开事务的业务方法,图示如下

这里代码示例中,会先开启 test() 方法的事务,然后执行到 testNewTx() 时,发现当前方法传播行为是新开事务,于是会再开启一个事务,等内层方法 testNewTx() 结束后,提交/回滚它的事务,然后再继续进行外层 test() 方法的事务,从这个案例不难想象,无论多少层事务嵌套,都是一样的处理逻辑,无非是嵌套层数的多少罢了,后面会详细介绍源码,实现相对比较简单
总结
Spring 事务管理是通过代理目标对象,执行代理方法,在原方法执行前开启事务、原方法执行后提交/回滚事务,当遇到嵌套事务时逻辑亦然
TransactionInterceptor
上一篇 AOP 实现原理,我们知道执行代理对象方法会调用一系列拦截器实现拦截。上一段我们说了 Spring 管理事务实际上就是对目标对象代理,实现了一个环绕通知的效果,而 TransactionInterceptor 就是实现这个环绕通知包装的拦截器。
java
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
//......
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
//以事务方式调用
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
}
观察源码可以发现 invokeWithinTransaction() 是父类 TransactionAspectSupport 实现的。具体实现逻辑后面会介绍
TransactionSynchronizationManager
TransactionSynchronizationManager 是事务管理过程中一个非常核心的类,它的作用是保存事务管理过程中事务的相关信息,绑定到当前线程,包括 事务资源、同步器、隔离级别、事务激活状态 等
java
public abstract class TransactionSynchronizationManager {
//保存当前线程的事务资源,key 通常是数据源,value 通常是 Connection 包装对象
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");
//保存当前事务的回调方法
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal<>("Transaction synchronizations");
//保存当前线程事务的名字
private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal<>("Current transaction name");
//当前线程事务是不是只读
private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal<>("Current transaction read-only status");
//当前线程事务隔离级别,通常开发中应该很少手动设置隔离级别
private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal<>("Current transaction isolation level");
//当前线程事务是不是激活的
private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal<>("Actual transaction active");
}
我们查看这个类的成员变量, 提供了六个 ThreadLocal 存储事务相关信息。其核心方法如下
java
//绑定资源(把修改后的 Connection 对象绑定到当前线程)
public static void bindResource(Object key, Object value){}
//解绑资源(把修改后的 Connection 对象从当前线程解绑)
public static Object unbindResource(Object key){}
//注册同步器(注册事务阶段回调代码,下一段会介绍 TransactionSynchronization)
public static void registerSynchronization(TransactionSynchronization synchronization){}
//获取同步器
public static List<TransactionSynchronization> getSynchronizations(){}
思考一个问题,为什么作者不用一个对象来封装这六个
ThreadLocal?它们都是当前线程绑定的事务相关的信息,完全可以抽象出一个对象?
TransactionSynchronization
介绍
查看注释,TransactionSynchronization 是事务同步回调接口,在 Spring 管理事务的过程中,事务管理进行到不同的阶段会回调不同的方法
java
public interface TransactionSynchronization extends Ordered, Flushable {
/**
* 挂起事务时回调
*/
default void suspend() {}
/**
* 恢复事务时回调
*/
default void resume() {}
/**
* Flush 的时候回调,例如 Hibernate/JPA 的 flush 操作.
*/
@Override
default void flush() {}
/**
* 创建新保存点后调用
*/
default void savepoint(Object savepoint) {}
/**
* 回滚到上一个保存点前调用
*/
default void savepointRollback(Object savepoint) {}
/**
* 事务提交前调用
*/
default void beforeCommit(boolean readOnly) {}
/**
* 事务完成(回滚/提交)前调用
*/
default void beforeCompletion() {}
/**
* 事务提交后调用
*/
default void afterCommit() {}
/**
* 事务完成(提交、回滚)后调用
*/
default void afterCompletion(int status) {}
}
上一段我们介绍了 TransactionSynchronizationManager 中有一个 registerSynchronization() 方法可以注册事务同步回调接口,它是绑定到当前线程的,不需要交给 Spring 管理,例如
java
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void beforeCommit(boolean readOnly) {
System.out.println("TransactionSynchronization beforeCommit");
}
//...
});
事务保存点 savepoint
值得一提的是 savepoint ,叫做保存点,是关系型数据库中支持部分回滚的一个命令
sql
BEGIN;
update 语句1
update 语句2
SAVEPOINT test1;
update 语句3
update 语句4
ROLLBACK TO SAVEPOINT test1;
-- ......
使用 ROLLBACK TO SAVEPOINT 可以指定事务回滚到哪一个节点位置。在 Spring 项目中可以使用如下的方式来设置保存点
java
@Transactional
public void test1(){
CashRepayApply apply = cashRepayApplyMapper.getForUpdate(1L);
apply.setRepayStatus("test");;
cashRepayApplyMapper.updateById(apply);
//创建保存点
Object sp1 = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
apply.setRepayStatus("test2");
cashRepayApplyMapper.updateById(apply);
try {
if(true){
throw new RuntimeException("<UNK>");
}
} catch (Exception e){
//回滚到保存点
TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(sp1);
}
}
TransactionExecutionListener
介绍
这个类是事务执行监听器,它是 Spring 6.1 版本新增的功能,它的功能和 TransactionSynchronization 有些类似,也是在事务的不同阶段执行一些回调方法
java
public interface TransactionExecutionListener {
/**
* 事务开始前执行
*/
default void beforeBegin(TransactionExecution transaction) {}
/**
* 事务开始后执行
*/
default void afterBegin(TransactionExecution transaction, @Nullable Throwable beginFailure) {}
/**
* 事务提交前执行
*/
default void beforeCommit(TransactionExecution transaction) {}
/**
* 事务提交后执行
*/
default void afterCommit(TransactionExecution transaction, @Nullable Throwable commitFailure) {}
/**
* 事务回滚前执行
*/
default void beforeRollback(TransactionExecution transaction) {}
/**
* 事务回滚后执行
*/
default void afterRollback(TransactionExecution transaction, @Nullable Throwable rollbackFailure) {}
}
值得注意的是 TransactionExecutionListener 是事务管理器 AbstractPlatformTransactionManager 中的一个成员变量,
java
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager {
private Collection<TransactionExecutionListener> transactionExecutionListeners = new ArrayList<>();
//......
前面介绍过,事务的三个重要操作,开启、提交、回滚都是事务管理器来实现的,在这些操作的过程中,不同的阶段会遍历调用 TransactionExecutionListener 的不同方法。
自动配置来源
和 TransactionSynchronization 最重要的一个区别是 TransactionSynchronization 是绑定到某一个事务(线程)上的回调,而 TransactionExecutionListener 是针对所有事务的回调。结合 TransactionExecutionListener 是抽象事务管理器的成员变量来思考,所以很明显 TransactionExecutionListener 是交给 Spring 管理才会被赋值到抽象事务管理器中生效的
在 SpringBoot 中, TransactionManagerCustomizationAutoConfiguration 自动配置类中
java
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfiguration(before = TransactionAutoConfiguration.class)
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionManagerCustomizationAutoConfiguration {
/*
* 从容器中获取 TransactionManagerCustomizer 的 Bean 列表,得到 TransactionManagerCustomizers
*/
@Bean
@ConditionalOnMissingBean
TransactionManagerCustomizers platformTransactionManagerCustomizers(
ObjectProvider<TransactionManagerCustomizer<?>> customizers) {
return TransactionManagerCustomizers.of(customizers.orderedStream().toList());
}
/*
* 从容器中获取 TransactionExecutionListener 的 Bean 列表,得到 ExecutionListenersTransactionManagerCustomizer
*/
@Bean
ExecutionListenersTransactionManagerCustomizer transactionExecutionListeners(
ObjectProvider<TransactionExecutionListener> listeners) {
return new ExecutionListenersTransactionManagerCustomizer(listeners.orderedStream().toList());
}
}
前面的文章介绍过 SpringBoot 源码中,XxxCustomizer 都是一些用户自定义扩展的实现抽象,TransactionManagerCustomizer 很明显是自定义事务管理器配置的,然后我们再看 JdbcTransactionManagerConfiguration
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnSingleCandidate(DataSource.class)
static class JdbcTransactionManagerConfiguration {
/*
* 从容器中获取 TransactionManagerCustomizers 列表应用到 DataSourceTransactionManager 中
*/
@Bean
@ConditionalOnMissingBean(TransactionManager.class)
DataSourceTransactionManager transactionManager(Environment environment, DataSource dataSource,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
DataSourceTransactionManager transactionManager = createTransactionManager(environment, dataSource);
transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
return transactionManager;
}
/*
* 具体的事务管理器
*/
private DataSourceTransactionManager createTransactionManager(Environment environment, DataSource dataSource) {
return environment.getProperty("spring.dao.exceptiontranslation.enabled", Boolean.class, Boolean.TRUE)
? new JdbcTransactionManager(dataSource) : new DataSourceTransactionManager(dataSource);
}
}
我们看在构造事务管理器交给 Spring 的时候会执行 TransactionManagerCustomizer#customize 方法,我们再观察 ExecutionListenersTransactionManagerCustomizer 的实现方法
java
class ExecutionListenersTransactionManagerCustomizer
implements TransactionManagerCustomizer<ConfigurableTransactionManager> {
private final List<TransactionExecutionListener> listeners;
ExecutionListenersTransactionManagerCustomizer(List<TransactionExecutionListener> listeners) {
this.listeners = listeners;
}
@Override
public void customize(ConfigurableTransactionManager transactionManager) {
//向事务管理器中添加 TransactionExecutionListener
this.listeners.forEach(transactionManager::addListener);
}
}
这下就很清晰了,只要我们自定义一个 TransactionExecutionListener 交给 Spring 管理即可被事务管理器应用
java
@Bean
public TransactionExecutionListener transactionExecutionListener() {
return new TransactionExecutionListener() {
@Override
public void beforeBegin(TransactionExecution transaction) {
System.out.println("beforeBegin");
}
//......
};
}
事务监听器和同步器对比
两者最重要的区别是 TransactionExecutionListener 作用的是所有事务,而 TransactionSynchronization 作用的是具体某个绑定到当前线程的事务,下面我们用一张表格对比两者区别
| 对比维度 | TransactionExecutionListener | TransactionSynchronization |
|---|---|---|
| 作用范围 | 全局级别:作用于事务管理器下的所有事务 | 线程级别:作用于当前线程绑定的事务 |
| 注册方式 | Spring 启动时通过事务管理器或 Bean 配置 | 运行时动态注册(通过TransactionSynchronizationManager.registerSynchronization() |
| 事务感知 | 可以获取TransactionExecution对象,了解事务全局信息 |
只能感知当前事务,无法获取事务详细信息 |
| 调用次数 | 每个事务生命周期都会触发 | 只有显式注册了的事务才触发 |
| 依赖关系 | 独立于特定线程,不依赖线程绑定 | 依赖当前线程的事务绑定状态 |
| 适用场景 | - 全局监控和统计 - 全局限流 - 审计日志 - 性能分析 | - 业务回调(发消息、事件) - 清理线程本地缓存 - 特定业务的补偿操作 - 事务完成后的资源释放 |
| 生命周期方法 | - afterBegin - beforeCommit - afterCommit - beforeRollback - afterRollback - afterCompletion - beforeSuspend - afterSuspend - beforeResume - afterResume |
- suspend - resume - flush - beforeCommit - beforeCompletion - afterCommit - afterCompletion |
| 执行顺序 | 相同阶段比同步器后执行 | 相同阶段比监听器先执行 |
| 异常处理 | 监听器中的异常会影响事务(可配置) | 同步中的异常通常不会影响事务 |
| 资源管理 | 由 Spring 容器管理生命周期 | 由当前事务管理,事务结束后自动清理 |
| 事务挂起 | 可以感知事务的挂起和恢复事件 | 可以实现挂起和恢复的回调 |
从两者的方法作用可以看出,
TransactionExecutionListener的方法涉及事务提交/回滚前的操作,使用它的beforeCommit()、afterCommit()等方法如果出现异常,会导致事务的回滚,所以需要注意在这些方法的使用中,如果不想让监听器的逻辑代码影响事务,一定要捕捉异常
结语
由于篇幅问题,本篇文章先介绍 Spring 事务管理的相关重要的类和接口,下一篇文章我们具体介绍声明式事务实现的源代码,以及对比 TransactionTemplate 实现编程式事务,使用事务管理器实现另一种编程式事务管理。