From 9a271ce6c92695b9421aa603c9aa56e805c7920c Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Fri, 6 May 2011 19:06:02 +0000 Subject: [PATCH] Introduce ImportSelector interface Allows @Enable* a layer of indirection for deciding which @Configuration class(es) to @Import. The @Import annotation may now accept @Configuration class literals and/or ImportSelector class literals. --- .../annotation/ConfigurationClassParser.java | 29 ++++++++++---- .../context/annotation/ImportSelector.java | 40 +++++++++++++++++++ 2 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 org.springframework.context/src/main/java/org/springframework/context/annotation/ImportSelector.java 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 112200cf75..e4bc1b4d4f 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 @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Set; import java.util.Stack; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.parsing.Location; import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.ProblemReporter; @@ -37,6 +38,7 @@ import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.StandardAnnotationMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.AssignableTypeFilter; import org.springframework.util.StringUtils; /** @@ -142,7 +144,7 @@ class ConfigurationClassParser { List> allImportAttribs = AnnotationUtils.findAllAnnotationAttributes(Import.class, metadata.getClassName(), true); for (Map importAttribs : allImportAttribs) { - processImport(configClass, (String[]) importAttribs.get("value")); + processImport(configClass, (String[]) importAttribs.get("value"), true); } if (metadata.isAnnotated(ImportResource.class.getName())) { @@ -162,16 +164,29 @@ class ConfigurationClassParser { } } - private void processImport(ConfigurationClass configClass, String[] classesToImport) throws IOException { - if (this.importStack.contains(configClass)) { + private void processImport(ConfigurationClass configClass, String[] classesToImport, boolean checkForCircularImports) throws IOException { + if (checkForCircularImports && this.importStack.contains(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack, configClass.getMetadata())); } else { this.importStack.push(configClass); - for (String classToImport : classesToImport) { - this.importStack.registerImport(configClass.getMetadata().getClassName(), classToImport); - MetadataReader reader = this.metadataReaderFactory.getMetadataReader(classToImport); - processConfigurationClass(new ConfigurationClass(reader, null)); + AnnotationMetadata importingClassMetadata = configClass.getMetadata(); + for (String candidate : classesToImport) { + MetadataReader reader = this.metadataReaderFactory.getMetadataReader(candidate); + if (new AssignableTypeFilter(ImportSelector.class).match(reader, metadataReaderFactory)) { + // 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); + } catch (ClassNotFoundException ex) { + throw new IllegalStateException(ex); + } + } + else { + // the candidate class not an ImportSelector -> process it as a @Configuration class + this.importStack.registerImport(importingClassMetadata.getClassName(), candidate); + processConfigurationClass(new ConfigurationClass(reader, null)); + } } this.importStack.pop(); } 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 new file mode 100644 index 0000000000..6527d10de4 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportSelector.java @@ -0,0 +1,40 @@ +/* + * 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.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 an annotation attribute. + * + * @author Chris Beams + * @since 3.1 + * @see Import + */ +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. + */ + String[] selectImports(AnnotationMetadata importingClassMetadata); + +} -- GitLab