Refactor ImportSelector support

Separate concerns of @Configuration class selection from the need to
register certain infrastructure beans such as auto proxy creators.

Prior to this change, ImportSelector implementations were responsible
for both of these concerns, leading to awkwardness and duplication.

Also introduced in this change is ImportBeanDefinitionRegistrar and
two implementations, AutoProxyRegistrar and AspectJAutoProxyRegistrar.
See the refactored implementations of CachingConfigurationSelector,
TransactionManagementConfigurationSelector to see the former;
AspectJAutoProxyConfigurationSelector to see the latter.

ImportSelector and ImportBeanDefinitionRegistrar are both handled as
special-case arguments to the @Import annotation within
ConfigurationClassParser.

These refactorings are important because they ensure that Spring users
will be able to understand and extend existing @Enable* annotations
and their backing ImportSelector and @Configuration classes, as well
as create their own with a minimum of effort.
This commit is contained in:
Chris Beams 2011-11-16 04:21:28 +00:00
parent 4f3cbb45f4
commit d1f6672a58
15 changed files with 311 additions and 163 deletions

View File

@ -16,61 +16,37 @@
package org.springframework.cache.annotation;
import java.util.Map;
import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportSelectorContext;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.AutoProxyRegistrar;
/**
* Selects which implementation of {@link AbstractCachingConfiguration} should be used
* based on the value of {@link EnableCaching#mode} on the importing @{@link Configuration}
* based on the value of {@link EnableCaching#mode} on the importing {@code @Configuration}
* class.
*
* @author Chris Beams
* @since 3.1
* @see EnableCaching
* @see AbstractCachingConfiguration
* @see ProxyCachingConfiguration
* @see org.springframework.cache.aspectj.AspectJCachingConfiguration
* @see AnnotationConfigUtils.CACHE_ASPECT_CONFIGURATION_CLASS_NAME
*/
public class CachingConfigurationSelector implements ImportSelector {
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
/**
* {@inheritDoc}
* <p>This implementation selects {@link ProxyCachingConfiguration} if
* {@link EnableCaching#mode()} equals {@code PROXY}, and otherwise selects
* {@link org.springframework.cache.aspectj.AspectJCachingConfiguration AspectJCacheConfiguration}.
* <p>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.
* @return {@link ProxyCachingConfiguration} or {@code AspectJCacheConfiguration} for
* {@code PROXY} and {@code ASPECTJ} values of {@link EnableCaching#mode()}, respectively
*/
public String[] selectImports(ImportSelectorContext context) {
AnnotationMetadata importingClassMetadata = context.getImportingClassMetadata();
BeanDefinitionRegistry registry = context.getBeanDefinitionRegistry();
Map<String, Object> enableCaching =
importingClassMetadata.getAnnotationAttributes(EnableCaching.class.getName());
Assert.notNull(enableCaching,
"@EnableCaching is not present on importing class " +
importingClassMetadata.getClassName());
switch ((AdviceMode) enableCaching.get("mode")) {
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean)enableCaching.get("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
return new String[] { ProxyCachingConfiguration.class.getName() };
return new String[] { AutoProxyRegistrar.class.getName(), ProxyCachingConfiguration.class.getName() };
case ASPECTJ:
return new String[] {"org.springframework.cache.aspectj.AspectJCachingConfiguration"};
return new String[] { AnnotationConfigUtils.CACHE_ASPECT_CONFIGURATION_CLASS_NAME };
default:
throw new IllegalArgumentException("Unknown AdviceMode " + enableCaching.get("mode"));
return null;
}
}

View File

@ -21,8 +21,10 @@ import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvi
import org.springframework.cache.interceptor.CacheInterceptor;
import org.springframework.cache.interceptor.CacheOperationSource;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.AutoProxyRegistrar;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Role;
/**

View File

@ -0,0 +1,107 @@
/*
* Copyright 2002-2011 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.context.annotation;
import java.lang.annotation.Annotation;
import java.util.Map;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
/**
* Convenient base class for {@link ImportSelector} implementations that select imports
* based on an {@link AdviceMode} value from an annotation (such as the {@code @Enable*}
* annotations).
*
* @param <A> Annotation containing {@linkplain #getAdviceModeAttributeName() AdviceMode
* attribute}
*
* @author Chris Beams
* @since 3.1
*/
public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector {
public static final String DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME = "mode";
/**
* The name of the {@link AdviceMode} attribute for the annotation specified by the
* generic type {@code A}. The default is {@value #DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME},
* but subclasses may override in order to customize.
*/
protected String getAdviceModeAttributeName() {
return DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME;
}
/**
* {@inheritDoc}
*
* <p>This implementation resolves the type of annotation from generic metadata and
* validates that (a) the annotation is in fact present on the importing
* {@code @Configuration} class and (b) that the given annotation has an
* {@linkplain #getAdviceModeAttributeName() advice mode attribute} of type
* {@link AdviceMode}.
*
* <p>The {@link #selectImports(AdviceMode)} method is then invoked, allowing the
* concrete implementation to choose imports in a safe and convenient fashion.
*
* @throws IllegalArgumentException if expected annotation {@code A} is not present
* on the importing {@code @Configuration} class or if {@link #selectImports(AdviceMode)}
* returns {@code null}
*/
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
Class<?> annoType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(annoType.getName());
Assert.notNull(attributes, String.format(
"@%s is not present on importing class '%s' as expected",
annoType.getSimpleName(), importingClassMetadata.getClassName()));
String modeAttrName = getAdviceModeAttributeName();
Assert.hasText(modeAttrName);
Object adviceMode = attributes.get(modeAttrName);
Assert.notNull(adviceMode, String.format(
"Advice mode attribute @%s#%s() does not exist",
annoType.getSimpleName(), modeAttrName));
Assert.isInstanceOf(AdviceMode.class, adviceMode, String.format(
"Incorrect type for advice mode attribute '@%s#%s()': ",
annoType.getSimpleName(), modeAttrName));
String[] imports = selectImports((AdviceMode) adviceMode);
Assert.notNull(imports, String.format("Unknown AdviceMode: '%s'", adviceMode));
return imports;
}
/**
* Determine which classes should be imported based on the given {@code AdviceMode}.
*
* <p>Returning {@code null} from this method indicates that the {@code AdviceMode} could
* not be handled or was unknown and that an {@code IllegalArgumentException} should
* be thrown.
*
* @param adviceMode the value of the {@linkplain #getAdviceModeAttributeName()
* advice mode attribute} for the annotation specified via generics.
*
* @return array containing classes to import; empty array if none, {@code null} if
* the given {@code AdviceMode} is unknown.
*/
protected abstract String[] selectImports(AdviceMode adviceMode);
}

View File

@ -119,6 +119,12 @@ public class AnnotationConfigUtils {
public static final String CACHE_ASPECT_CLASS_NAME =
"org.springframework.cache.aspectj.AnnotationCacheAspect";
/**
* The name of the AspectJ caching aspect @{@code Configuration} class.
*/
public static final String CACHE_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.cache.aspectj.AspectJCachingConfiguration";
/**
* The bean name of the internally managed JPA annotation processor.
*/

View File

@ -31,15 +31,15 @@ import org.springframework.core.type.AnnotationMetadata;
* @see EnableAspectJAutoProxy
* @since 3.1
*/
public class AspectJAutoProxyConfigurationSelector implements ImportSelector {
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
/**
* Register, escalate, and configure the AspectJ auto proxy creator. Always return
* an empty array, as no actual {@code @Configuration} classes are required.
* Register, escalate, and configure the AspectJ auto proxy creator based on the value
* of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
* {@code @Configuration} class.
*/
public String[] selectImports(ImportSelectorContext context) {
BeanDefinitionRegistry registry = context.getBeanDefinitionRegistry();
AnnotationMetadata importingClassMetadata = context.getImportingClassMetadata();
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> enableAJAutoProxy =
importingClassMetadata.getAnnotationAttributes(EnableAspectJAutoProxy.class.getName());
@ -49,8 +49,6 @@ public class AspectJAutoProxyConfigurationSelector implements ImportSelector {
if ((Boolean)enableAJAutoProxy.get("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
return new String[] { };
}
}

View File

@ -0,0 +1,72 @@
package org.springframework.context.annotation;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.type.AnnotationMetadata;
/**
* Registers an auto proxy creator against the current {@link BeanDefinitionRegistry}
* as appropriate based on an {@code @Enable*} annotation having {@code mode} and
* {@code proxyTargetClass} attributes set to the correct values.
*
* @author Chris Beams
* @see EnableAspectJAutoProxy
* @since 3.1
*/
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
private final Log logger = LogFactory.getLog(getClass());
/**
* Register, escalate, and configure the standard auto proxy creator (APC) against the
* given registry. Works by finding the nearest annotation declared on the importing
* {@code @Configuration} class that has both {@code mode} and {@code proxyTargetClass}
* attributes. If {@code mode} is set to {@code PROXY}, the APC is registered; if
* {@code proxyTargetClass} is set to {@code true}, then the APC is forced to use
* subclass (CGLIB) proxying.
*
* <p>Several {@code @Enable*} annotations expose both {@code mode} and
* {@code proxyTargetClass} attributes. It is important to note that most of these
* capabilities end up sharing a {@linkplain AopConfigUtils#AUTO_PROXY_CREATOR_BEAN_NAME
* single APC}. For this reason, this implementation doesn't "care" exactly which
* annotation it finds -- as long as it exposes the right {@code mode} and
* {@code proxyTargetClass} attributes, the APC can be registered and configured all
* the same.
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
for (String annoType : annoTypes) {
Map<String, Object> candidate = importingClassMetadata.getAnnotationAttributes(annoType);
Object mode = candidate.get("mode");
Object proxyTargetClass = candidate.get("proxyTargetClass");
if (mode != null && proxyTargetClass != null
&& mode.getClass().equals(AdviceMode.class)
&& proxyTargetClass.getClass().equals(Boolean.class)) {
candidateFound = true;
if (mode == AdviceMode.PROXY) {
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean)proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
}
}
}
if (!candidateFound) {
String name = getClass().getSimpleName();
logger.warn(String.format("%s was imported but no annotations were found " +
"having both 'mode' and 'proxyTargetClass' attributes of type " +
"AdviceMode and boolean respectively. This means that auto proxy " +
"creator registration and configuration may not have occured as " +
"intended, and components may not be proxied as expected. Check to " +
"ensure that %s has been @Import'ed on the same class where these " +
"annotations are declared; otherwise remove the import of %s " +
"altogether.", name, name, name));
}
}
}

View File

@ -338,9 +338,9 @@ class ConfigurationClassBeanDefinitionReader {
private static class InvalidConfigurationImportProblem extends Problem {
public InvalidConfigurationImportProblem(String className, Resource resource, AnnotationMetadata metadata) {
super(String.format("%s was @Import'ed but is not annotated with @Configuration " +
"nor does it declare any @Bean methods. Update the class to " +
"meet one of these requirements or do not attempt to @Import it.", className),
new Location(resource, metadata));
"nor does it declare any @Bean methods; it does not implement ImportSelector " +
"or extend ImportBeanDefinitionRegistrar. Update the class to meet one of these requirements " +
"or do not attempt to @Import it.", className), new Location(resource, metadata));
}
}

View File

@ -100,8 +100,8 @@ import java.lang.annotation.Target;
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(AspectJAutoProxyConfigurationSelector.class)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
/**

View File

@ -26,15 +26,18 @@ import java.lang.annotation.Target;
* Indicates one or more @{@link Configuration} classes to import.
*
* <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
* Only supported for actual {@code @Configuration}-annotated classes and implementations
* of the {@link ImportSelector} interface.
* Only supported for classes annotated with {@code @Configuration} or declaring at least
* one {@link @Bean} method, as well as {@link ImportSelector} and
* {@link ImportBeanDefinitionRegistrar} implementations.
*
* <p>@{@link Bean} definitions declared in imported {@code @Configuration} classes
* <p>@{@code Bean} definitions declared in imported {@code @Configuration} classes
* should be accessed by using @{@link Autowired} injection. Either the bean itself can
* be autowired, or the configuration class instance declaring the bean can be autowired.
* The latter approach allows for explicit, IDE-friendly navigation between
* {@code @Configuration} class methods.
*
* <p>May be declared at the class level or as a meta-annotation.
*
* <p>If XML or other non-{@code @Configuration} bean definition resources need to be
* imported, use @{@link ImportResource}
*
@ -44,7 +47,7 @@ import java.lang.annotation.Target;
* @see ImportSelector
* @see ImportResource
*/
@Target({ElementType.TYPE})
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

View File

@ -0,0 +1,54 @@
/*
* Copyright 2002-2011 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.context.annotation;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.core.type.AnnotationMetadata;
/**
* Interface to be implemented by types that register additional bean definitions when
* processing @{@link Configuration} classes. Useful when operating at the bean definition
* level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
*
* <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type
* may be provided to the @{@link Import} annotation (or may also be returned from an
* {@code ImportSelector}).
*
* <p>See implementations and associated unit tests for usage examples.
*
* @author Chris Beams
* @since 3.1
* @see Import
* @see ImportSelector
* @see Configuration
*/
public interface ImportBeanDefinitionRegistrar {
/**
* Register bean definitions as necessary based on the given annotation metadata of
* the importing {@code @Configuration} class.
* <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
* registered here, due to lifecycle constraints related to {@code @Configuration}
* class processing.
* @param importingClassMetadata annotation metadata of the importing class
* @param registry current bean definition registry
*/
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}

View File

@ -16,30 +16,25 @@
package org.springframework.context.annotation;
import org.springframework.core.type.AnnotationMetadata;
/**
* Interface to be implemented by types that determine which @{@link Configuration}
* class(es) should be imported based on a given selection criteria, usually one or more
* annotation attributes.
*
* <p>In certain cases, an {@code ImportSelector} may register additional bean definitions
* through the {@code BeanDefinitionRegistry} available in the
* {@link ImportSelectorContext} provided to the {@link #selectImports} method.
*
* @author Chris Beams
* @since 3.1
* @see Import
* @see ImportBeanDefinitionRegistrar
* @see Configuration
*/
public interface ImportSelector {
/**
* 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 {@code AnnotationMetadata} of the importing @{@link
* Configuration} class and the enclosing {@code BeanDefinitionRegistry}.
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(ImportSelectorContext context);
String[] selectImports(AnnotationMetadata importingClassMetadata);
}

View File

@ -1,33 +0,0 @@
package org.springframework.context.annotation;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.type.AnnotationMetadata;
/**
* Context object holding the {@link AnnotationMetadata} of the @{@link Configuration}
* class that imported the current {@link ImportSelector} as well as the enclosing
* {@link BeanDefinitionRegistry} to allow for conditional bean definition
* registration when necessary.
*
* @author Chris Beams
* @since 3.1
* @see Import
* @see ImportSelector
*/
public class ImportSelectorContext {
private final AnnotationMetadata importingClassMetadata;
private final BeanDefinitionRegistry registry;
ImportSelectorContext(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
this.importingClassMetadata = importingClassMetadata;
this.registry = registry;
}
public AnnotationMetadata getImportingClassMetadata() {
return this.importingClassMetadata;
}
public BeanDefinitionRegistry getBeanDefinitionRegistry() {
return registry;
}
}

View File

@ -16,52 +16,35 @@
package org.springframework.scheduling.annotation;
import java.util.Map;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.ImportSelectorContext;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
/**
* Selects which implementation of {@link AbstractAsyncConfiguration}
* should be used based on the value of {@link EnableAsync#mode} on the
* importing @{@link Configuration} class.
* Selects which implementation of {@link AbstractAsyncConfiguration} should be used based
* on the value of {@link EnableAsync#mode} on the importing {@code @Configuration} class.
*
* @author Chris Beams
* @since 3.1
* @see EnableAsync
* @see AbstractAsyncConfiguration
* @see ProxyAsyncConfiguration
* @see AnnotationConfigUtils#ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME
*/
public class AsyncConfigurationSelector implements ImportSelector {
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
/**
* {@inheritDoc}
* <p>This implementation selects {@link ProxyAsyncConfiguration} if
* {@link EnableAsync#mode()} equals {@code PROXY}, and otherwise selects
* {@link org.springframework.scheduling.aspectj.AspectJAsyncConfiguration
* AspectJAsyncConfiguration}. No additional {@code BeanDefinition}s are registered
* in either case.
* @return {@link ProxyAsyncConfiguration} or {@code AspectJAsyncConfiguration} for
* {@code PROXY} and {@code ASPECTJ} values of {@link EnableAsync#mode()}, respectively
*/
public String[] selectImports(ImportSelectorContext context) {
AnnotationMetadata importingClassMetadata = context.getImportingClassMetadata();
Map<String, Object> enableAsync =
importingClassMetadata.getAnnotationAttributes(EnableAsync.class.getName());
Assert.notNull(enableAsync,
"@EnableAsync is not present on importing class " +
importingClassMetadata.getClassName());
switch ((AdviceMode) enableAsync.get("mode")) {
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] { ProxyAsyncConfiguration.class.getName() };
case ASPECTJ:
return new String[] { AnnotationConfigUtils.ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME };
default:
throw new IllegalArgumentException("Unknown AdviceMode " + enableAsync.get("mode"));
return null;
}
}

View File

@ -16,63 +16,42 @@
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.AdviceMode;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportSelectorContext;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.context.annotation.AutoProxyRegistrar;
import org.springframework.transaction.config.TransactionManagementConfigUtils;
/**
* Selects which implementation of {@link AbstractTransactionManagementConfiguration}
* should be used based on the value of {@link EnableTransactionManagement#mode} on the
* importing @{@link Configuration} class.
* importing {@code @Configuration} class.
*
* @author Chris Beams
* @since 3.1
* @see EnableTransactionManagement
* @see AbstractTransactionManagementConfiguration
* @see ProxyTransactionManagementConfiguration
* @see org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration
* @see TransactionManagementConfigUtils#TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME
*/
public class TransactionManagementConfigurationSelector implements ImportSelector {
public class TransactionManagementConfigurationSelector
extends AdviceModeImportSelector<EnableTransactionManagement> {
/**
* {@inheritDoc}
* <p>This implementation selects {@link ProxyTransactionManagementConfiguration} if
* {@link EnableTransactionManagement#mode()} equals {@code PROXY}, and otherwise selects
* {@link org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration
* AspectJTransactionManagementConfiguration}.
* <p>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.
* @return {@link ProxyTransactionManagementConfiguration} or
* {@code AspectJTransactionManagementConfiguration} for {@code PROXY} and
* {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, respectively
*/
public String[] selectImports(ImportSelectorContext context) {
AnnotationMetadata importingClassMetadata = context.getImportingClassMetadata();
BeanDefinitionRegistry registry = context.getBeanDefinitionRegistry();
Map<String, Object> enableTx =
importingClassMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName());
Assert.notNull(enableTx,
"@EnableTransactionManagement is not present on importing class " +
importingClassMetadata.getClassName());
switch ((AdviceMode) enableTx.get("mode")) {
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean)enableTx.get("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
return new String[] { ProxyTransactionManagementConfiguration.class.getName() };
return new String[] { AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName() };
case ASPECTJ:
return new String[] {"org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration"};
return new String[] { TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME };
default:
throw new IllegalArgumentException("Unknown AdviceMode " + enableTx.get("mode"));
return null;
}
}
}

View File

@ -36,4 +36,10 @@ public abstract class TransactionManagementConfigUtils {
public static final String TRANSACTION_ASPECT_CLASS_NAME =
"org.springframework.transaction.aspectj.AnnotationTransactionAspect";
/**
* The name of the AspectJ transaction management @{@code Configuration} class.
*/
public static final String TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration";
}