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
This commit is contained in:
Stephane Nicoll 2017-04-02 16:52:58 +02:00
parent 751cd6e0b2
commit db33a75484
6 changed files with 156 additions and 40 deletions

View File

@ -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 {
}
}

View File

@ -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 {

View File

@ -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 {
}
}
}

View File

@ -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 {

View File

@ -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();
}
}
}

View File

@ -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 {