@Configuration classes get processed according to their @Order (if applicable)
Issue: SPR-12657
This commit is contained in:
parent
9d497cbd98
commit
f5b4e18209
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
|
@ -17,10 +17,14 @@
|
|||
package org.springframework.context.annotation;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
|
@ -264,7 +268,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
* {@link Configuration} classes.
|
||||
*/
|
||||
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
|
||||
Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>();
|
||||
List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
|
||||
String[] candidateNames = registry.getBeanDefinitionNames();
|
||||
|
||||
for (String beanName : candidateNames) {
|
||||
|
|
@ -285,6 +289,16 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
return;
|
||||
}
|
||||
|
||||
// Sort by previously determined @Order value, if applicable
|
||||
Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
|
||||
@Override
|
||||
public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
|
||||
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
|
||||
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
|
||||
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
|
||||
}
|
||||
});
|
||||
|
||||
// Detect any custom bean name generation strategy supplied through the enclosing application context
|
||||
SingletonBeanRegistry singletonRegistry = null;
|
||||
if (registry instanceof SingletonBeanRegistry) {
|
||||
|
|
@ -301,9 +315,10 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
this.metadataReaderFactory, this.problemReporter, this.environment,
|
||||
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
|
||||
|
||||
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
|
||||
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
|
||||
do {
|
||||
parser.parse(configCandidates);
|
||||
parser.parse(candidates);
|
||||
parser.validate();
|
||||
|
||||
Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
|
||||
|
|
@ -318,7 +333,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
this.reader.loadBeanDefinitions(configClasses);
|
||||
alreadyParsed.addAll(configClasses);
|
||||
|
||||
configCandidates.clear();
|
||||
candidates.clear();
|
||||
if (registry.getBeanDefinitionCount() > candidateNames.length) {
|
||||
String[] newCandidateNames = registry.getBeanDefinitionNames();
|
||||
Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
|
||||
|
|
@ -331,14 +346,14 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
|||
BeanDefinition beanDef = registry.getBeanDefinition(candidateName);
|
||||
if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory) &&
|
||||
!alreadyParsedClasses.contains(beanDef.getBeanClassName())) {
|
||||
configCandidates.add(new BeanDefinitionHolder(beanDef, candidateName));
|
||||
candidates.add(new BeanDefinitionHolder(beanDef, candidateName));
|
||||
}
|
||||
}
|
||||
}
|
||||
candidateNames = newCandidateNames;
|
||||
}
|
||||
}
|
||||
while (!configCandidates.isEmpty());
|
||||
while (!candidates.isEmpty());
|
||||
|
||||
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
|
||||
if (singletonRegistry != null) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
|
@ -18,6 +18,7 @@ package org.springframework.context.annotation;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
|
@ -27,6 +28,9 @@ import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
|||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.StandardAnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
|
|
@ -49,6 +53,9 @@ abstract class ConfigurationClassUtils {
|
|||
private static final String CONFIGURATION_CLASS_ATTRIBUTE =
|
||||
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
|
||||
|
||||
private static final String ORDER_ATTRIBUTE =
|
||||
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "order");
|
||||
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ConfigurationClassUtils.class);
|
||||
|
||||
|
|
@ -101,6 +108,11 @@ abstract class ConfigurationClassUtils {
|
|||
}
|
||||
}
|
||||
|
||||
Map<String, Object> orderAttributes = metadata.getAnnotationAttributes(Order.class.getName());
|
||||
if (orderAttributes != null) {
|
||||
beanDef.setAttribute(ORDER_ATTRIBUTE, orderAttributes.get(AnnotationUtils.VALUE));
|
||||
}
|
||||
|
||||
if (isFullConfigurationCandidate(metadata)) {
|
||||
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
|
||||
return true;
|
||||
|
|
@ -173,4 +185,16 @@ abstract class ConfigurationClassUtils {
|
|||
return CONFIGURATION_CLASS_LITE.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the order for the given configuration class bean definition,
|
||||
* as set by {@link #checkConfigurationClassCandidate}.
|
||||
* @param beanDef the bean definition to check
|
||||
* @return the {@link @Order} annotation value on the configuration class,
|
||||
* or {@link Ordered#LOWEST_PRECEDENCE} if none declared
|
||||
*/
|
||||
public static int getOrder(BeanDefinition beanDef) {
|
||||
Integer order = (Integer) beanDef.getAttribute(ORDER_ATTRIBUTE);
|
||||
return (order != null ? order : Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
|
|
@ -28,6 +28,7 @@ import org.junit.Test;
|
|||
import org.springframework.aop.scope.ScopedObject;
|
||||
import org.springframework.aop.scope.ScopedProxyUtils;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
|
@ -40,6 +41,7 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
|||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.componentscan.simple.SimpleComponent;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.io.DescriptiveResource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
|
@ -271,6 +273,35 @@ public class ConfigurationClassPostProcessorTests {
|
|||
beanFactory.getBean("bar", TestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorFailsOnImplicitOverrideIfOverridingIsNotAllowed() {
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class);
|
||||
rbd.setResource(new DescriptiveResource("XML or something"));
|
||||
beanFactory.registerBeanDefinition("bar", rbd);
|
||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class));
|
||||
beanFactory.setAllowBeanDefinitionOverriding(false);
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
try {
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
fail("Should have thrown BeanDefinitionStoreException");
|
||||
}
|
||||
catch (BeanDefinitionStoreException ex) {
|
||||
assertTrue(ex.getMessage().contains("bar"));
|
||||
assertTrue(ex.getMessage().contains("SingletonBeanConfig"));
|
||||
assertTrue(ex.getMessage().contains(TestBean.class.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configurationClassesProcessedInCorrectOrder() {
|
||||
beanFactory.registerBeanDefinition("config1", new RootBeanDefinition(OverridingSingletonBeanConfig.class));
|
||||
beanFactory.registerBeanDefinition("config2", new RootBeanDefinition(SingletonBeanConfig.class));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
assertTrue(beanFactory.getBean(Foo.class) instanceof ExtendedFoo);
|
||||
beanFactory.getBean(Bar.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scopedProxyTargetMarkedAsNonAutowireCandidate() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
|
|
@ -485,6 +516,7 @@ public class ConfigurationClassPostProcessorTests {
|
|||
// -------------------------------------------------------------------------
|
||||
|
||||
@Configuration
|
||||
@Order(1)
|
||||
static class SingletonBeanConfig {
|
||||
|
||||
public @Bean
|
||||
|
|
@ -498,9 +530,27 @@ public class ConfigurationClassPostProcessorTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(2)
|
||||
static class OverridingSingletonBeanConfig {
|
||||
|
||||
public @Bean
|
||||
ExtendedFoo foo() {
|
||||
return new ExtendedFoo();
|
||||
}
|
||||
|
||||
public @Bean
|
||||
Bar bar() {
|
||||
return new Bar(foo());
|
||||
}
|
||||
}
|
||||
|
||||
static class Foo {
|
||||
}
|
||||
|
||||
static class ExtendedFoo extends Foo {
|
||||
}
|
||||
|
||||
static class Bar {
|
||||
|
||||
final Foo foo;
|
||||
|
|
|
|||
Loading…
Reference in New Issue