introduced BeanDefinitionRegistryPostProcessor extension to BeanFactoryPostProcessor; @Configuration classes support definition of BeanFactoryPostProcessor beans as well (SPR-6455, SPR-6611)
This commit is contained in:
parent
2d6ea2f4fe
commit
6b2b5c4c23
|
|
@ -27,6 +27,7 @@ import java.util.Set;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.annotation.Autowire;
|
||||
import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
|
||||
|
|
@ -48,7 +49,6 @@ import org.springframework.core.io.Resource;
|
|||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.core.type.StandardAnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
|
@ -68,11 +68,11 @@ import org.springframework.util.StringUtils;
|
|||
*/
|
||||
class ConfigurationClassBeanDefinitionReader {
|
||||
|
||||
static final String CONFIGURATION_CLASS_FULL = "full";
|
||||
private static final String CONFIGURATION_CLASS_FULL = "full";
|
||||
|
||||
static final String CONFIGURATION_CLASS_LITE = "lite";
|
||||
private static final String CONFIGURATION_CLASS_LITE = "lite";
|
||||
|
||||
static final String CONFIGURATION_CLASS_ATTRIBUTE =
|
||||
private static final String CONFIGURATION_CLASS_ATTRIBUTE =
|
||||
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ConfigurationClassBeanDefinitionReader.class);
|
||||
|
|
@ -86,7 +86,6 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
private final MetadataReaderFactory metadataReaderFactory;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link ConfigurationClassBeanDefinitionReader} instance that will be used
|
||||
* to populate the given {@link BeanDefinitionRegistry}.
|
||||
|
|
@ -95,6 +94,7 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
*/
|
||||
public ConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
|
||||
ProblemReporter problemReporter, MetadataReaderFactory metadataReaderFactory) {
|
||||
|
||||
this.registry = registry;
|
||||
this.sourceExtractor = sourceExtractor;
|
||||
this.problemReporter = problemReporter;
|
||||
|
|
@ -145,13 +145,15 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Registered bean definition for imported @Configuration class %s", configBeanName));
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
try {
|
||||
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
|
||||
AnnotationMetadata metadata = reader.getAnnotationMetadata();
|
||||
this.problemReporter.error(
|
||||
new InvalidConfigurationImportProblem(className, reader.getResource(), metadata));
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Could not create MetadataReader for class " + className);
|
||||
}
|
||||
}
|
||||
|
|
@ -181,7 +183,7 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
this.registry.registerAlias(beanName, alias);
|
||||
}
|
||||
|
||||
// has this already been overriden (i.e.: via XML)?
|
||||
// has this already been overridden (e.g. via XML)?
|
||||
if (this.registry.containsBeanDefinition(beanName)) {
|
||||
BeanDefinition existingBeanDef = registry.getBeanDefinition(beanName);
|
||||
// is the existing bean definition one that was created from a configuration class?
|
||||
|
|
@ -277,6 +279,7 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether the given bean definition is a candidate for a configuration class,
|
||||
* and mark it accordingly.
|
||||
|
|
@ -284,7 +287,7 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
* @param metadataReaderFactory the current factory in use by the caller
|
||||
* @return whether the candidate qualifies as (any kind of) configuration class
|
||||
*/
|
||||
static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
|
||||
public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
|
||||
AnnotationMetadata metadata = null;
|
||||
|
||||
// Check already loaded Class if present...
|
||||
|
|
@ -322,6 +325,14 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given bean definition indicates a full @Configuration class.
|
||||
*/
|
||||
public static boolean isFullConfigurationClass(BeanDefinition beanDef) {
|
||||
return CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition
|
||||
* created from a configuration class as opposed to any other configuration source.
|
||||
|
|
@ -329,7 +340,7 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
* definition was created externally.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private class ConfigurationClassBeanDefinition extends RootBeanDefinition implements AnnotatedBeanDefinition {
|
||||
private static class ConfigurationClassBeanDefinition extends RootBeanDefinition implements AnnotatedBeanDefinition {
|
||||
|
||||
private AnnotationMetadata annotationMetadata;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,9 +16,6 @@
|
|||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import static org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.CONFIGURATION_CLASS_ATTRIBUTE;
|
||||
import static org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.CONFIGURATION_CLASS_FULL;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
|
|
@ -40,6 +37,7 @@ import org.springframework.beans.factory.parsing.ProblemReporter;
|
|||
import org.springframework.beans.factory.parsing.SourceExtractor;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
|
|
@ -63,7 +61,7 @@ import org.springframework.util.ClassUtils;
|
|||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor, BeanClassLoaderAware {
|
||||
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, BeanClassLoaderAware {
|
||||
|
||||
/** Whether the CGLIB2 library is present on the classpath */
|
||||
private static final boolean cglibAvailable = ClassUtils.isPresent(
|
||||
|
|
@ -124,16 +122,18 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Derive further bean definitions from the configuration classes in the registry.
|
||||
*/
|
||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
|
||||
processConfigBeanDefinitions(registry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the Configuration classes for servicing bean requests at runtime
|
||||
* by replacing them with CGLIB-enhanced subclasses.
|
||||
*/
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
if (!(beanFactory instanceof BeanDefinitionRegistry)) {
|
||||
throw new IllegalStateException(
|
||||
"ConfigurationClassPostProcessor expects a BeanFactory that implements BeanDefinitionRegistry");
|
||||
}
|
||||
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
|
||||
enhanceConfigurationClasses(beanFactory);
|
||||
}
|
||||
|
||||
|
|
@ -174,8 +174,8 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
|
|||
parser.validate();
|
||||
|
||||
// Read the model and create bean definitions based on its content
|
||||
ConfigurationClassBeanDefinitionReader reader =
|
||||
new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory);
|
||||
ConfigurationClassBeanDefinitionReader reader = new ConfigurationClassBeanDefinitionReader(
|
||||
registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory);
|
||||
reader.loadBeanDefinitions(parser.getConfigurationClasses());
|
||||
}
|
||||
|
||||
|
|
@ -189,7 +189,7 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
|
|||
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<String, AbstractBeanDefinition>();
|
||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
||||
if (CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE))) {
|
||||
if (ConfigurationClassBeanDefinitionReader.isFullConfigurationClass(beanDef)) {
|
||||
if (!(beanDef instanceof AbstractBeanDefinition)) {
|
||||
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
|
||||
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2010 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.
|
||||
|
|
@ -39,6 +39,8 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
|||
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.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
||||
import org.springframework.beans.support.ResourceEditorRegistrar;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
|
@ -569,6 +571,21 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
|||
* <p>Must be called before singleton instantiation.
|
||||
*/
|
||||
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
|
||||
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
|
||||
if (beanFactory instanceof BeanDefinitionRegistry) {
|
||||
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
|
||||
for (BeanFactoryPostProcessor postProcessor : getBeanFactoryPostProcessors()) {
|
||||
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
|
||||
((BeanDefinitionRegistryPostProcessor) postProcessor).postProcessBeanDefinitionRegistry(registry);
|
||||
}
|
||||
}
|
||||
Collection<BeanDefinitionRegistryPostProcessor> registryPostProcessors =
|
||||
beanFactory.getBeansOfType(BeanDefinitionRegistryPostProcessor.class, true, false).values();
|
||||
for (BeanDefinitionRegistryPostProcessor postProcessor : registryPostProcessors) {
|
||||
postProcessor.postProcessBeanDefinitionRegistry(registry);
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke factory processors registered with the context instance.
|
||||
invokeBeanFactoryPostProcessors(getBeanFactoryPostProcessors(), beanFactory);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2010 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.
|
||||
|
|
@ -26,9 +26,13 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|||
import org.springframework.beans.factory.annotation.Required;
|
||||
import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
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.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
|
@ -58,6 +62,7 @@ public class ConfigurationClassProcessingTests {
|
|||
factory.registerBeanDefinition(configBeanName, new RootBeanDefinition(configClass));
|
||||
}
|
||||
ConfigurationClassPostProcessor ccpp = new ConfigurationClassPostProcessor();
|
||||
ccpp.postProcessBeanDefinitionRegistry(factory);
|
||||
ccpp.postProcessBeanFactory(factory);
|
||||
RequiredAnnotationBeanPostProcessor rapp = new RequiredAnnotationBeanPostProcessor();
|
||||
rapp.setBeanFactory(factory);
|
||||
|
|
@ -123,6 +128,19 @@ public class ConfigurationClassProcessingTests {
|
|||
assertNotSame(bar.getSpouse(), baz);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configurationWithPostProcessor() {
|
||||
BeanFactory factory = new AnnotationConfigApplicationContext(ConfigWithPostProcessor.class);
|
||||
|
||||
TestBean foo = factory.getBean("foo", TestBean.class);
|
||||
ITestBean bar = factory.getBean("bar", ITestBean.class);
|
||||
ITestBean baz = factory.getBean("baz", ITestBean.class);
|
||||
|
||||
assertEquals("foo-processed", foo.getName());
|
||||
assertEquals("bar-processed", bar.getName());
|
||||
assertEquals("baz-processed", baz.getName());
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class ConfigWithBeanWithCustomName {
|
||||
|
|
@ -152,7 +170,9 @@ public class ConfigurationClassProcessingTests {
|
|||
|
||||
@Configuration
|
||||
static class ConfigWithBeanWithAliases {
|
||||
|
||||
static TestBean testBean = new TestBean();
|
||||
|
||||
@Bean(name={"name1", "alias1", "alias2", "alias3"})
|
||||
public TestBean methodName() {
|
||||
return testBean;
|
||||
|
|
@ -177,7 +197,40 @@ public class ConfigurationClassProcessingTests {
|
|||
|
||||
@Bean @Scope("prototype")
|
||||
public TestBean baz() {
|
||||
return new TestBean("bar");
|
||||
return new TestBean("baz");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class ConfigWithPostProcessor extends ConfigWithPrototypeBean {
|
||||
|
||||
@Bean
|
||||
public BeanPostProcessor beanPostProcessor() {
|
||||
return new BeanPostProcessor() {
|
||||
String nameSuffix;
|
||||
public void setNameSuffix(String nameSuffix) {
|
||||
this.nameSuffix = nameSuffix;
|
||||
}
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) {
|
||||
if (bean instanceof ITestBean) {
|
||||
((ITestBean) bean).setName(((ITestBean) bean).getName() + nameSuffix);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) {
|
||||
return bean;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BeanFactoryPostProcessor beanFactoryPostProcessor() {
|
||||
return new BeanFactoryPostProcessor() {
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
BeanDefinition bd = beanFactory.getBeanDefinition("beanPostProcessor");
|
||||
bd.getPropertyValues().addPropertyValue("nameSuffix", "-processed");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2010 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.
|
||||
|
|
@ -16,16 +16,17 @@
|
|||
|
||||
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.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import test.beans.TestBean;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
|
@ -34,12 +35,11 @@ import org.springframework.context.annotation.Bean;
|
|||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportResource;
|
||||
|
||||
import test.beans.TestBean;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link ImportResource} support.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class ImportResourceTests {
|
||||
@Test
|
||||
|
|
@ -47,13 +47,17 @@ public class ImportResourceTests {
|
|||
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"));
|
||||
TestBean tb = ctx.getBean("javaDeclaredBean", TestBean.class);
|
||||
assertEquals("myName", tb.getName());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ImportResource("classpath:org/springframework/context/annotation/configuration/ImportXmlConfig-context.xml")
|
||||
static class ImportXmlConfig {
|
||||
@Value("${name}")
|
||||
private String name;
|
||||
public @Bean TestBean javaDeclaredBean() {
|
||||
return new TestBean("java.declared");
|
||||
return new TestBean(this.name);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -63,6 +67,8 @@ public class ImportResourceTests {
|
|||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlWithRelativePathConfig.class);
|
||||
assertTrue("did not contain java-declared bean", ctx.containsBean("javaDeclaredBean"));
|
||||
assertTrue("did not contain xml-declared bean", ctx.containsBean("xmlDeclaredBean"));
|
||||
TestBean tb = ctx.getBean("javaDeclaredBean", TestBean.class);
|
||||
assertEquals("myName", tb.getName());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
|
|
|||
|
|
@ -7,4 +7,12 @@
|
|||
<constructor-arg value="xml.declared"/>
|
||||
</bean>
|
||||
|
||||
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
|
||||
<property name="properties">
|
||||
<map>
|
||||
<entry key="name" value="myName"/>
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
|
|
|||
Loading…
Reference in New Issue