Adapt auto-configurations to back-off in presence of TransactionManager

Previous to this commit, an auto-configuration would create a
PlatformTransactionManager if none is present and others conditions are
met. Spring Framework now has the notion of TransactionManager, a parent
interface that gathers both PlatformTransactionManager and
ReactiveTransactionManager.

Spring Boot should not be in a situation where both managers are defined
out-of-the-box. This commit makes sure to back-off if any
TransactionManager is available.

Closes gh-22851
pull/23084/head
Stephane Nicoll 4 years ago
parent f191b7513d
commit 085091dbe3

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-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.
@ -31,7 +31,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionManager;
/**
* {@link EnableAutoConfiguration Auto-configuration} for
@ -44,7 +44,7 @@ import org.springframework.transaction.PlatformTransactionManager;
* @since 1.0.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
@ConditionalOnClass({ JdbcTemplate.class, TransactionManager.class })
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {
@ -54,7 +54,7 @@ public class DataSourceTransactionManagerAutoConfiguration {
static class DataSourceTransactionManagerConfiguration {
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
@ConditionalOnMissingBean(TransactionManager.class)
DataSourceTransactionManager transactionManager(DataSource dataSource,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-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.
@ -54,6 +54,7 @@ import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.jta.JtaTransactionManager;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@ -92,7 +93,7 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware {
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnMissingBean(TransactionManager.class)
public PlatformTransactionManager transactionManager(
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
JpaTransactionManager transactionManager = new JpaTransactionManager();

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-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.
@ -41,7 +41,6 @@ import org.springframework.boot.jta.atomikos.AtomikosXADataSourceWrapper;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.jta.JtaTransactionManager;
import org.springframework.util.StringUtils;
@ -57,7 +56,7 @@ import org.springframework.util.StringUtils;
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({ AtomikosProperties.class, JtaProperties.class })
@ConditionalOnClass({ JtaTransactionManager.class, UserTransactionManager.class })
@ConditionalOnMissingBean(PlatformTransactionManager.class)
@ConditionalOnMissingBean(org.springframework.transaction.TransactionManager.class)
class AtomikosJtaConfiguration {
@Bean(initMethod = "init", destroyMethod = "shutdownWait")

@ -37,7 +37,6 @@ import org.springframework.boot.jms.XAConnectionFactoryWrapper;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.jta.JtaTransactionManager;
import org.springframework.util.StringUtils;
@ -54,7 +53,7 @@ import org.springframework.util.StringUtils;
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(JtaProperties.class)
@ConditionalOnClass({ JtaTransactionManager.class, BitronixContext.class })
@ConditionalOnMissingBean(PlatformTransactionManager.class)
@ConditionalOnMissingBean(org.springframework.transaction.TransactionManager.class)
class BitronixJtaConfiguration {
@Bean

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-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.
@ -23,7 +23,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.config.JtaTransactionManagerFactoryBean;
import org.springframework.transaction.jta.JtaTransactionManager;
@ -38,7 +37,7 @@ import org.springframework.transaction.jta.JtaTransactionManager;
@ConditionalOnClass(JtaTransactionManager.class)
@ConditionalOnJndi({ JtaTransactionManager.DEFAULT_USER_TRANSACTION_NAME, "java:comp/TransactionManager",
"java:appserver/TransactionManager", "java:pm/TransactionManager", "java:/TransactionManager" })
@ConditionalOnMissingBean(PlatformTransactionManager.class)
@ConditionalOnMissingBean(org.springframework.transaction.TransactionManager.class)
class JndiJtaConfiguration {
@Bean

@ -26,7 +26,7 @@ import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@ -54,7 +54,7 @@ class DataSourceTransactionManagerAutoConfigurationTests {
void transactionManagerWithExistingDataSourceIsConfigured() {
this.contextRunner.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
.run((context) -> {
assertThat(context).hasSingleBean(PlatformTransactionManager.class)
assertThat(context).hasSingleBean(TransactionManager.class)
.hasSingleBean(DataSourceTransactionManager.class);
assertThat(context.getBean(DataSourceTransactionManager.class).getDataSource())
.isSameAs(context.getBean(DataSource.class));
@ -67,7 +67,7 @@ class DataSourceTransactionManagerAutoConfigurationTests {
.withPropertyValues("spring.transaction.default-timeout=1m",
"spring.transaction.rollback-on-commit-failure=true")
.run((context) -> {
assertThat(context).hasSingleBean(PlatformTransactionManager.class)
assertThat(context).hasSingleBean(TransactionManager.class)
.hasSingleBean(DataSourceTransactionManager.class);
DataSourceTransactionManager transactionManager = context
.getBean(DataSourceTransactionManager.class);
@ -79,22 +79,21 @@ class DataSourceTransactionManagerAutoConfigurationTests {
@Test
void transactionManagerWithExistingTransactionManagerIsNotOverridden() {
this.contextRunner
.withBean("myTransactionManager", PlatformTransactionManager.class,
() -> mock(PlatformTransactionManager.class))
.run((context) -> assertThat(context).hasSingleBean(PlatformTransactionManager.class)
.withBean("myTransactionManager", TransactionManager.class, () -> mock(TransactionManager.class))
.run((context) -> assertThat(context).hasSingleBean(TransactionManager.class)
.hasBean("myTransactionManager"));
}
@Test
void transactionWithMultipleDataSourcesIsNotConfigured() {
this.contextRunner.withUserConfiguration(MultiDataSourceConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(PlatformTransactionManager.class));
.run((context) -> assertThat(context).doesNotHaveBean(TransactionManager.class));
}
@Test
void transactionWithMultipleDataSourcesAndPrimaryCandidateIsConfigured() {
this.contextRunner.withUserConfiguration(MultiDataSourceUsingPrimaryConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(PlatformTransactionManager.class)
assertThat(context).hasSingleBean(TransactionManager.class)
.hasSingleBean(DataSourceTransactionManager.class);
assertThat(context.getBean(DataSourceTransactionManager.class).getDataSource())
.isSameAs(context.getBean("test1DataSource"));

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-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.
@ -49,6 +49,7 @@ import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionManager;
import static org.assertj.core.api.Assertions.assertThat;
@ -93,7 +94,7 @@ abstract class AbstractJpaAutoConfigurationTests {
return (context) -> {
assertThat(context).hasNotFailed();
assertThat(context).hasSingleBean(JpaProperties.class);
assertThat(context).doesNotHaveBean(PlatformTransactionManager.class);
assertThat(context).doesNotHaveBean(TransactionManager.class);
assertThat(context).doesNotHaveBean(EntityManagerFactory.class);
};
}
@ -117,7 +118,7 @@ abstract class AbstractJpaAutoConfigurationTests {
}
@Test
void jtaTransactionManagerTakesPrecedence() {
void jpaTransactionManagerTakesPrecedenceOverSimpleDataSourceOne() {
this.contextRunner.withConfiguration(AutoConfigurations.of(DataSourceTransactionManagerAutoConfiguration.class))
.run((context) -> {
assertThat(context).hasSingleBean(DataSource.class);
@ -211,7 +212,8 @@ abstract class AbstractJpaAutoConfigurationTests {
@Test
void usesManuallyDefinedTransactionManagerBeanIfAvailable() {
this.contextRunner.withUserConfiguration(TestConfigurationWithTransactionManager.class).run((context) -> {
PlatformTransactionManager txManager = context.getBean(PlatformTransactionManager.class);
assertThat(context).hasSingleBean(TransactionManager.class);
TransactionManager txManager = context.getBean(TransactionManager.class);
assertThat(txManager).isInstanceOf(CustomJpaTransactionManager.class);
});
}
@ -360,7 +362,7 @@ abstract class AbstractJpaAutoConfigurationTests {
static class TestConfigurationWithTransactionManager {
@Bean
PlatformTransactionManager transactionManager() {
TransactionManager testTransactionManager() {
return new CustomJpaTransactionManager();
}

@ -51,7 +51,6 @@ import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.jta.JtaTransactionManager;
import static org.assertj.core.api.Assertions.assertThat;
@ -81,7 +80,7 @@ class JtaAutoConfigurationTests {
}
@Test
void customPlatformTransactionManager() {
void customTransactionManager() {
this.context = new AnnotationConfigApplicationContext(CustomTransactionManagerConfig.class,
JtaAutoConfiguration.class);
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
@ -240,8 +239,8 @@ class JtaAutoConfigurationTests {
static class CustomTransactionManagerConfig {
@Bean
PlatformTransactionManager transactionManager() {
return mock(PlatformTransactionManager.class);
org.springframework.transaction.TransactionManager testTransactionManager() {
return mock(org.springframework.transaction.TransactionManager.class);
}
}

Loading…
Cancel
Save