Add auto-configuration support for TransactionalOperator

Closes gh-18265
pull/18295/head
Andy Wilkinson 5 years ago
parent 3b01bc0366
commit 8791b696f8

@ -33,8 +33,11 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.ReactiveTransactionManager;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.annotation.AbstractTransactionManagementConfiguration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.reactive.TransactionalOperator;
import org.springframework.transaction.support.TransactionOperations;
import org.springframework.transaction.support.TransactionTemplate;
@ -59,6 +62,13 @@ public class TransactionAutoConfiguration {
return new TransactionManagerCustomizers(customizers.orderedStream().collect(Collectors.toList()));
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(ReactiveTransactionManager.class)
public TransactionalOperator transactionalOperator(ReactiveTransactionManager transactionManager) {
return TransactionalOperator.create(transactionManager);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnSingleCandidate(PlatformTransactionManager.class)
public static class TransactionTemplateConfiguration {
@ -72,7 +82,7 @@ public class TransactionAutoConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(PlatformTransactionManager.class)
@ConditionalOnBean(TransactionManager.class)
@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
public static class EnableTransactionManagementConfiguration {

@ -18,7 +18,6 @@ package org.springframework.boot.autoconfigure.transaction;
import javax.sql.DataSource;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
@ -26,14 +25,15 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.ReactiveTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.reactive.TransactionalOperator;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate;
@ -52,43 +52,86 @@ class TransactionAutoConfigurationTests {
.withConfiguration(AutoConfigurations.of(TransactionAutoConfiguration.class));
@Test
void noTransactionManager() {
void whenThereIsNoPlatformTransactionManagerNoTransactionTemplateIsAutoConfigured() {
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(TransactionTemplate.class));
}
@Test
void singleTransactionManager() {
void whenThereIsASinglePlatformTransactionManagerATransactionTemplateIsAutoConfigured() {
this.contextRunner.withUserConfiguration(SinglePlatformTransactionManagerConfiguration.class).run((context) -> {
PlatformTransactionManager transactionManager = context.getBean(PlatformTransactionManager.class);
TransactionTemplate transactionTemplate = context.getBean(TransactionTemplate.class);
assertThat(transactionTemplate.getTransactionManager()).isSameAs(transactionManager);
});
}
@Test
void whenThereIsASingleReactiveTransactionManagerATransactionalOperatorIsAutoConfigured() {
this.contextRunner.withUserConfiguration(SingleReactiveTransactionManagerConfiguration.class).run((context) -> {
ReactiveTransactionManager transactionManager = context.getBean(ReactiveTransactionManager.class);
TransactionalOperator transactionalOperator = context.getBean(TransactionalOperator.class);
assertThat(transactionalOperator).extracting("transactionManager").isSameAs(transactionManager);
});
}
@Test
void whenThereAreBothReactiveAndPlatformTransactionManagersATemplateAndAnOperatorAreAutoConfigured() {
this.contextRunner
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class))
.withPropertyValues("spring.datasource.initialization-mode:never").run((context) -> {
PlatformTransactionManager transactionManager = context.getBean(PlatformTransactionManager.class);
.withUserConfiguration(SinglePlatformTransactionManagerConfiguration.class,
SingleReactiveTransactionManagerConfiguration.class)
.run((context) -> {
PlatformTransactionManager platformTransactionManager = context
.getBean(PlatformTransactionManager.class);
TransactionTemplate transactionTemplate = context.getBean(TransactionTemplate.class);
assertThat(transactionTemplate.getTransactionManager()).isSameAs(transactionManager);
assertThat(transactionTemplate.getTransactionManager()).isSameAs(platformTransactionManager);
ReactiveTransactionManager reactiveTransactionManager = context
.getBean(ReactiveTransactionManager.class);
TransactionalOperator transactionalOperator = context.getBean(TransactionalOperator.class);
assertThat(transactionalOperator).extracting("transactionManager")
.isSameAs(reactiveTransactionManager);
});
}
@Test
void severalTransactionManagers() {
this.contextRunner.withUserConfiguration(SeveralTransactionManagersConfiguration.class)
void whenThereAreSeveralPlatformTransactionManagersNoTransactionTemplateIsAutoConfigured() {
this.contextRunner.withUserConfiguration(SeveralPlatformTransactionManagersConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(TransactionTemplate.class));
}
@Test
void customTransactionManager() {
this.contextRunner.withUserConfiguration(CustomTransactionManagerConfiguration.class).run((context) -> {
void whenThereAreSeveralReactiveTransactionManagersNoTransactionOperatorIsAutoConfigured() {
this.contextRunner.withUserConfiguration(SeveralReactiveTransactionManagersConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(TransactionalOperator.class));
}
@Test
void whenAUserProvidesATransactionTemplateTheAutoConfiguredTemplateBacksOff() {
this.contextRunner.withUserConfiguration(CustomPlatformTransactionManagerConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(TransactionTemplate.class);
assertThat(context.getBean("transactionTemplateFoo")).isInstanceOf(TransactionTemplate.class);
});
}
@Test
void whenAUserProvidesATransactionalOperatorTheAutoConfiguredOperatorBacksOff() {
this.contextRunner.withUserConfiguration(SingleReactiveTransactionManagerConfiguration.class,
CustomTransactionalOperatorConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(TransactionalOperator.class);
assertThat(context.getBean("customTransactionalOperator"))
.isInstanceOf(TransactionalOperator.class);
});
}
@Test
void platformTransactionManagerCustomizers() {
this.contextRunner.withUserConfiguration(SeveralTransactionManagersConfiguration.class).run((context) -> {
TransactionManagerCustomizers customizers = context.getBean(TransactionManagerCustomizers.class);
assertThat(customizers).extracting("customizers").asList().hasSize(1).first()
.isInstanceOf(TransactionProperties.class);
});
this.contextRunner.withUserConfiguration(SeveralPlatformTransactionManagersConfiguration.class)
.run((context) -> {
TransactionManagerCustomizers customizers = context.getBean(TransactionManagerCustomizers.class);
assertThat(customizers).extracting("customizers").asList().hasSize(1).first()
.isInstanceOf(TransactionProperties.class);
});
}
@Test
@ -99,7 +142,7 @@ class TransactionAutoConfigurationTests {
@Test
void transactionManagerUsesCglibByDefault() {
this.contextRunner.withUserConfiguration(TransactionManagersConfiguration.class).run((context) -> {
this.contextRunner.withUserConfiguration(PlatformTransactionManagersConfiguration.class).run((context) -> {
assertThat(context.getBean(AnotherServiceImpl.class).isTransactionActive()).isTrue();
assertThat(context.getBeansOfType(TransactionalServiceImpl.class)).hasSize(1);
});
@ -107,7 +150,7 @@ class TransactionAutoConfigurationTests {
@Test
void transactionManagerCanBeConfiguredToJdkProxy() {
this.contextRunner.withUserConfiguration(TransactionManagersConfiguration.class)
this.contextRunner.withUserConfiguration(PlatformTransactionManagersConfiguration.class)
.withPropertyValues("spring.aop.proxy-target-class=false").run((context) -> {
assertThat(context.getBean(AnotherService.class).isTransactionActive()).isTrue();
assertThat(context).doesNotHaveBean(AnotherServiceImpl.class);
@ -119,7 +162,7 @@ class TransactionAutoConfigurationTests {
void customEnableTransactionManagementTakesPrecedence() {
this.contextRunner
.withUserConfiguration(CustomTransactionManagementConfiguration.class,
TransactionManagersConfiguration.class)
PlatformTransactionManagersConfiguration.class)
.withPropertyValues("spring.aop.proxy-target-class=true").run((context) -> {
assertThat(context.getBean(AnotherService.class).isTransactionActive()).isTrue();
assertThat(context).doesNotHaveBean(AnotherServiceImpl.class);
@ -127,8 +170,28 @@ class TransactionAutoConfigurationTests {
});
}
@Configuration
static class SinglePlatformTransactionManagerConfiguration {
@Bean
PlatformTransactionManager transactionManager() {
return mock(PlatformTransactionManager.class);
}
}
@Configuration
static class SingleReactiveTransactionManagerConfiguration {
@Bean
ReactiveTransactionManager reactiveTransactionManager() {
return mock(ReactiveTransactionManager.class);
}
}
@Configuration(proxyBeanMethods = false)
static class SeveralTransactionManagersConfiguration {
static class SeveralPlatformTransactionManagersConfiguration {
@Bean
PlatformTransactionManager transactionManagerOne() {
@ -143,7 +206,22 @@ class TransactionAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class CustomTransactionManagerConfiguration {
static class SeveralReactiveTransactionManagersConfiguration {
@Bean
ReactiveTransactionManager reactiveTransactionManager1() {
return mock(ReactiveTransactionManager.class);
}
@Bean
ReactiveTransactionManager reactiveTransactionManager2() {
return mock(ReactiveTransactionManager.class);
}
}
@Configuration(proxyBeanMethods = false)
static class CustomPlatformTransactionManagerConfiguration {
@Bean
TransactionTemplate transactionTemplateFoo(PlatformTransactionManager transactionManager) {
@ -157,6 +235,16 @@ class TransactionAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class CustomTransactionalOperatorConfiguration {
@Bean
TransactionalOperator customTransactionalOperator() {
return mock(TransactionalOperator.class);
}
}
@Configuration(proxyBeanMethods = false)
static class BaseConfiguration {
@ -174,7 +262,7 @@ class TransactionAutoConfigurationTests {
@Configuration(proxyBeanMethods = false)
@Import(BaseConfiguration.class)
static class TransactionManagersConfiguration {
static class PlatformTransactionManagersConfiguration {
@Bean
DataSourceTransactionManager transactionManager(DataSource dataSource) {

Loading…
Cancel
Save