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 d19ea11c715..e3728123261 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. @@ -32,8 +32,6 @@ 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.annotation.AbstractTransactionManagementConfiguration; -import org.springframework.transaction.annotation.EnableTransactionManagement; /** * {@link EnableAutoConfiguration Auto-configuration} for @@ -79,11 +77,4 @@ public class DataSourceTransactionManagerAutoConfiguration { } - @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 80204020a77..d3a6c21e752 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. @@ -48,7 +48,6 @@ import org.springframework.jndi.JndiLocatorDelegate; 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; @@ -61,8 +60,7 @@ import org.springframework.util.ClassUtils; * @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/TransactionAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration.java index 43d503a2ba0..2c48e1f4843 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration.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. @@ -20,8 +20,10 @@ import java.util.List; import org.springframework.beans.factory.ObjectProvider; 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.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; @@ -30,6 +32,8 @@ 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.annotation.AbstractTransactionManagementConfiguration; +import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.support.TransactionTemplate; /** @@ -71,4 +75,25 @@ public class TransactionAutoConfiguration { } } + @Configuration + @ConditionalOnBean(PlatformTransactionManager.class) + @ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class) + public static class EnableTransactionManagementConfiguration { + + @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/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfigurationTests.java index 5a6af783d2d..816eea2ad00 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 @@ -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. @@ -28,7 +28,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; @@ -52,8 +51,6 @@ public class DataSourceTransactionManagerAutoConfigurationTests { 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 @@ -68,8 +65,7 @@ public class DataSourceTransactionManagerAutoConfigurationTests { @Test public void testManualConfiguration() throws Exception { - this.context.register(SwitchTransactionsOn.class, - EmbeddedDataSourceConfiguration.class, + this.context.register(EmbeddedDataSourceConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, TransactionAutoConfiguration.class); this.context.refresh(); @@ -79,8 +75,7 @@ public class DataSourceTransactionManagerAutoConfigurationTests { @Test public void testExistingTransactionManager() { - this.context.register(SwitchTransactionsOn.class, - TransactionManagerConfiguration.class, + this.context.register(TransactionManagerConfiguration.class, EmbeddedDataSourceConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, TransactionAutoConfiguration.class); @@ -99,8 +94,6 @@ public class DataSourceTransactionManagerAutoConfigurationTests { this.context.refresh(); assertThat(this.context.getBeansOfType(PlatformTransactionManager.class)) .isEmpty(); - assertThat(this.context.getBean(AbstractTransactionManagementConfiguration.class)) - .isNotNull(); } @Test @@ -130,11 +123,6 @@ public class DataSourceTransactionManagerAutoConfigurationTests { assertThat(transactionManager.isRollbackOnCommitFailure()).isTrue(); } - @EnableTransactionManagement - protected static class SwitchTransactionsOn { - - } - @Configuration protected static class TransactionManagerConfiguration { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfigurationTests.java index 1964c3e09f3..1e358085659 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/TransactionAutoConfigurationTests.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. @@ -19,17 +19,25 @@ package org.springframework.boot.autoconfigure.transaction; import java.util.List; import java.util.Map; +import javax.sql.DataSource; + import org.junit.After; import org.junit.Test; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; 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.test.util.ReflectionTestUtils; import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; import static org.assertj.core.api.Assertions.assertThat; @@ -60,8 +68,8 @@ public class TransactionAutoConfigurationTests { @Test public void singleTransactionManager() { - load(DataSourceAutoConfiguration.class, - DataSourceTransactionManagerAutoConfiguration.class); + load(new Class[] { DataSourceAutoConfiguration.class, + DataSourceTransactionManagerAutoConfiguration.class }); PlatformTransactionManager transactionManager = this.context .getBean(PlatformTransactionManager.class); TransactionTemplate transactionTemplate = this.context @@ -93,15 +101,53 @@ public class TransactionAutoConfigurationTests { List field = (List) ReflectionTestUtils.getField(customizers, "customizers"); assertThat(field).hasSize(1).first().isInstanceOf(TransactionProperties.class); - } - private void load(Class... configs) { + + @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); + } + + @Test + public void customEnableTransactionManagementTakesPrecedence() { + load(new Class[] { CustomTransactionManagementConfiguration.class, + TransactionManagersConfiguration.class }, + "spring.aop.proxy-target-class=true"); + 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) { + load(new Class[] { config }, environment); + } + + private void load(Class[] configs, String... environment) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(configs); applicationContext.register(TransactionAutoConfiguration.class); - EnvironmentTestUtils.addEnvironment(applicationContext, - "spring.datasource.initialize:false"); + EnvironmentTestUtils.addEnvironment(applicationContext, environment); applicationContext.refresh(); this.context = applicationContext; } @@ -141,4 +187,74 @@ public class TransactionAutoConfigurationTests { } + @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(); + } + + } + + @Configuration + @EnableTransactionManagement(proxyTargetClass = false) + static class CustomTransactionManagementConfiguration { + + } + + 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 53d85f89ce9..38bb60ee8ca 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.GemfireTransactionManager; 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 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; */ @SpringBootApplication @EnableGemfireRepositories -@EnableTransactionManagement @EnableConfigurationProperties(SampleDataGemFireProperties.class) public class SampleDataGemFireApplication {