diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index 036766d2cea..a09f033833e 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -84,6 +84,8 @@ class ConfigurationClassParser { private final ResourceLoader resourceLoader; + private final BeanDefinitionRegistry registry; + private final ComponentScanAnnotationParser componentScanParser; @@ -98,8 +100,9 @@ class ConfigurationClassParser { this.problemReporter = problemReporter; this.environment = environment; this.resourceLoader = resourceLoader; - - this.componentScanParser = new ComponentScanAnnotationParser(this.resourceLoader, this.environment, registry); + this.registry = registry; + this.componentScanParser = + new ComponentScanAnnotationParser(this.resourceLoader, this.environment, this.registry); } @@ -245,7 +248,8 @@ class ConfigurationClassParser { // the candidate class is an ImportSelector -> delegate to it to determine imports try { ImportSelector selector = BeanUtils.instantiateClass(Class.forName(candidate), ImportSelector.class); - processImport(configClass, selector.selectImports(importingClassMetadata), false); + ImportSelector.Context context = new ImportSelector.Context(importingClassMetadata, this.registry); + processImport(configClass, selector.selectImports(context), false); } catch (ClassNotFoundException ex) { throw new IllegalStateException(ex); } diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportSelector.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportSelector.java index 5cc718f867a..76920bc97e9 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportSelector.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportSelector.java @@ -16,6 +16,7 @@ package org.springframework.context.annotation; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.core.type.AnnotationMetadata; /** @@ -23,6 +24,10 @@ import org.springframework.core.type.AnnotationMetadata; * class(es) should be imported based on a given selection criteria, usually one or more * annotation attributes. * + *

In certain cases, an {@code ImportSelector} may register additional bean definitions + * through the {@link BeanDefinitionRegistry} available in the + * {@code Context} provided to the {@link #selectImports} method. + * * @author Chris Beams * @since 3.1 * @see Import @@ -31,10 +36,41 @@ import org.springframework.core.type.AnnotationMetadata; public interface ImportSelector { /** - * Select and return the names of which class(es) should be imported. - * @param importingClassMetadata the AnnotationMetodata of the - * importing @{@link Configuration} class. + * Select and return the names of which class(es) should be imported based on + * the {@code AnnotationMetadata} of the importing {@code @Configuration} class and + * optionally register any {@code BeanDefinition}s necessary to support the selected + * classes. + * @param context containing the AnnotationMetadata of the importing @{@link + * Configuration} class and the enclosing {@link BeanDefinitionRegistry}. + */ + String[] selectImports(Context context); + + + /** + * Context object holding the {@link AnnotationMetadata} of the {@code @Configuration} + * class that imported this {@link ImportSelector} as well as the enclosing + * {@link BeanDefinitionRegistry} to allow for conditional bean definition + * registration when necessary. + * + * @author Chris Beams + * @since 3.1 */ - String[] selectImports(AnnotationMetadata importingClassMetadata); + static class Context { + private final AnnotationMetadata importingClassMetadata; + private final BeanDefinitionRegistry registry; + + public Context(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + this.importingClassMetadata = importingClassMetadata; + this.registry = registry; + } + + public AnnotationMetadata getImportingClassMetadata() { + return this.importingClassMetadata; + } + + public BeanDefinitionRegistry getBeanDefinitionRegistry() { + return registry; + } + } } diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurationSelector.java b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurationSelector.java index 41539bb6f16..9f5791b46f6 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurationSelector.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/annotation/AsyncConfigurationSelector.java @@ -39,12 +39,15 @@ import org.springframework.util.Assert; public class AsyncConfigurationSelector implements ImportSelector { /** - * Import {@link ProxyAsyncConfiguration} if {@link EnableAsync#mode()} equals - * {@code PROXY}, otherwise import + * {@inheritDoc} + *

This implementation selects {@link ProxyAsyncConfiguration} if + * {@link EnableAsync#mode()} equals {@code PROXY}, and otherwise selects * {@link org.springframework.scheduling.aspectj.AspectJAsyncConfiguration - * AspectJAsyncConfiguration}. + * AspectJAsyncConfiguration}. No additional {@code BeanDefinition}s are registered + * in either case. */ - public String[] selectImports(AnnotationMetadata importingClassMetadata) { + public String[] selectImports(ImportSelector.Context context) { + AnnotationMetadata importingClassMetadata = context.getImportingClassMetadata(); Map enableAsync = importingClassMetadata.getAnnotationAttributes(EnableAsync.class.getName()); Assert.notNull(enableAsync, diff --git a/org.springframework.integration-tests/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementIntegrationTests.java b/org.springframework.integration-tests/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementIntegrationTests.java index e5e59cdee57..51bcaeebf5f 100644 --- a/org.springframework.integration-tests/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementIntegrationTests.java +++ b/org.springframework.integration-tests/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementIntegrationTests.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -32,9 +33,14 @@ import org.junit.Test; import org.springframework.aop.Advisor; import org.springframework.aop.framework.Advised; import org.springframework.aop.support.AopUtils; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.concurrent.ConcurrentMapCache; +import org.springframework.cache.support.SimpleCacheManager; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportResource; import org.springframework.context.config.AdviceMode; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; @@ -152,6 +158,38 @@ public class EnableTransactionManagementIntegrationTests { assertThat(txManager2.rollbacks, equalTo(0)); } + @Test + public void apcEscalation() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(EnableTxAndCachingConfig.class); + ctx.refresh(); + } + + + @Configuration + @EnableTransactionManagement + @ImportResource("org/springframework/transaction/annotation/enable-caching.xml") + static class EnableTxAndCachingConfig { + @Bean + public PlatformTransactionManager txManager() { + return new CallCountingTransactionManager(); + } + + @Bean + public FooRepository fooRepository() { + return new DummyFooRepository(); + } + + @Bean + public CacheManager cacheManager() { + SimpleCacheManager mgr = new SimpleCacheManager(); + ArrayList caches = new ArrayList(); + caches.add(new ConcurrentMapCache()); + mgr.setCaches(caches); + return mgr; + } + } + @Configuration @EnableTransactionManagement diff --git a/org.springframework.integration-tests/src/test/resources/org/springframework/transaction/annotation/enable-caching.xml b/org.springframework.integration-tests/src/test/resources/org/springframework/transaction/annotation/enable-caching.xml new file mode 100644 index 00000000000..efc9ec29f30 --- /dev/null +++ b/org.springframework.integration-tests/src/test/resources/org/springframework/transaction/annotation/enable-caching.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/org.springframework.transaction/src/main/java/org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.java b/org.springframework.transaction/src/main/java/org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.java index 2dca350475b..48a6436ace1 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.java +++ b/org.springframework.transaction/src/main/java/org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.java @@ -16,8 +16,6 @@ package org.springframework.transaction.annotation; -import org.springframework.aop.config.AopConfigUtils; -import org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -67,12 +65,4 @@ public class ProxyTransactionManagementConfiguration extends AbstractTransaction return interceptor; } - // TODO: deal with escalation of APCs - @Bean(name=AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME) - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - public InfrastructureAdvisorAutoProxyCreator apc() { - InfrastructureAdvisorAutoProxyCreator apc = new InfrastructureAdvisorAutoProxyCreator(); - apc.setProxyTargetClass((Boolean) this.enableTx.get("proxyTargetClass")); - return apc; - } } diff --git a/org.springframework.transaction/src/main/java/org/springframework/transaction/annotation/TransactionManagementConfigurationSelector.java b/org.springframework.transaction/src/main/java/org/springframework/transaction/annotation/TransactionManagementConfigurationSelector.java index 69ce2b0b5fd..56290a8c00b 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/transaction/annotation/TransactionManagementConfigurationSelector.java +++ b/org.springframework.transaction/src/main/java/org/springframework/transaction/annotation/TransactionManagementConfigurationSelector.java @@ -18,6 +18,9 @@ package org.springframework.transaction.annotation; import java.util.Map; +import org.springframework.aop.config.AopConfigUtils; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportSelector; import org.springframework.context.config.AdviceMode; import org.springframework.core.type.AnnotationMetadata; @@ -38,12 +41,19 @@ import org.springframework.util.Assert; public class TransactionManagementConfigurationSelector implements ImportSelector { /** - * Import {@link ProxyTransactionManagementConfiguration} if {@link - * EnableTransactionManagement#mode()} equals {@code PROXY}, otherwise import {@link - * org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration + * {@inheritDoc} + *

This implementation selects {@link ProxyTransactionManagementConfiguration} if + * {@link EnableTransactionManagement#mode()} equals {@code PROXY}, and otherwise selects + * {@link org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration * AspectJTransactionManagementConfiguration}. + *

If {@code #mode()} equals {@code PROXY}, an auto-proxy creator bean definition + * will also be added to the enclosing {@link BeanDefinitionRegistry} and escalated + * if necessary through the usual {@link AopConfigUtils} family of methods. */ - public String[] selectImports(AnnotationMetadata importingClassMetadata) { + public String[] selectImports(ImportSelector.Context context) { + AnnotationMetadata importingClassMetadata = context.getImportingClassMetadata(); + BeanDefinitionRegistry registry = context.getBeanDefinitionRegistry(); + Map enableTx = importingClassMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName()); Assert.notNull(enableTx, @@ -52,7 +62,11 @@ public class TransactionManagementConfigurationSelector implements ImportSelecto switch ((AdviceMode) enableTx.get("mode")) { case PROXY: - return new String[] {ProxyTransactionManagementConfiguration.class.getName()}; + AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); + if ((Boolean)enableTx.get("proxyTargetClass")) { + AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); + } + return new String[] { ProxyTransactionManagementConfiguration.class.getName() }; case ASPECTJ: return new String[] {"org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration"}; default: