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:
parent
4f3cbb45f4
commit
d1f6672a58
|
|
@ -16,61 +16,37 @@
|
||||||
|
|
||||||
package org.springframework.cache.annotation;
|
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.AdviceMode;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.AdviceModeImportSelector;
|
||||||
import org.springframework.context.annotation.ImportSelectorContext;
|
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||||
import org.springframework.context.annotation.ImportSelector;
|
import org.springframework.context.annotation.AutoProxyRegistrar;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selects which implementation of {@link AbstractCachingConfiguration} should be used
|
* 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.
|
* class.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
* @see EnableCaching
|
* @see EnableCaching
|
||||||
* @see AbstractCachingConfiguration
|
|
||||||
* @see ProxyCachingConfiguration
|
* @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}
|
* {@inheritDoc}
|
||||||
* <p>This implementation selects {@link ProxyCachingConfiguration} if
|
* @return {@link ProxyCachingConfiguration} or {@code AspectJCacheConfiguration} for
|
||||||
* {@link EnableCaching#mode()} equals {@code PROXY}, and otherwise selects
|
* {@code PROXY} and {@code ASPECTJ} values of {@link EnableCaching#mode()}, respectively
|
||||||
* {@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.
|
|
||||||
*/
|
*/
|
||||||
public String[] selectImports(ImportSelectorContext context) {
|
public String[] selectImports(AdviceMode adviceMode) {
|
||||||
AnnotationMetadata importingClassMetadata = context.getImportingClassMetadata();
|
switch (adviceMode) {
|
||||||
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")) {
|
|
||||||
case PROXY:
|
case PROXY:
|
||||||
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
|
return new String[] { AutoProxyRegistrar.class.getName(), ProxyCachingConfiguration.class.getName() };
|
||||||
if ((Boolean)enableCaching.get("proxyTargetClass")) {
|
|
||||||
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
|
|
||||||
}
|
|
||||||
return new String[] { ProxyCachingConfiguration.class.getName() };
|
|
||||||
case ASPECTJ:
|
case ASPECTJ:
|
||||||
return new String[] {"org.springframework.cache.aspectj.AspectJCachingConfiguration"};
|
return new String[] { AnnotationConfigUtils.CACHE_ASPECT_CONFIGURATION_CLASS_NAME };
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown AdviceMode " + enableCaching.get("mode"));
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,10 @@ import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvi
|
||||||
import org.springframework.cache.interceptor.CacheInterceptor;
|
import org.springframework.cache.interceptor.CacheInterceptor;
|
||||||
import org.springframework.cache.interceptor.CacheOperationSource;
|
import org.springframework.cache.interceptor.CacheOperationSource;
|
||||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||||
|
import org.springframework.context.annotation.AutoProxyRegistrar;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.annotation.Role;
|
import org.springframework.context.annotation.Role;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -119,6 +119,12 @@ public class AnnotationConfigUtils {
|
||||||
public static final String CACHE_ASPECT_CLASS_NAME =
|
public static final String CACHE_ASPECT_CLASS_NAME =
|
||||||
"org.springframework.cache.aspectj.AnnotationCacheAspect";
|
"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.
|
* The bean name of the internally managed JPA annotation processor.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -31,15 +31,15 @@ import org.springframework.core.type.AnnotationMetadata;
|
||||||
* @see EnableAspectJAutoProxy
|
* @see EnableAspectJAutoProxy
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
public class AspectJAutoProxyConfigurationSelector implements ImportSelector {
|
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register, escalate, and configure the AspectJ auto proxy creator. Always return
|
* Register, escalate, and configure the AspectJ auto proxy creator based on the value
|
||||||
* an empty array, as no actual {@code @Configuration} classes are required.
|
* of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
|
||||||
|
* {@code @Configuration} class.
|
||||||
*/
|
*/
|
||||||
public String[] selectImports(ImportSelectorContext context) {
|
public void registerBeanDefinitions(
|
||||||
BeanDefinitionRegistry registry = context.getBeanDefinitionRegistry();
|
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||||
AnnotationMetadata importingClassMetadata = context.getImportingClassMetadata();
|
|
||||||
|
|
||||||
Map<String, Object> enableAJAutoProxy =
|
Map<String, Object> enableAJAutoProxy =
|
||||||
importingClassMetadata.getAnnotationAttributes(EnableAspectJAutoProxy.class.getName());
|
importingClassMetadata.getAnnotationAttributes(EnableAspectJAutoProxy.class.getName());
|
||||||
|
|
@ -49,8 +49,6 @@ public class AspectJAutoProxyConfigurationSelector implements ImportSelector {
|
||||||
if ((Boolean)enableAJAutoProxy.get("proxyTargetClass")) {
|
if ((Boolean)enableAJAutoProxy.get("proxyTargetClass")) {
|
||||||
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
|
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new String[] { };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -338,9 +338,9 @@ class ConfigurationClassBeanDefinitionReader {
|
||||||
private static class InvalidConfigurationImportProblem extends Problem {
|
private static class InvalidConfigurationImportProblem extends Problem {
|
||||||
public InvalidConfigurationImportProblem(String className, Resource resource, AnnotationMetadata metadata) {
|
public InvalidConfigurationImportProblem(String className, Resource resource, AnnotationMetadata metadata) {
|
||||||
super(String.format("%s was @Import'ed but is not annotated with @Configuration " +
|
super(String.format("%s was @Import'ed but is not annotated with @Configuration " +
|
||||||
"nor does it declare any @Bean methods. Update the class to " +
|
"nor does it declare any @Bean methods; it does not implement ImportSelector " +
|
||||||
"meet one of these requirements or do not attempt to @Import it.", className),
|
"or extend ImportBeanDefinitionRegistrar. Update the class to meet one of these requirements " +
|
||||||
new Location(resource, metadata));
|
"or do not attempt to @Import it.", className), new Location(resource, metadata));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,8 +100,8 @@ import java.lang.annotation.Target;
|
||||||
*/
|
*/
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Import(AspectJAutoProxyConfigurationSelector.class)
|
|
||||||
@Documented
|
@Documented
|
||||||
|
@Import(AspectJAutoProxyRegistrar.class)
|
||||||
public @interface EnableAspectJAutoProxy {
|
public @interface EnableAspectJAutoProxy {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -110,4 +110,4 @@ public @interface EnableAspectJAutoProxy {
|
||||||
*/
|
*/
|
||||||
boolean proxyTargetClass() default false;
|
boolean proxyTargetClass() default false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -26,15 +26,18 @@ import java.lang.annotation.Target;
|
||||||
* Indicates one or more @{@link Configuration} classes to import.
|
* Indicates one or more @{@link Configuration} classes to import.
|
||||||
*
|
*
|
||||||
* <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
|
* <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
|
||||||
* Only supported for actual {@code @Configuration}-annotated classes and implementations
|
* Only supported for classes annotated with {@code @Configuration} or declaring at least
|
||||||
* of the {@link ImportSelector} interface.
|
* 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
|
* 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.
|
* be autowired, or the configuration class instance declaring the bean can be autowired.
|
||||||
* The latter approach allows for explicit, IDE-friendly navigation between
|
* The latter approach allows for explicit, IDE-friendly navigation between
|
||||||
* {@code @Configuration} class methods.
|
* {@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
|
* <p>If XML or other non-{@code @Configuration} bean definition resources need to be
|
||||||
* imported, use @{@link ImportResource}
|
* imported, use @{@link ImportResource}
|
||||||
*
|
*
|
||||||
|
|
@ -44,7 +47,7 @@ import java.lang.annotation.Target;
|
||||||
* @see ImportSelector
|
* @see ImportSelector
|
||||||
* @see ImportResource
|
* @see ImportResource
|
||||||
*/
|
*/
|
||||||
@Target({ElementType.TYPE})
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
public @interface Import {
|
public @interface Import {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -16,30 +16,25 @@
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface to be implemented by types that determine which @{@link Configuration}
|
* 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
|
* class(es) should be imported based on a given selection criteria, usually one or more
|
||||||
* annotation attributes.
|
* 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
|
* @author Chris Beams
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
* @see Import
|
* @see Import
|
||||||
|
* @see ImportBeanDefinitionRegistrar
|
||||||
* @see Configuration
|
* @see Configuration
|
||||||
*/
|
*/
|
||||||
public interface ImportSelector {
|
public interface ImportSelector {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select and return the names of which class(es) should be imported based on
|
* Select and return the names of which class(es) should be imported based on
|
||||||
* the {@code AnnotationMetadata} of the importing {@code @Configuration} class and
|
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
|
||||||
* 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}.
|
|
||||||
*/
|
*/
|
||||||
String[] selectImports(ImportSelectorContext context);
|
String[] selectImports(AnnotationMetadata importingClassMetadata);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -16,52 +16,35 @@
|
||||||
|
|
||||||
package org.springframework.scheduling.annotation;
|
package org.springframework.scheduling.annotation;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.AdviceMode;
|
import org.springframework.context.annotation.AdviceMode;
|
||||||
|
import org.springframework.context.annotation.AdviceModeImportSelector;
|
||||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
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}
|
* Selects which implementation of {@link AbstractAsyncConfiguration} should be used based
|
||||||
* should be used based on the value of {@link EnableAsync#mode} on the
|
* on the value of {@link EnableAsync#mode} on the importing {@code @Configuration} class.
|
||||||
* importing @{@link Configuration} class.
|
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
* @see EnableAsync
|
* @see EnableAsync
|
||||||
* @see AbstractAsyncConfiguration
|
|
||||||
* @see ProxyAsyncConfiguration
|
* @see ProxyAsyncConfiguration
|
||||||
* @see AnnotationConfigUtils#ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME
|
* @see AnnotationConfigUtils#ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME
|
||||||
*/
|
*/
|
||||||
public class AsyncConfigurationSelector implements ImportSelector {
|
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
* <p>This implementation selects {@link ProxyAsyncConfiguration} if
|
* @return {@link ProxyAsyncConfiguration} or {@code AspectJAsyncConfiguration} for
|
||||||
* {@link EnableAsync#mode()} equals {@code PROXY}, and otherwise selects
|
* {@code PROXY} and {@code ASPECTJ} values of {@link EnableAsync#mode()}, respectively
|
||||||
* {@link org.springframework.scheduling.aspectj.AspectJAsyncConfiguration
|
|
||||||
* AspectJAsyncConfiguration}. No additional {@code BeanDefinition}s are registered
|
|
||||||
* in either case.
|
|
||||||
*/
|
*/
|
||||||
public String[] selectImports(ImportSelectorContext context) {
|
public String[] selectImports(AdviceMode adviceMode) {
|
||||||
AnnotationMetadata importingClassMetadata = context.getImportingClassMetadata();
|
switch (adviceMode) {
|
||||||
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")) {
|
|
||||||
case PROXY:
|
case PROXY:
|
||||||
return new String[] {ProxyAsyncConfiguration.class.getName()};
|
return new String[] { ProxyAsyncConfiguration.class.getName() };
|
||||||
case ASPECTJ:
|
case ASPECTJ:
|
||||||
return new String[] {AnnotationConfigUtils.ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
|
return new String[] { AnnotationConfigUtils.ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME };
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown AdviceMode " + enableAsync.get("mode"));
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,63 +16,42 @@
|
||||||
|
|
||||||
package org.springframework.transaction.annotation;
|
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.AdviceMode;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.AdviceModeImportSelector;
|
||||||
import org.springframework.context.annotation.ImportSelectorContext;
|
import org.springframework.context.annotation.AutoProxyRegistrar;
|
||||||
import org.springframework.context.annotation.ImportSelector;
|
import org.springframework.transaction.config.TransactionManagementConfigUtils;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selects which implementation of {@link AbstractTransactionManagementConfiguration}
|
* Selects which implementation of {@link AbstractTransactionManagementConfiguration}
|
||||||
* should be used based on the value of {@link EnableTransactionManagement#mode} on the
|
* should be used based on the value of {@link EnableTransactionManagement#mode} on the
|
||||||
* importing @{@link Configuration} class.
|
* importing {@code @Configuration} class.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
* @see EnableTransactionManagement
|
* @see EnableTransactionManagement
|
||||||
* @see AbstractTransactionManagementConfiguration
|
|
||||||
* @see ProxyTransactionManagementConfiguration
|
* @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}
|
* {@inheritDoc}
|
||||||
* <p>This implementation selects {@link ProxyTransactionManagementConfiguration} if
|
* @return {@link ProxyTransactionManagementConfiguration} or
|
||||||
* {@link EnableTransactionManagement#mode()} equals {@code PROXY}, and otherwise selects
|
* {@code AspectJTransactionManagementConfiguration} for {@code PROXY} and
|
||||||
* {@link org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration
|
* {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, respectively
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
public String[] selectImports(ImportSelectorContext context) {
|
@Override
|
||||||
AnnotationMetadata importingClassMetadata = context.getImportingClassMetadata();
|
protected String[] selectImports(AdviceMode adviceMode) {
|
||||||
BeanDefinitionRegistry registry = context.getBeanDefinitionRegistry();
|
switch (adviceMode) {
|
||||||
|
|
||||||
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")) {
|
|
||||||
case PROXY:
|
case PROXY:
|
||||||
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
|
return new String[] { AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName() };
|
||||||
if ((Boolean)enableTx.get("proxyTargetClass")) {
|
|
||||||
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
|
|
||||||
}
|
|
||||||
return new String[] { ProxyTransactionManagementConfiguration.class.getName() };
|
|
||||||
case ASPECTJ:
|
case ASPECTJ:
|
||||||
return new String[] {"org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration"};
|
return new String[] { TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME };
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown AdviceMode " + enableTx.get("mode"));
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,4 +36,10 @@ public abstract class TransactionManagementConfigUtils {
|
||||||
public static final String TRANSACTION_ASPECT_CLASS_NAME =
|
public static final String TRANSACTION_ASPECT_CLASS_NAME =
|
||||||
"org.springframework.transaction.aspectj.AnnotationTransactionAspect";
|
"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";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue