記事ディレクトリ
序文
ビジネスを完了するための AOP とビジネス ロジックの連携を表面的ではなく深く理解します。記事の内容は少し長く、解体すると前後の論理的一貫性に影響を与える可能性があります。
基本操作
データベース実行トランザクションの手順は次のとおりです。
- オープントランザクション開始 SQL commit autocommit = on
- SQL
- 専念
- ロールバック
mysql のトランザクションはbegin
開始およびcommit
終了し、トランザクション内のエラーはロールバックされます。通常は有効になっておりautocommit=on
、各 SQL がコミットされます。
確認
- 1 つのメソッドが 2 つの挿入を呼び出します。2 つの挿入は別個の接続オブジェクトであるか、同じオブジェクトである必要がありますが、接続では autocommit=true になっています。
public Object sw() {
Demo demo = new Demo();
demo.setDbName("sw-ces1");
demoMapper.insert(demo);
demo = new Demo();
demo.setDbName("sw-ces2");
demoMapper.insert(demo);
return null;
}
このメソッドは、次に示すように、insert を 2 回呼び出し、2 つの接続インスタンス オブジェクトを生成しconnection
、auto commit=true を生成します。
方法定位:org.mybatis.spring.SqlSessionTemplate#insert(java.lang.String, java.lang.Object)
- Spring トランザクションでは、一部のデータベース操作が同じトランザクションで完了するように、同じ接続オブジェクトである必要があります。
次に、次のように注釈を付けます。@Transactional
@Transactional
public Object sw() {
Demo demo = new Demo();
demo.setDbName("sw-ces1");
demoMapper.insert(demo);
demo = new Demo();
demo.setDbName("sw-ces2");
demoMapper.insert(demo);
return null;
}
接続オブジェクトを再度確認すると、2 つの挿入の接続オブジェクトが同じであり、autocommit=false であることがわかります。
org.mybatis.spring.SqlSessionTemplate#insert(java.lang.String, java.lang.Object)
ここで、Spring のトランザクションの基本的な概要が明確になり、Spring による接続の管理を使用してトランザクション制御を実現します。
Springトランザクションの伝播メカニズム
Spring の管理は Connect の管理によって確かに実現されていますが、これは私たちの一方的な理解にすぎません。Spring の機能は想像以上に多くのことができます。以下は Spring の公式の機能説明です: データ アクセス ( spring.io )
- Java Transaction API (JTA)、JDBC、Hibernate、Java Persistence API (JPA) などのさまざまなトランザクション API にわたる一貫したプログラミング モデル。
- 宣言型トランザクション管理のサポート。
- JTA などの複雑なトランザクション API よりも、プログラムによるトランザクション管理のためのシンプルな API 。
- Spring のデータ アクセス抽象化と完全に統合されます。
Spring では、7 つの型が列挙型として定義されています。
-
REQUIRED (デフォルト): トランザクション アノテーションを持つメソッドとメソッド内で呼び出されるメソッドによって使用される接続が同じであり、同じトランザクション内にあることを確認します。
-
REQUIRES_NEW: このタイプが存在する場合、Spring は接続を再作成し、新しいトランザクションを開始します。
-
NESTED: ネストされた伝播メカニズム。外部トランザクションがある場合、トランザクションはネストされますが、トランザクションも外部トランザクションのスコープ内にあります。
-
サポート: 外層にトランザクションがない場合、トランザクションは追加されませんが、内部トランザクションは有効になります。
-
必須: 現在のトランザクションを使用します。現在のトランザクションが存在しない場合は、例外をスローします。
-
NOT_SUPPORTED: メソッド呼び出しチェーン中に
NOT_SUPPORTED
トランザクション中断が発生しても、トランザクション中断は有効になりません。次の呼び出しチェーンでトランザクション中断が発生すると、トランザクション中断REQUIRED
は再び有効になります。 -
NEVER: 非トランザクション モード。メソッド内に開いているトランザクションがある場合、例外がスローされます。
@Transactional
使用方法は、アノテーションの標準タイプと同じくらい簡単です。
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
特別なメカニズムの説明
使用した場合の効果はここで説明しますが、後からソースコードを見るとわかりやすいです。
サポートされていません
メソッド呼び出しチェーン中にNOT_SUPPORTED
トランザクション中断が発生してもトランザクション中断は有効になりませんが、次の呼び出しチェーンでトランザクション中断が発生するとREQUIRED
再び有効になります。
/**
* 外层 事务 生效
*/
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void out(){
Demo demo = new Demo();
demo.setName("NOT_SUPPORTED外层事务");
demoMapper.insert(demo);
// 调用标记NOT_SUPPORTED了的方法
applicationContext.getBean(DemoService.class).in();
}
// 中间层 NOT_SUPPORTED 没有事务
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NOT_SUPPORTED)
public void in() {
Demo demo = new Demo();
demo.setName("NOT_SUPPORTED内层事务");
demoMapper.insert(demo);
// 调用开启事务的方法
// applicationContext.getBean(DemoService.class).in2();
applicationContext.getBean(DemoService.class).in22();
}
// 下层 事务 生效
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void in2() {
Demo demo = new Demo();
demo.setName("NOT_SUPPORTED内层 内层 事务");
demoMapper.insert(demo);
int i = 1/0;
}
// 下层 事务 不生效
public void in22() {
Demo demo = new Demo();
demo.setName("NOT_SUPPORTED内层 内层 事务2");
demoMapper.insert(demo);
int i = 1/0;
}
結果は以下の2つです。
入れ子になった
ネストされた伝播メカニズム。外部トランザクションがある場合、トランザクションはネストされますが、トランザクションは外部トランザクションのスコープ内にもあります。
NESTED
トランザクションは個別にロールバック可能NESTED
トランザクションメソッドの例外は外層で食われ、NESTED
ゲームアーカイブなど外層のトランザクションは互いに独立している- 外側の層が異常な場合、内側の
NESTED
トランザクションもロールバックされます
/**
外层 事务
*/
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void out2() {
Demo demo = new Demo();
demo.setName("NESTED外层事务");
demoMapper.insert(demo);
try {
applicationContext.getBean(DemoService.class).in3();
} catch (Exception e) {
System.out.println("NESTED方法异常");
}
// 如果,这里报异常,内部的`NESTED`事务也会回滚
// int i = 1/0;
}
/**
中间层 NESTED 事务
* 1. NESTED 可以独立回滚;如果出现异常,那么这个方法内的所有数据回滚
* 2. 如果这里是REQUIRED,那么包括外层都会回滚
*/
@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
public void in3() {
Demo demo = new Demo();
demo.setName("NESTED内层事务");
demoMapper.insert(demo);
applicationContext.getBean(DemoService.class).in4();
}
// 下层 事务
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void in4() {
Demo demo = new Demo();
demo.setName("NESTED内层 内层 事务");
demoMapper.insert(demo);
int i = 1/0;
}
サポート
SUPPORT
外層にトランザクションがない場合、トランザクションは追加されませんが、次のように内部トランザクションが有効になります。
// 外层 没事务
public void out3() {
Demo demo = new Demo();
demo.setName("SUPPORTS外层事务");
demoMapper.insert(demo);
applicationContext.getBean(DemoService.class).in6();
}
// 中间层 SUPPORTS没事务
@Transactional(rollbackFor = Exception.class,propagation = Propagation.SUPPORTS)
public void in6() {
Demo demo = new Demo();
demo.setName("SUPPORTS内层事务");
demoMapper.insert(demo);
applicationContext.getBean(DemoService.class).in4();
}
// 下层 开启事务,并且生效
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void in4() {
Demo demo = new Demo();
demo.setName("NESTED内层 内层 事务");
demoMapper.insert(demo);
int i = 1/0;
}
ソースコード
ロードトランザクション自動構成クラス
Spring の多くのコンポーネントは自動構成クラスを通じてロードされます。Spring 独自の場合は、spring-boot-autoconfigure
次の構成クラスでspring.factories
次の構成クラスを見つけることができます。
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
注釈を追加しますか: @EnableTransactionManagement
@SpringBootApplication
実際には、これを使用する必要はありません。その中にあるものは@EnableAutoConfiguration
取得されますspring.factories
。その中にクラスがありますTransactionAutoConfiguration
。その一部を見てみましょう:
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnBean({
TransactionManager.class})
@ConditionalOnMissingBean({
AbstractTransactionManagementConfiguration.class})
public static class EnableTransactionManagementConfiguration {
public EnableTransactionManagementConfiguration() {
}
@Configuration(
proxyBeanMethods = false
)
// 启动事务注解
@EnableTransactionManagement(
proxyTargetClass = true
)
@ConditionalOnProperty(
prefix = "spring.aop",
name = {
"proxy-target-class"},
havingValue = "true",
matchIfMissing = true
)
public static class CglibAutoProxyConfiguration {
public CglibAutoProxyConfiguration() {
}
}
@Configuration(
proxyBeanMethods = false
)
@EnableTransactionManagement(
proxyTargetClass = false
)
@ConditionalOnProperty(
prefix = "spring.aop",
name = {
"proxy-target-class"},
havingValue = "false",
matchIfMissing = false
)
public static class JdkDynamicAutoProxyConfiguration {
public JdkDynamicAutoProxyConfiguration() {
}
}
}
上記のように、構成クラスを挿入してEnableTransactionManagementConfiguration
から注釈をスキャンする@EnableTransactionManagement
ため、注釈を追加する必要はありません。
構成クラスの説明
TransactionAutoConfiguration
次の注釈が構成クラスのヘッダーに追加されます。
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({
PlatformTransactionManager.class})
@AutoConfigureAfter({
JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class})
@EnableConfigurationProperties({
TransactionProperties.class})
@Configuration
: Spring によって構成クラスのロゴとして認識される、ここで考えてみましょう。Spring によって構成クラスとして認識されるロゴにはどのようなアノテーションがありますか?
@ConditionalOnClass({PlatformTransactionManager.class})
: これは、PlatformTransactionManager
クラスが存在する場合にのみ設定クラスをロードするためのものです。
@AutoConfigureAfter({JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class})
:
Spring がこれらの設定クラスをロードした後にこの設定クラスがロードされ、spring-data-jdbc を導入すると、この設定クラスが存在し、同時に jta、jpa、jdbc、neo4j をサポートしていることがわかりますDataSourceTransactionManagerAutoConfiguration
。
@EnableConfigurationProperties({TransactionProperties.class})
spring.transaction.defaultTimeout/rollbackOnCommitFailure
: トランザクション設定パラメータ、属性を定義できます
そこで重要なのはEnableTransactionManagement
;
EnableTransactionManagement は何をするのか
このアノテーションには 2 つの重要なプロパティがあります。
proxyTargetClass : デフォルト値: false; true: CGLIB プロキシが使用できることを示し、インターフェースがある場合は、jdk 動的プロキシが引き続き使用され、false: JDK 動的プロキシが使用されます。
mode : デフォルト値: PROXY
;PROXY
値が の場合、JDK ダイナミック プロキシを使用することを意味し、アスペクト プロキシにASPECTJ
Spring を使用することを意味します;同じクラスへのローカル呼び出しはインターセプトできないことに@Aspect
注意してください。PROXY
を使用する必要がありますASPECTJ
AutoProxyRegistrar の機能
AutoProxyRegistrar
beanNameをbeanDefinitionとして継承したinternalAutoProxyCreator
クラスとして登録する機能です。InfrastructureAdvisorAutoProxyCreator
AbstractAdvisorAutoProxyCreator
AbstractAdvisorAutoProxyCreator: このクラスは AOP の章で説明されており、AOP プロキシ自動作成クラスであり、主に次の操作を実行します。
- まず、Bean が初期化された後、その
postProcessAfterInitialization
メソッドがプロキシ クラス処理のために呼び出されます。 - Bean をプロキシする必要があるかどうかを判断する
- プロキシオブジェクトを作成します(ここで作成したプロキシはオブジェクトに応じて判断されます)
AOP プロキシとトランザクション プロキシはどちらも同じものを実装しAbstractAdvisorAutoProxyCreator
、その後、さまざまなシナリオの機能に適応するカスタム実装を実装します。
AutoProxyRegistrar
次に、実装を見てみましょう。実装されているためImportBeanDefinitionRegistrar
、ここで Bean を登録する論理操作が行われます。
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
// 获取配置类CglibAutoProxyConfiguration上的注解
Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
for (String annType : annTypes) {
// 获取注解上的属性
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (candidate == null) {
continue;
}
// 这个属性应该只有这里用到,用于判断是否创建代理
Object mode = candidate.get("mode");
// 这个属性它对应的是`proxyFactory`里的proxyTargetClass的,表示可以使用cglib代理
Object proxyTargetClass = candidate.get("proxyTargetClass");
if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
Boolean.class == proxyTargetClass.getClass()) {
candidateFound = true;
if (mode == AdviceMode.PROXY) {
// 如果不存在 InfrastructureAdvisorAutoProxyCreator 则注入一个:
// beanName=internalAutoProxyCreator,
// class=InfrastructureAdvisorAutoProxyCreator
// 的beanDefinition
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass) {
// 强制使用代理类设置,这里是把`proxyTargetClass`设置到InfrastructureAdvisorAutoProxyCreator
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
if (!candidateFound && logger.isInfoEnabled()) {
String name = getClass().getSimpleName();
// ......
}
}
ここを見てください:
ここでプロパティを設定しますproxyTargetClass
。InfrastructureAdvisorAutoProxyCreator
これはbeanNameですinternalAutoProxyCreator
。忘れた場合は、上記のコードのコメントをもう一度読むことができます。InfrastructureAdvisorAutoProxyCreator
ここで.add("proxyTargetClass", Boolean.TRUE);
、このクラスには属性がないproxyTargetClass
ため、add が使用されています。これは、次のロジックに関連するため、ここで言及しますが、実際には、実際には意味がありません。
作成されたプロキシ クラスは jdk ダイナミック プロキシまたは cglib ですか
結果: CGLIB プロキシのみになり、JDK プロキシではなくなります。理由については、以下を参照してください。
EnableTransactionManagement
これはアノテーションに設定されておりproxyTargetClass = true
、CGLIB を使用できることを意味します。以下のプロキシ クラスを作成するコードを参照してください。
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 判断是否需要优化,是否使用cglib代理,是否指定了接口
// 如果不需要优化,且没有指定要使用cglib代理,而且没有指定接口,就使用JDK代理
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("xxxxx");
}
// 判断代理类是否是一个接口,或者是否代理类
// 如果是则使用JDK代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
このメソッドには 3 つの判定があります (2 つ目は無視されます)。
- 1 つ目
if
: 最適化が必要かどうか、cglib プロキシを使用するかどうか、およびインターフェイスを指定するかどうかを決定します。まず、クラスの登録および作成時に、cblib プロキシ ( ) の使用を指定します。この条件が有効になると、インターフェイスは存在しませんconfig.isProxyTargetClass()=true
。検索が実行され、インターフェイスオブジェクト、つまり が設定されていませんがhasNoUserSuppliedProxyInterfaces(config)=false
、ここには or があるため、最初の判定が入ります - 3 番目
if
:targetClass
インターフェイス タイプかどうかを判断します。ここでのコードは Bean が初期化された後に実行されることに注意してください。そのため、この targetClass はインターフェイス タイプではありません。つまり、トランザクションをプロキシすることになりますtargetClass.isInterface()=false
。Proxy.isProxyClass(targetClass)=false
3番目は直接入力しない場合return new ObjenesisCglibAopProxy(config);
図:
前の AOP の章で、Spring の自動プロキシでは、プロキシ オブジェクトにインターフェイスがある場合は JDK プロキシが使用され、インターフェイスがない場合は cglib が使用されると述べました。
そして、ここでは属性の影響でproxyTargetClass=true
3番目のif判定に行き、3番目のifは全て偽となるため、全てcglibプロキシとなっています。
要約:
EnableTransactionManagementConfiguration
構成クラスには 2 つのアノテーションが構成されており、@EnableTransactionManagement
プロパティは異なりますが、いずれかのプロパティが次のとおりである限り、proxyTargetClass = true
true の場合、トランザクション プロキシ作成クラスが登録されInfrastructureAdvisorAutoProxyCreator
、proxyTargetClass = true
その中に設定が設定されます。プロキシクラス作成時 インターフェースの探索と設定を行った後、最後に CGLIB プロキシを作成します(私の結論はインターネット上のものとは異なります。インターネット上ではインターフェースの実装の有無でプロキシの方法が異なると言われていますが、私が見ているものはそうではありません)AutoProxyRegister
主な役割はトランザクション自動プロキシ作成クラスの作成です
ProxyTransactionManagementConfiguration の機能
ProxyTransactionManagementConfiguration
主な機能はポイントカットとプロキシロジックアドバイスの登録です。
ここでは@Role
無視してください。重要ではありません。
ここでは、次のように 3 つの Bean が注入され、プロキシ オブジェクトのインターセプト プロキシ ロジックが完成します。
TransactionInterceptor: から継承されたトランザクション アドバイス。TransactionAspectSupport
トランザクションのコア クラス、トランザクション管理、送信、レビュー、その他の処理の詳細が実装されます。
TransactionAttributeSource:@Transactional
トランザクションアノテーションの属性取得と、ポイントをカットするかどうかの判断を提供します()
BeanFactoryTransactionAttributeSourceAdvisor: 継承AbstractPointcutAdvisor
、トランザクション アドバイザ: pointCut + アドバイス、そして最後に ProxyFactory で取得したアドバイザ。
txManager: トランザクション マネージャー、クラス: TransactionManager
、親クラスによって挿入されました
TransactionAttributeSource
なぜ実装されないのかについてはここで説明しますPointcut
が、要点はカットされています。
実際、BeanFactoryTransactionAttributeSourceAdvisor
ここでの重要なロジックはTransactionAttributeSource
エージェントによって処理されます。
要約:
- プロキシに必要なカット ポイントを登録しました (どのクラスにプロキシが必要か)。
- トランザクション マネージャー インジェクションを含むプロキシ ロジック (トランザクションのオープン、jdbc の実行、ロールバックなどの詳細なロジック)
トランザクションプロキシプロセス
これらはすべて から継承しAbstractAdvisorAutoProxyCreator
、 のサブクラスでもありBeanPostProcessor
、InfrastructureAdvisorAutoProxyCreator
メソッドをオーバーライドしないため、プロキシを作成するプロセスは AOP の章と同じです。
Bean ポストプロセッサ呼び出しを初期化します。
AutoProxyregistrar
で行われたproxyTargetClass
設定は、コードを通じてコピーされますproxyFactory.copyFrom(this);
。ここでは、これは 、InfrastructureAdvisorAutoProxyCreator
継承ProxyConfig
ですが、その class には属性が定義されていませんproxyTargetClass
が、AutoProxyregistrar
この属性は の beanDefinition に手動で追加されます。
したがって、次のコードは使用されず、アドバイザを直接取得します。
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
この時点で、各トランザクション プロキシ オブジェクトが生成されます。
トランザクションプロキシを完了する方法
ここで、TransactionInterceptor
これがプロキシ メソッドを実行するためのエントリ ポイントであることが直接わかります。その理由は上で説明されています。
場所:org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
// tas提供事务注解`@Transactional`的属性获取,以及是否切点的判断
TransactionAttributeSource tas = getTransactionAttributeSource();
// tas 绝对不会为null,在自动配置初始化就已经设置进去了
// 获取方法上的@Transactional属性信息
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 获取事务管理器,
// 因为自动配置(TransactionAutoConfiguration)注入的是DataSourceTransactionManagerAutoConfiguration,
// 所以这里的tm是DataSourceTransactionManager
final TransactionManager tm = determineTransactionManager(txAttr);
// 这段是reactive的代码,不用理会
if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
// ......
}
// 这里是转换为父类,面向接口
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
// 生成事务方法标识
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// 事务信息
// 0.生成事务标识
// 1. 先创建数据源对象DataSourceTransactionObject
// 2. 从线程缓存中获取connectionHolder
// 3. 两个分支:
// 3.1 没有存在事务
// 3.1.1. 判断是否需要开启事务:REQUIRED/REQUIRES_NEW/NESTED
// 3.1.2. 创建事务维护对象TransactionStatus
// 3.1.3. 从DataSource中获取一个connection,并设置到connectionHolder中
// 3.1.4. 设置事务属性:状态,事务级别,只读false
// 3.1.5. 禁止自动提交:autoCommit=false
// 3.1.6. 设置事务激活状态,超时时间
// 3.1.7. 将connectionHolder以DataSource为key设置到线程缓存resource中
// 3.1.8. 激活当前事务:设置事务状态、属性等到当前线程变量里
// 4. 将事务线程里的事务信息设置到当前的事务信息里,用于回退,然后绑定当前的事务信息到线程缓存里
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// 执行业务方法逻辑
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 会先判断是否是我们指定的异常,如果是就会滚,如果不是,继续commit
// 这里会恢复被挂起的事务信息
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 清除事务信息
// 回退到上一个事务信息,如果上一个事务信息不存在,那么就结束事务了
cleanupTransactionInfo(txInfo);
}
// vavr类存在才会走,不用理会
if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
// 提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
このセクションは完全なトランザクション処理です。createTransactionIfNecessary
メソッドを入力して、その取得方法を確認します。
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// 如果事务属性的名称不存在,则将之前创建的事务标识设置进去
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
// 事务维护对象:它保存了事务信息对象、事务的是否提交、是否回滚、和挂起的resource等信息
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// 获取事务
// 1. 先创建数据源对象DataSourceTransactionObject
// 2. 从线程缓存中获取connectionHolder
// 3. 两个分支:
// 3.1 没有存在事务
// 3.1.1. 判断是否需要开启事务:REQUIRED/REQUIRES_NEW/NESTED
// 3.1.2. 创建事务维护对象TransactionStatus
// 3.1.3. 从DataSource中获取一个connection,并设置到connectionHolder中
// 3.1.4. 设置事务属性:状态,事务级别,只读false
// 3.1.5. 禁止自动提交:autoCommit=false
// 3.1.6. 设置事务激活状态,超时时间
// 3.1.7. 将connectionHolder以DataSource为key设置到线程缓存resource中
// 3.1.8. 激活当前事务:设置事务状态、属性等到当前线程变量里
// 3.2 存在事务
// 3.2.1. NEVER传播机制:不允许有事务,报错
// 3.2.2. NOT_SUPPORTED传播机制:不支持事务,
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
// 包装对象
// 4. 将事务线程里的事务信息设置到当前的事务信息里,用于回退,然后绑定当前的事务信息到线程缓存里
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
入力getTransaction
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
// 事务定义信息
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
// 获取当前线程的事务对象
// 1. 先创建数据源对象DataSourceTransactionObject
// 2. 从线程缓存中获取connectionHolder
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
// 判断是否有连接处理器和事务是否已开启
if (isExistingTransaction(transaction)) {
// 存在事务,那么安装事务传播机制执行
return handleExistingTransaction(def, transaction, debugEnabled);
}
//3.1 没有存在事务
// Check definition settings for new transaction.
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
// MANDATORY传播机制:如果当前事务不存在,则抛出Exception
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
// REQUIRED默认的传播机制
// REQUIRES_NEW新事务传播机制
// NESTED嵌套传播机制
// 除开这3种,其他的都不需要开启事务
// 3.1.1. 判断是否需要开启事务:REQUIRED/REQUIRES_NEW/NESTED
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 创建挂起的事务信息,第一次开启事务,这里suspendedResources=null,就是没有可以被挂起的
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
// 开启新事务
// 初始化当前线程里的事务信息
// 3.1.2. 创建事务维护对象TransactionStatus
// 3.1.3. 从DataSource中获取一个connection,并设置到connectionHolder中
// 3.1.4. 设置事务属性:状态,事务级别,只读false
// 3.1.5. 禁止自动提交:autoCommit=false
// 3.1.6. 设置事务激活状态,超时时间
// 3.1.7. 将connectionHolder以DataSource为key设置到线程缓存resource中
// 3.1.8. 激活当前事务:设置事务状态、属性等到当前线程变量里
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + def);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
初めて取引を開始する方法
入力doGetTransaction
protected Object doGetTransaction() {
// 创建数据源事务对象
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
// 初始默认设置,默认是false
txObject.setSavepointAllowed(isNestedTransactionAllowed());
// 通过dataSource从线程中获取对应的连接处理器,用于管理数据库连接行为
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
// 将connectingHolder设置到数据源事务对象中,之后判断一个事务是否存在,就是通过这个对象,判断是否有connectionHolder
// 因为不管执行多少次,都是会走这一步
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
TransactionSynchronizationManager は同期メカニズムのトランザクション管理オブジェクトであり、内部に keyと valueNamedThreadLocal
を持つスレッド レベルの変数があります。dataSource
ConnectionHolder
入力startTransaction
方法doBegin
_
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// 当没有connection处理器,或者事务不同步时就创建一个连接connection处理器
// 这里可以直接理解为,没有连接就获取一个connection,生成connection处理器
// 在上一步中,transaction对象,是从线程缓存中获取的,
// 如果线程缓存中存在connectionHolder,(txObject.hasConnectionHolder() = true)
// 那就表示已经走过这个方法:isSynchronizedWithTransaction()=true
// 如果txObject.hasConnectionHolder()=false,那isSynchronizedWithTransaction()也是false(默认值)
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
// 从DataSource中获取新连接
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
// 生成connectionHolder,并设置到当前的事务对象里
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// 更改状态 isSynchronizedWithTransaction() = true
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
// 获取最终的connection
con = txObject.getConnectionHolder().getConnection();
// 把事务信息设置到connection
// 并且,如果connection获取到的事务基本和@Transaction注解的基本不一样,就返回connection的事务级别,注意,connection是可能是上一次的,也可能是新创建的
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
// 将获取到的上一次事务的事务级别设置到当前的事务对象里,这里可以理解成事务的传播
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// 默认false
txObject.setReadOnly(definition.isReadOnly());
// 这里就是禁止自动提交,一切交由事务管理器去做
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
// 设置事务是否可读
prepareTransactionalConnection(con, definition);
// 设置事务激活状态
txObject.getConnectionHolder().setTransactionActive(true);
// 事务的超时时间,默认无限制
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
// 如果是新的connectionHolder那就设置到线程缓存中
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 3.1.2. 创建事务维护对象TransactionStatus
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 开启事务
// 3.1.3. 从DataSource中获取一个connection,并设置到connectionHolder中
// 3.1.4. 设置事务属性:状态,上一次的事务级别,只读false
// 3.1.5. 禁止自动提交:autoCommit=false
// 3.1.6. 设置事务激活状态,超时时间
// 3.1.7. 将connectionHolder以DataSource为key设置到线程缓存resource中
doBegin(transaction, definition);
// 3.1.8. 激活当前事务:设置事务状态、属性等到当前线程变量里
prepareSynchronization(status, definition);
return status;
}
最後のステップ: prepareTransactionInfo
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, String joinpointIdentification,
@Nullable TransactionStatus status) {
// 创建事务信息
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) {
if (logger.isTraceEnabled()) {
logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
// The transaction manager will flag an error if an incompatible tx already exists.
txInfo.newTransactionStatus(status);
}
else {
// The TransactionInfo.hasTransaction() method will return false. We created it only
// to preserve the integrity of the ThreadLocal stack maintained in this class.
if (logger.isTraceEnabled()) {
logger.trace("No need to create transaction for [" + joinpointIdentification +
"]: This method is not transactional.");
}
}
// 这里是将当前线程里缓存的事务信息绑定到当前的事务信息里(oldTransactionInfo)
// 怎么说呢,这里其实就是将上一次的事务信息从线程缓存里拿到,然后设置到当前事务里,在需要回退到上一个状态时用
txInfo.bindToThread();
return txInfo;
}
次に、ビジネス ロジックを実行し、ロールバックとコミットを実行する必要があります。
メソッドに戻りましょう。
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
ロールバックする方法を参照してください。
を書くときは、ここで使用する属性を@Transaction
追加することがよくあります。rollbackFor
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
// 判断是否符合我们指定的异常
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 回滚
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
// 那不是我们指定的异常,则继续提交
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
次に、トランザクション情報をクリアしますcleanupTransactionInfo
。最下層は次の段落です。
private void restoreThreadLocalStatus() {
// Use stack to restore old transaction TransactionInfo.
// Will be null if none was set.
transactionInfoHolder.set(this.oldTransactionInfo);
}
これもtxInfo.bindToThread();
このメソッドに対応します。以前のトランザクション情報があれば、その情報にロールバックします。
次にこの段落:
これは here を取得できるクラスでありCallbackPreferringPlatformTransactionManager
、そのサブクラスは JTA であるため、ここでは従いませんが、同じロジックになるはずです。
既存のトランザクションを実行する方法
初めに用事があれば、
org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
// NEVER传播机制,是不允许有事务的
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
// NOT_SUPPORTED传播机制,出现这个时,当前注解下的都没有事务,也没不要开启,所以没有doBegin
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
// 获取被挂起的事务信息,能走这段代码就说明,已存在事务,所以这里获取的是上一次开启的事务
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
// 生成一个新的没有事务信息TransactionStatus回去,同时也把上一次挂起的事务信息设置进去了
// 挂起的事务信息在这次事务提交后或是异常时,是要进行操作的
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
// REQUIRES_NEW新事物传播机制
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
// 同样,挂起事务
// 获取被挂起的事务信息,能走这段代码就说明,已存在事务,所以这里获取的是上一次开启的事务
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
// 开启事务,和上面解释过的方法的是同一个方法,同时也把上一次挂起的事务信息设置进去了
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
// NESTED传播机制:建立保存点,异常后回退到上一个保存点
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
// 默认true
if (useSavepointForNestedTransaction()) {
// 这里生成一个新的事务维护对象,newTransaction=false,这里用的还是之前的事务
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
// 这里是:getConnection().setSavepoint(SAVEPOINT_NAME_PREFIX + this.savepointCounter)
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
return startTransaction(definition, transaction, debugEnabled, null);
}
}
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
// 是否验证事务
if (isValidateExistingTransaction()) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
// 如果当前的事务级别不存在,或者与指定的级别不同,报错
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] specifies isolation level which is incompatible with existing transaction: " +
(currentIsolationLevel != null ?
isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
"(unknown)"));
}
}
// 如果只读,报错
if (!definition.isReadOnly()) {
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] is not marked as read-only but existing transaction is");
}
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 生成信息的事务维护对象
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
トランザクション一時停止からの回復
すでにトランザクションが存在する場合、トランザクションを再度開始します。伝播メカニズムは次のとおりです。NOT_SUPPORTED,REQUIRES_NEW
前の connectionHolder が取得され、SuspendedResourcesHolder
保留中のオブジェクトに設定され、connectionHolder を含む現在のスレッド内のすべてのトランザクション情報が保存されます。新しいものを取得すると、この中断されたオブジェクトはトランザクション保守オブジェクトconnection
に設定され、現在のトランザクションが実行されるか例外が発生したときに中断されたオブジェクトが復元されます。TransactionStatus
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
// 激活状态,这个只要执行了startTransaction方法都是true,即事务的已开启
// 会执行的有:REQUIRES_NEW/NESTED传播机制
if (TransactionSynchronizationManager.isSynchronizationActive()) {
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {
// 清除掉:transaction里的connectionHolder,线程缓存里的connectionHolder
// 并返回清除的对象,也就是旧的connectionHolder
suspendedResources = doSuspend(transaction);
}
// 清除当前线程的事务信息:事务名称,只读状态,隔离级别等
String name = TransactionSynchronizationManager.getCurrentTransactionName();
TransactionSynchronizationManager.setCurrentTransactionName(null);
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
TransactionSynchronizationManager.setActualTransactionActive(false);
// 再生成挂起的事务对象,并返回,之后再commit后或者rollback后使用
return new SuspendedResourcesHolder(
suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
}
catch (RuntimeException | Error ex) {
// doSuspend failed - original transaction is still active...
doResumeSynchronization(suspendedSynchronizations);
throw ex;
}
}
// 然后这里,我不知道它会在什么情况下会走到这里
else if (transaction != null) {
// Transaction active but no synchronization active.
Object suspendedResources = doSuspend(transaction);
return new SuspendedResourcesHolder(suspendedResources);
}
else {
// 这里就是第一次开启事务时会返回,也就表示没有可被挂起的对象
return null;
}
}
ロールバック
- 実行すると、
cleanupTransactionInfo
トランザクション オブジェクトがTransactionInfo
前のトランザクションに戻ります。
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
例外が発生すると、最初に例外が実行されますcleanupTransactionInfo
。その最下層は次のとおりです。
private void restoreThreadLocalStatus() {
// 如果时第一次开启的事务,oldTransactionInfo为null,否则,oldTransactionInfo是上一次的事务
transactionInfoHolder.set(this.oldTransactionInfo);
}
oldTransactionInfo
オブジェクトは、新しいものを開くときにREQUIRED/REQUIRES_NEW/NESTED
スレッドにバインドされます( )、つまりbindToThread()
、この時点oldTransactionInfo
で取得されます。このときに開かれたトランザクションが以前に開かれていた場合、ここで、前回のトランザクション オブジェクトが形成されます. 類似リンクリスト 各ノードの存在は、前のトランザクションと現在のトランザクションという 2 つの属性を持っているため、連鎖反応が起こりやすいです。transactionInfoHolder
transactionInfoHolder
oldTransactionInfo
transactionInfoHolder
private void bindToThread() {
this.oldTransactionInfo = transactionInfoHolder.get();
transactionInfoHolder.set(this);
}
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
// 这里默认RuntimeException 和 Error 会被回退
// 判断是否是我们指定的异常,即是否需要回滚
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
else {
// 不需要回滚,则继续提交
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
さらに奥へ進む
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
// 前置方法,可用于扩展
triggerBeforeCompletion(status);
// 如果要保存点,也就是NESTED传播机制
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
// 回滚到上一个保存点
status.rollbackToHeldSavepoint();
}
// 新事物
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
// 回滚:connection.rolaback
doRollback(status);
}
// 不是上面两种情况的走这里
else {
// 这里判断参与事务的某个方法异常,是否需要全局回滚
// 设想一下,什么情况会进来?
// NESTED REQUIRES_NEW这两种都机制都在上面处理了,那么下面处理的就是REQUIRED
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
// 标记属性rollbackOnly=true
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
// Unexpected rollback only matters here if we're asked to fail early
if (!isFailEarlyOnGlobalRollbackOnly()) {
// 立即终止事务执行
unexpectedRollback = false;
}
}
}
catch (RuntimeException | Error ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
// 后置方法
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// Raise UnexpectedRollbackException if we had a global rollback-only marker
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
// 这个是重要方法:回退到挂载点
cleanupAfterCompletion(status);
}
}
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
status.setCompleted();
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.clear();
}
if (status.isNewTransaction()) {
doCleanupAfterCompletion(status.getTransaction());
}
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}
Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
// 这里就是恢复挂载点的逻辑
// 会生成挂载点的只有:NOT_SUPPORTED,REQUIRES_NEW
resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
}
}
マウントポイントを復元した後、スレッドは同じままで、トランザクション情報は古い情報になり、トランザクションの実行はエージェントに依存しているため、外側のエージェントが最後のトランザクション情報になります。
同じコミット内に同じメソッドがありますcleanupAfterCompletion
。