xmlImports) {
+ XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this.registry);
+ reader.loadBeanDefinitions(xmlImports.toArray(new String[]{}));
+ }
/**
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition
@@ -216,6 +224,7 @@ class ConfigurationClassBeanDefinitionReader {
* Used in bean overriding cases where it's necessary to determine whether the bean
* definition was created externally.
*/
+ @SuppressWarnings("serial")
private class ConfigurationClassBeanDefinition extends RootBeanDefinition implements AnnotatedBeanDefinition {
private AnnotationMetadata annotationMetadata;
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 64ed5bd3e1..ddbe1b5e4f 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
@@ -36,14 +36,18 @@ import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
/**
- * Parses a {@link Configuration} class definition, populating a configuration model.
- * This ASM-based implementation avoids reflection and eager class loading in order to
- * interoperate effectively with lazy class loading in a Spring ApplicationContext.
- *
+ * Parses a {@link Configuration} class definition, populating a model (collection) of
+ * {@link ConfigurationClass} objects (parsing a single Configuration class may result in
+ * any number of ConfigurationClass objects because one Configuration class may import
+ * another using the {@link Import} annotation).
+ *
* This class helps separate the concern of parsing the structure of a Configuration
* class from the concern of registering {@link BeanDefinition} objects based on the
* content of that model.
*
+ *
This ASM-based implementation avoids reflection and eager class loading in order to
+ * interoperate effectively with lazy class loading in a Spring ApplicationContext.
+ *
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.0
@@ -55,19 +59,19 @@ class ConfigurationClassParser {
private final ProblemReporter problemReporter;
- private final Set model;
-
private final Stack importStack = new ImportStack();
+
+ private final Set configurationClasses =
+ new LinkedHashSet();
/**
* Create a new {@link ConfigurationClassParser} instance that will be used
- * to populate a configuration model.
+ * to populate the set of configuration classes.
*/
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory, ProblemReporter problemReporter) {
this.metadataReaderFactory = metadataReaderFactory;
this.problemReporter = problemReporter;
- this.model = new LinkedHashSet();
}
@@ -84,11 +88,11 @@ class ConfigurationClassParser {
/**
* Parse the specified {@link Configuration @Configuration} class.
- * @param clazz the Clazz to parse
+ * @param clazz the Class to parse
* @param beanName may be null, but if populated represents the bean id
* (assumes that this configuration class was configured via XML)
*/
- public void parse(Class clazz, String beanName) throws IOException {
+ public void parse(Class> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName));
}
@@ -100,7 +104,7 @@ class ConfigurationClassParser {
String superClassName = metadata.getSuperClassName();
if (superClassName != null && !Object.class.getName().equals(superClassName)) {
if (metadata instanceof StandardAnnotationMetadata) {
- Class clazz = ((StandardAnnotationMetadata) metadata).getIntrospectedClass();
+ Class> clazz = ((StandardAnnotationMetadata) metadata).getIntrospectedClass();
metadata = new StandardAnnotationMetadata(clazz.getSuperclass());
}
else {
@@ -112,25 +116,30 @@ class ConfigurationClassParser {
metadata = null;
}
}
- if (this.model.contains(configClass) && configClass.getBeanName() != null) {
+ if (this.configurationClasses.contains(configClass) && configClass.getBeanName() != null) {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
- this.model.remove(configClass);
+ this.configurationClasses.remove(configClass);
}
- this.model.add(configClass);
+ this.configurationClasses.add(configClass);
}
protected void doProcessConfigurationClass(ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
if (metadata.isAnnotated(Import.class.getName())) {
processImport(configClass, (String[]) metadata.getAnnotationAttributes(Import.class.getName()).get("value"));
}
+ if (metadata.isAnnotated(ImportXml.class.getName())) {
+ for (String xmlImport : (String[]) metadata.getAnnotationAttributes(ImportXml.class.getName()).get("value")) {
+ configClass.addXmlImport(xmlImport);
+ }
+ }
Set methods = metadata.getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata methodMetadata : methods) {
configClass.addMethod(new ConfigurationClassMethod(methodMetadata, configClass));
}
}
- public void processImport(ConfigurationClass configClass, String[] classesToImport) throws IOException {
+ private void processImport(ConfigurationClass configClass, String[] classesToImport) throws IOException {
if (this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack, configClass.getMetadata()));
}
@@ -156,17 +165,17 @@ class ConfigurationClassParser {
}
/**
- * Recurse through the model validating each {@link ConfigurationClass}.
+ * Validate each {@link ConfigurationClass} object.
* @see ConfigurationClass#validate
*/
public void validate() {
- for (ConfigurationClass configClass : this.model) {
+ for (ConfigurationClass configClass : this.configurationClasses) {
configClass.validate(this.problemReporter);
}
}
- public Set getModel() {
- return this.model;
+ public Set getConfigurationClasses() {
+ return this.configurationClasses;
}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
index 32170d76db..eee004a859 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
@@ -184,7 +184,7 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
parser.validate();
// Read the model and create bean definitions based on its content
- new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor).loadBeanDefinitions(parser.getModel());
+ new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor).loadBeanDefinitions(parser.getConfigurationClasses());
}
/**
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportXml.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportXml.java
new file mode 100644
index 0000000000..108d0cb670
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ImportXml.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2002-2009 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.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+public @interface ImportXml {
+
+ String[] value();
+
+ Class> relativeTo() default void.class;
+
+}
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportXmlConfig-context.xml b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportXmlConfig-context.xml
new file mode 100644
index 0000000000..c9b3968c32
--- /dev/null
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportXmlConfig-context.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportXmlTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportXmlTests.java
new file mode 100644
index 0000000000..ce193849d8
--- /dev/null
+++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportXmlTests.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2002-2009 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.configuration;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.ImportXml;
+
+import test.beans.TestBean;
+
+/**
+ * Integration tests for {@link ImportXml} support.
+ *
+ * @author Chris Beams
+ */
+public class ImportXmlTests {
+ @Test
+ public void testImportXmlWorks() {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlConfig.class);
+ assertTrue("did not contain java-declared bean", ctx.containsBean("javaDeclaredBean"));
+ assertTrue("did not contain xml-declared bean", ctx.containsBean("xmlDeclaredBean"));
+ }
+
+ @Configuration
+ @ImportXml("classpath:org/springframework/context/annotation/configuration/ImportXmlConfig-context.xml")
+ static class ImportXmlConfig {
+ public @Bean TestBean javaDeclaredBean() {
+ return new TestBean("java.declared");
+ }
+ }
+
+ // -------------------------------------------------------------------------
+
+ @Ignore
+ @Test
+ public void testImportXmlWorksWithRelativePathing() {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportsXmlWithRelativeTo.class);
+ assertTrue("did not contain java-declared bean", ctx.containsBean("javaDeclaredBean"));
+ assertTrue("did not contain xml-declared bean", ctx.containsBean("xmlDeclaredBean"));
+ }
+
+ @Configuration
+ @ImportXml(value="beans.xml", relativeTo=ImportXmlTests.class)
+ static class ImportsXmlWithRelativeTo {
+ public @Bean TestBean javaDeclaredBean() {
+ return new TestBean("java.declared");
+ }
+ }
+
+ @Ignore
+ @Test
+ public void testImportXmlWorksWithAutowired() {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AutowiredImportXml.class);
+ String name = ctx.getBean("xmlBeanName", String.class);
+ assertThat(name, equalTo("xmlBean"));
+ }
+
+ @Configuration
+ @ImportXml(value="beans.xml", relativeTo=AutowiredImportXml.class)
+ static class AutowiredImportXml {
+ @Autowired TestBean xmlDeclaredBean;
+
+ public @Bean String xmlBeanName() {
+ return xmlDeclaredBean.getName();
+ }
+ }
+
+}
\ No newline at end of file