From b20dff16faebac9731140537f8206fd67975eccf Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 17 Mar 2017 14:38:13 +0100 Subject: [PATCH] Enable transaction management consistently Previously to this commit, transaction management was only enabled when a `DataSource` is configured. The processing of `@Transactional` annotations are now enabled as long as a `PlatformTransactionManager` is present. Also, the `spring.aop.proxy-target-class` is now honoured if set, still defaulting to CGLIB mode. Closes gh-8434 --- ...ceTransactionManagerAutoConfiguration.java | 11 +- .../jpa/HibernateJpaAutoConfiguration.java | 6 +- ...ransactionManagementAutoConfiguration.java | 56 +++++++ .../main/resources/META-INF/spring.factories | 1 + ...nsactionManagerAutoConfigurationTests.java | 19 +-- ...ctionManagementAutoConfigurationTests.java | 149 ++++++++++++++++++ .../gemfire/SampleDataGemFireApplication.java | 4 +- .../main/resources/META-INF/spring.factories | 1 + 8 files changed, 213 insertions(+), 34 deletions(-) create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/EnableTransactionManagementAutoConfiguration.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/EnableTransactionManagementAutoConfigurationTests.java diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfiguration.java index d5feaf1352cf..a3ebf7c17016 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -29,8 +29,6 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.annotation.AbstractTransactionManagementConfiguration; -import org.springframework.transaction.annotation.EnableTransactionManagement; /** * {@link EnableAutoConfiguration Auto-configuration} for @@ -63,11 +61,4 @@ public DataSourceTransactionManager transactionManager() { } - @ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class) - @Configuration - @EnableTransactionManagement(proxyTargetClass = true) - protected static class TransactionManagementConfiguration { - - } - } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java index 4d1a38c69a72..ba4b84417bcf 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -47,7 +47,6 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; -import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.jta.JtaTransactionManager; import org.springframework.util.ClassUtils; @@ -60,8 +59,7 @@ * @author Andy Wilkinson */ @Configuration -@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class, - EnableTransactionManagement.class, EntityManager.class }) +@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class, EntityManager.class }) @Conditional(HibernateEntityManagerCondition.class) @AutoConfigureAfter({ DataSourceAutoConfiguration.class }) public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/EnableTransactionManagementAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/EnableTransactionManagementAutoConfiguration.java new file mode 100644 index 000000000000..347715d30aee --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/EnableTransactionManagementAutoConfiguration.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.transaction; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.AbstractTransactionManagementConfiguration; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * Enables Spring's annotation-driven transaction management capability. + * + * @author Stephane Nicoll + * @since 1.4.6 + */ +@Configuration +@ConditionalOnClass(EnableTransactionManagement.class) +@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class) +@ConditionalOnBean(PlatformTransactionManager.class) +@AutoConfigureAfter(TransactionAutoConfiguration.class) +class EnableTransactionManagementAutoConfiguration { + + @Configuration + @EnableTransactionManagement(proxyTargetClass = false) + @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false) + public static class JdkDynamicAutoProxyConfiguration { + + } + + @Configuration + @EnableTransactionManagement(proxyTargetClass = true) + @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) + public static class CglibAutoProxyConfiguration { + + } + +} diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 62f5a8b2df29..82dbb88df024 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -87,6 +87,7 @@ org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\ org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\ org.springframework.boot.autoconfigure.velocity.VelocityAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ +org.springframework.boot.autoconfigure.transaction.EnableTransactionManagementAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\ org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\ diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfigurationTests.java index 93b44a1414dc..a7bf2e793499 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfigurationTests.java @@ -25,8 +25,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.annotation.AbstractTransactionManagementConfiguration; -import org.springframework.transaction.annotation.EnableTransactionManagement; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -49,8 +47,6 @@ public void testDataSourceExists() throws Exception { this.context.refresh(); assertThat(this.context.getBean(DataSource.class)).isNotNull(); assertThat(this.context.getBean(DataSourceTransactionManager.class)).isNotNull(); - assertThat(this.context.getBean(AbstractTransactionManagementConfiguration.class)) - .isNotNull(); } @Test @@ -64,8 +60,7 @@ public void testNoDataSourceExists() throws Exception { @Test public void testManualConfiguration() throws Exception { - this.context.register(SwitchTransactionsOn.class, - EmbeddedDataSourceConfiguration.class, + this.context.register(EmbeddedDataSourceConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(DataSource.class)).isNotNull(); @@ -74,8 +69,7 @@ public void testManualConfiguration() throws Exception { @Test public void testExistingTransactionManager() { - this.context.register(SwitchTransactionsOn.class, - TransactionManagerConfiguration.class, + this.context.register(TransactionManagerConfiguration.class, EmbeddedDataSourceConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class); this.context.refresh(); @@ -92,8 +86,6 @@ public void testMultiDataSource() throws Exception { this.context.refresh(); assertThat(this.context.getBeansOfType(PlatformTransactionManager.class)) .isEmpty(); - assertThat(this.context.getBean(AbstractTransactionManagementConfiguration.class)) - .isNotNull(); } @Test @@ -102,13 +94,6 @@ public void testMultiDataSourceUsingPrimary() throws Exception { DataSourceTransactionManagerAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(DataSourceTransactionManager.class)).isNotNull(); - assertThat(this.context.getBean(AbstractTransactionManagementConfiguration.class)) - .isNotNull(); - } - - @EnableTransactionManagement - protected static class SwitchTransactionsOn { - } @Configuration diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/EnableTransactionManagementAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/EnableTransactionManagementAutoConfigurationTests.java new file mode 100644 index 000000000000..3aaa1ea50c6f --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/EnableTransactionManagementAutoConfigurationTests.java @@ -0,0 +1,149 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.boot.autoconfigure.transaction; + +import javax.sql.DataSource; + +import org.junit.After; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; +import org.springframework.boot.test.util.EnvironmentTestUtils; +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.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link EnableTransactionManagementAutoConfiguration}. + * + * @author Stephane Nicoll + */ +public class EnableTransactionManagementAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @After + public void tearDown() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void transactionNotManagedWithNoTransactionManager() { + load(BaseConfiguration.class); + assertThat(this.context.getBean(TransactionalService.class) + .isTransactionActive()).isFalse(); + } + + @Test + public void transactionManagerUsesCglibByDefault() { + load(TransactionManagersConfiguration.class); + assertThat(this.context.getBean(AnotherServiceImpl.class) + .isTransactionActive()).isTrue(); + assertThat(this.context.getBeansOfType(TransactionalServiceImpl.class)).hasSize(1); + } + + @Test + public void transactionManagerCanBeConfiguredToJdkProxy() { + load(TransactionManagersConfiguration.class, "spring.aop.proxy-target-class=false"); + assertThat(this.context.getBean(AnotherService.class) + .isTransactionActive()).isTrue(); + assertThat(this.context.getBeansOfType(AnotherServiceImpl.class)).hasSize(0); + assertThat(this.context.getBeansOfType(TransactionalServiceImpl.class)).hasSize(0); + } + + private void load(Class config, String... environment) { + AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); + applicationContext.register(config); + applicationContext.register(EnableTransactionManagementAutoConfiguration.class); + EnvironmentTestUtils.addEnvironment(applicationContext, environment); + applicationContext.refresh(); + this.context = applicationContext; + } + + @Configuration + static class BaseConfiguration { + + @Bean + public TransactionalService transactionalService() { + return new TransactionalServiceImpl(); + } + + @Bean + public AnotherServiceImpl anotherService() { + return new AnotherServiceImpl(); + } + } + + @Configuration + @Import(BaseConfiguration.class) + static class TransactionManagersConfiguration { + + @Bean + public DataSourceTransactionManager transactionManager() { + return new DataSourceTransactionManager(dataSource()); + } + + @Bean + public DataSource dataSource() { + return DataSourceBuilder.create() + .driverClassName("org.hsqldb.jdbc.JDBCDriver") + .url("jdbc:hsqldb:mem:tx").username("sa").build(); + } + + } + + interface TransactionalService { + + @Transactional + boolean isTransactionActive(); + + } + + static class TransactionalServiceImpl implements TransactionalService { + + + @Override + public boolean isTransactionActive() { + return TransactionSynchronizationManager.isActualTransactionActive(); + } + } + + interface AnotherService { + + boolean isTransactionActive(); + + } + + static class AnotherServiceImpl implements AnotherService { + + + @Override + @Transactional + public boolean isTransactionActive() { + return TransactionSynchronizationManager.isActualTransactionActive(); + } + } + +} diff --git a/spring-boot-samples/spring-boot-sample-data-gemfire/src/main/java/sample/data/gemfire/SampleDataGemFireApplication.java b/spring-boot-samples/spring-boot-sample-data-gemfire/src/main/java/sample/data/gemfire/SampleDataGemFireApplication.java index 53d85f89ce9c..38bb60ee8ca4 100644 --- a/spring-boot-samples/spring-boot-sample-data-gemfire/src/main/java/sample/data/gemfire/SampleDataGemFireApplication.java +++ b/spring-boot-samples/spring-boot-sample-data-gemfire/src/main/java/sample/data/gemfire/SampleDataGemFireApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -32,7 +32,6 @@ import org.springframework.data.gemfire.RegionAttributesFactoryBean; import org.springframework.data.gemfire.ReplicatedRegionFactoryBean; import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories; -import org.springframework.transaction.annotation.EnableTransactionManagement; /** * The GemstoneAppConfiguration class for allowing Spring Boot to pick up additional @@ -43,7 +42,6 @@ */ @SpringBootApplication @EnableGemfireRepositories -@EnableTransactionManagement @EnableConfigurationProperties(SampleDataGemFireProperties.class) public class SampleDataGemFireApplication { diff --git a/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories index 722162a648a6..b1325e916dd2 100644 --- a/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories @@ -11,6 +11,7 @@ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConf org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ +org.springframework.boot.autoconfigure.transaction.EnableTransactionManagementAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration # AutoConfigureJson auto-configuration imports