提交 cdb01cbd 编写于 作者: C Chris Beams

Introduce ImportAware interface

@Configuration classes may implement ImportAware in order to be injected
with the AnnotationMetadata of their @Import'ing class.

Includes the introduction of a new PriorityOrdered
ImportAwareBeanPostProcessor that handles injection of the
importing class metadata.
上级 89005a5b
...@@ -19,6 +19,7 @@ package org.springframework.context.annotation; ...@@ -19,6 +19,7 @@ package org.springframework.context.annotation;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
...@@ -62,7 +63,7 @@ class ConfigurationClassParser { ...@@ -62,7 +63,7 @@ class ConfigurationClassParser {
private final ProblemReporter problemReporter; private final ProblemReporter problemReporter;
private final Stack<ConfigurationClass> importStack = new ImportStack(); private final ImportStack importStack = new ImportStack();
private final Set<ConfigurationClass> configurationClasses = private final Set<ConfigurationClass> configurationClasses =
new LinkedHashSet<ConfigurationClass>(); new LinkedHashSet<ConfigurationClass>();
...@@ -168,6 +169,7 @@ class ConfigurationClassParser { ...@@ -168,6 +169,7 @@ class ConfigurationClassParser {
else { else {
this.importStack.push(configClass); this.importStack.push(configClass);
for (String classToImport : classesToImport) { for (String classToImport : classesToImport) {
this.importStack.registerImport(configClass.getMetadata().getClassName(), classToImport);
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(classToImport); MetadataReader reader = this.metadataReaderFactory.getMetadataReader(classToImport);
processConfigurationClass(new ConfigurationClass(reader, null)); processConfigurationClass(new ConfigurationClass(reader, null));
} }
...@@ -189,9 +191,28 @@ class ConfigurationClassParser { ...@@ -189,9 +191,28 @@ class ConfigurationClassParser {
return this.configurationClasses; return this.configurationClasses;
} }
public ImportRegistry getImportRegistry() {
return this.importStack;
}
interface ImportRegistry {
String getImportingClassFor(String importedClass);
}
@SuppressWarnings("serial") @SuppressWarnings("serial")
private static class ImportStack extends Stack<ConfigurationClass> { private static class ImportStack extends Stack<ConfigurationClass> implements ImportRegistry {
private Map<String, String> imports = new HashMap<String, String>();
public String getImportingClassFor(String importedClass) {
return imports.get(importedClass);
}
public void registerImport(String importingClass, String importedClass) {
imports.put(importedClass, importingClass);
}
/** /**
* Simplified contains() implementation that tests to see if any {@link ConfigurationClass} * Simplified contains() implementation that tests to see if any {@link ConfigurationClass}
......
...@@ -24,27 +24,38 @@ import java.util.Set; ...@@ -24,27 +24,38 @@ import java.util.Set;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.beans.factory.parsing.FailFastProblemReporter; import org.springframework.beans.factory.parsing.FailFastProblemReporter;
import org.springframework.beans.factory.parsing.PassThroughSourceExtractor; import org.springframework.beans.factory.parsing.PassThroughSourceExtractor;
import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.beans.factory.parsing.SourceExtractor; import org.springframework.beans.factory.parsing.SourceExtractor;
import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.EnvironmentAware; import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware; import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ConfigurationClassParser.ImportRegistry;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
...@@ -147,6 +158,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo ...@@ -147,6 +158,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
* Derive further bean definitions from the configuration classes in the registry. * Derive further bean definitions from the configuration classes in the registry.
*/ */
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
BeanDefinitionReaderUtils.registerWithGeneratedName(new RootBeanDefinition(ImportAwareBeanPostProcessor.class), registry);
if (this.postProcessBeanDefinitionRegistryCalled) { if (this.postProcessBeanDefinitionRegistryCalled) {
throw new IllegalStateException( throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called for this post-processor"); "postProcessBeanDefinitionRegistry already called for this post-processor");
...@@ -231,6 +243,13 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo ...@@ -231,6 +243,13 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
// Read the model and create bean definitions based on its content // Read the model and create bean definitions based on its content
reader.loadBeanDefinitions(parser.getConfigurationClasses()); reader.loadBeanDefinitions(parser.getConfigurationClasses());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (registry instanceof SingletonBeanRegistry) {
if (!((SingletonBeanRegistry) registry).containsSingleton("importRegistry")) {
((SingletonBeanRegistry) registry).registerSingleton("importRegistry", parser.getImportRegistry());
}
}
} }
/** /**
...@@ -278,4 +297,41 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo ...@@ -278,4 +297,41 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
} }
} }
private static class ImportAwareBeanPostProcessor implements PriorityOrdered, BeanFactoryAware, BeanPostProcessor {
private BeanFactory beanFactory;
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ImportAware) {
ImportRegistry importRegistry = beanFactory.getBean(ImportRegistry.class);
String importingClass = importRegistry.getImportingClassFor(bean.getClass().getSuperclass().getName());
if (importingClass != null) {
try {
AnnotationMetadata metadata = new SimpleMetadataReaderFactory().getMetadataReader(importingClass).getAnnotationMetadata();
((ImportAware) bean).setImportMetadata(metadata);
} catch (IOException ex) {
// should never occur -> at this point we know the class is present anyway
throw new IllegalStateException(ex);
}
}
else {
// no importing class was found
}
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
} }
/*
* 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.Aware;
import org.springframework.core.type.AnnotationMetadata;
/**
* Interface to be implemented by any @{@link Configuration} class that wishes
* to be injected with the {@link AnnotationMetadata} of the @{@code Configuration}
* class that imported it. Useful in conjunction with annotations that
* use @{@link Import} as a meta-annotation.
*
* @author Chris Beams
* @since 3.1
*/
public interface ImportAware extends Aware {
/**
* Set the annotation metadata of the importing @{@code Configuration} class.
*/
void setImportMetadata(AnnotationMetadata importMetadata);
}
/*
* 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 static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertThat;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor;
/**
* Tests that an ImportAware @Configuration classes gets injected with the
* annotation metadata of the @Configuration class that imported it.
*
* @author Chris Beams
* @since 3.1
*/
public class ImportAwareTests {
@Test
public void directlyAnnotatedWithImport() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ImportingConfig.class);
ctx.refresh();
ctx.getBean("importedConfigBean");
ImportedConfig importAwareConfig = ctx.getBean(ImportedConfig.class);
AnnotationMetadata importMetadata = importAwareConfig.importMetadata;
assertThat("import metadata was not injected", importMetadata, notNullValue());
assertThat(importMetadata.getClassName(), is(ImportingConfig.class.getName()));
Map<String, Object> importAttribs = importMetadata.getAnnotationAttributes(Import.class.getName());
Class<?>[] importedClasses = (Class<?>[])importAttribs.get("value");
assertThat(importedClasses[0].getName(), is(ImportedConfig.class.getName()));
}
@Test
public void indirectlyAnnotatedWithImport() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(IndirectlyImportingConfig.class);
ctx.refresh();
ctx.getBean("importedConfigBean");
ImportedConfig importAwareConfig = ctx.getBean(ImportedConfig.class);
AnnotationMetadata importMetadata = importAwareConfig.importMetadata;
assertThat("import metadata was not injected", importMetadata, notNullValue());
assertThat(importMetadata.getClassName(), is(IndirectlyImportingConfig.class.getName()));
Map<String, Object> enableAttribs = importMetadata.getAnnotationAttributes(EnableImportedConfig.class.getName());
String foo = (String)enableAttribs.get("foo");
assertThat(foo, is("xyz"));
}
@Configuration
@Import(ImportedConfig.class)
static class ImportingConfig {
}
@Configuration
@EnableImportedConfig(foo="xyz")
static class IndirectlyImportingConfig {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ImportedConfig.class)
public @interface EnableImportedConfig {
String foo() default "";
}
@Configuration
static class ImportedConfig implements ImportAware {
AnnotationMetadata importMetadata;
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.importMetadata = importMetadata;
}
@Bean
public BPP importedConfigBean() {
return new BPP();
}
@Bean
public AsyncAnnotationBeanPostProcessor asyncBPP() {
return new AsyncAnnotationBeanPostProcessor();
}
}
static class BPP implements BeanFactoryAware, BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
return bean;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("ImportAwareTests.BPP.setBeanFactory()");
}
}
}
...@@ -193,8 +193,8 @@ public class ScopingTests { ...@@ -193,8 +193,8 @@ public class ScopingTests {
@Test @Test
public void testScopedConfigurationBeanDefinitionCount() throws Exception { public void testScopedConfigurationBeanDefinitionCount() throws Exception {
// count the beans // count the beans
// 6 @Beans + 1 Configuration + 2 scoped proxy // 6 @Beans + 1 Configuration + 2 scoped proxy + 1 importRegistry
assertEquals(9, ctx.getBeanDefinitionCount()); assertEquals(10, ctx.getBeanDefinitionCount());
} }
// /** // /**
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册