Browse Source

introduced BeanDefinitionRegistryPostProcessor extension to BeanFactoryPostProcessor; @Configuration classes support definition of BeanFactoryPostProcessor beans as well (SPR-6455, SPR-6611)

pull/23217/head
Juergen Hoeller 15 years ago
parent
commit
6b2b5c4c23
  1. 31
      org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
  2. 24
      org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
  3. 19
      org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
  4. 57
      org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java
  5. 22
      org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportResourceTests.java
  6. 8
      org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportXmlConfig-context.xml

31
org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java

@ -27,6 +27,7 @@ import java.util.Set; @@ -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; @@ -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; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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;

24
org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java

@ -16,9 +16,6 @@ @@ -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; @@ -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; @@ -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 @@ -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 @@ -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 @@ -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");

19
org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java

@ -1,5 +1,5 @@ @@ -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; @@ -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 @@ -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);

57
org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java

@ -1,5 +1,5 @@ @@ -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; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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");
}
};
}
}

22
org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportResourceTests.java

@ -1,5 +1,5 @@ @@ -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 @@ @@ -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; @@ -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 { @@ -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 { @@ -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

8
org.springframework.context/src/test/java/org/springframework/context/annotation/configuration/ImportXmlConfig-context.xml

@ -7,4 +7,12 @@ @@ -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…
Cancel
Save