diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java index c71e2ec106..61a5cb7cf8 100644 --- a/spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java @@ -183,13 +183,6 @@ public abstract class TestContextTransactionUtils { if (bf instanceof ListableBeanFactory) { ListableBeanFactory lbf = (ListableBeanFactory) bf; - // Look up single bean by type - Map txMgrs = - BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, PlatformTransactionManager.class); - if (txMgrs.size() == 1) { - return txMgrs.values().iterator().next(); - } - // Look up single TransactionManagementConfigurer Map configurers = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, TransactionManagementConfigurer.class); @@ -203,6 +196,13 @@ public abstract class TestContextTransactionUtils { return (PlatformTransactionManager) tm; } + // Look up single bean by type + Map txMgrs = + BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, PlatformTransactionManager.class); + if (txMgrs.size() == 1) { + return txMgrs.values().iterator().next(); + } + try { // Look up single bean by type, with support for 'primary' beans return bf.getBean(PlatformTransactionManager.class); diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/manager/LookUpTxMgrViaTransactionManagementConfigurerWithSingleTxMgrBeanTests.java b/spring-test/src/test/java/org/springframework/test/context/transaction/manager/LookUpTxMgrViaTransactionManagementConfigurerWithSingleTxMgrBeanTests.java new file mode 100644 index 0000000000..ea4263bb21 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/manager/LookUpTxMgrViaTransactionManagementConfigurerWithSingleTxMgrBeanTests.java @@ -0,0 +1,99 @@ +/* + * Copyright 2002-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.transaction.manager; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import org.springframework.test.context.transaction.AfterTransaction; +import org.springframework.transaction.TransactionManager; +import org.springframework.transaction.annotation.TransactionManagementConfigurer; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.testfixture.CallCountingTransactionManager; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration test that verifies the behavior for transaction manager lookups + * when only one transaction manager is configured as a bean in the application + * context and a non-bean transaction manager is configured via the + * {@link TransactionManagementConfigurer} API. + * + * @author Sam Brannen + * @since 5.3 + */ +@SpringJUnitConfig +@Transactional +class LookUpTxMgrViaTransactionManagementConfigurerWithSingleTxMgrBeanTests { + + @Autowired + CallCountingTransactionManager txManager; + + @Autowired + Config config; + + + @Test + void transactionalTest() { + assertThat(txManager.begun).isEqualTo(0); + assertThat(txManager.inflight).isEqualTo(0); + assertThat(txManager.commits).isEqualTo(0); + assertThat(txManager.rollbacks).isEqualTo(0); + + CallCountingTransactionManager annotationDriven = config.annotationDriven; + assertThat(annotationDriven.begun).isEqualTo(1); + assertThat(annotationDriven.inflight).isEqualTo(1); + assertThat(annotationDriven.commits).isEqualTo(0); + assertThat(annotationDriven.rollbacks).isEqualTo(0); + } + + @AfterTransaction + void afterTransaction() { + assertThat(txManager.begun).isEqualTo(0); + assertThat(txManager.inflight).isEqualTo(0); + assertThat(txManager.commits).isEqualTo(0); + assertThat(txManager.rollbacks).isEqualTo(0); + + CallCountingTransactionManager annotationDriven = config.annotationDriven; + assertThat(annotationDriven.begun).isEqualTo(1); + assertThat(annotationDriven.inflight).isEqualTo(0); + assertThat(annotationDriven.commits).isEqualTo(0); + assertThat(annotationDriven.rollbacks).isEqualTo(1); + } + + + @Configuration + static class Config implements TransactionManagementConfigurer { + + final CallCountingTransactionManager annotationDriven = new CallCountingTransactionManager(); + + @Bean + TransactionManager txManager() { + return new CallCountingTransactionManager(); + } + + @Override + public TransactionManager annotationDrivenTransactionManager() { + return annotationDriven; + } + + } + +} diff --git a/spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java b/spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java index ddc6e0713c..d7ba674273 100644 --- a/spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java +++ b/spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java @@ -168,6 +168,29 @@ public class EnableTransactionManagementTests { ctx.close(); } + @Test + public void txManagerIsResolvedCorrectlyWithSingleTxManagerBeanAndTxMgmtConfigurer() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext( + EnableTxConfig.class, SingleTxManagerBeanAndTxMgmtConfigurerConfig.class); + assertThat(ctx.getBeansOfType(TransactionManager.class)).hasSize(1); + TransactionalTestBean bean = ctx.getBean(TransactionalTestBean.class); + CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class); + SingleTxManagerBeanAndTxMgmtConfigurerConfig config = ctx.getBean(SingleTxManagerBeanAndTxMgmtConfigurerConfig.class); + CallCountingTransactionManager annotationDriven = config.annotationDriven; + + // invoke a transactional method, causing the PlatformTransactionManager bean to be resolved. + bean.findAllFoos(); + + assertThat(txManager.begun).isEqualTo(0); + assertThat(txManager.commits).isEqualTo(0); + assertThat(txManager.rollbacks).isEqualTo(0); + assertThat(annotationDriven.begun).isEqualTo(1); + assertThat(annotationDriven.commits).isEqualTo(1); + assertThat(annotationDriven.rollbacks).isEqualTo(0); + + ctx.close(); + } + /** * A cheap test just to prove that in ASPECTJ mode, the AnnotationTransactionAspect does indeed * get loaded -- or in this case, attempted to be loaded at which point the test fails. @@ -384,6 +407,30 @@ public class EnableTransactionManagementTests { } + @Configuration + static class SingleTxManagerBeanAndTxMgmtConfigurerConfig implements TransactionManagementConfigurer { + + final CallCountingTransactionManager annotationDriven = new CallCountingTransactionManager(); + + @Bean + public TransactionalTestBean testBean() { + return new TransactionalTestBean(); + } + + @Bean + public PlatformTransactionManager txManager() { + return new CallCountingTransactionManager(); + } + + // The transaction manager returned from this method is intentionally not + // registered as a bean in the ApplicationContext. + @Override + public PlatformTransactionManager annotationDrivenTransactionManager() { + return annotationDriven; + } + } + + @Configuration @EnableTransactionManagement static class Spr11915Config {