@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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -17,10 +17,14 @@
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
import java.beans.PropertyDescriptor;
|
import java.beans.PropertyDescriptor;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
@ -264,7 +268,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||||
* {@link Configuration} classes.
|
* {@link Configuration} classes.
|
||||||
*/
|
*/
|
||||||
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
|
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
|
||||||
Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>();
|
List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
|
||||||
String[] candidateNames = registry.getBeanDefinitionNames();
|
String[] candidateNames = registry.getBeanDefinitionNames();
|
||||||
|
|
||||||
for (String beanName : candidateNames) {
|
for (String beanName : candidateNames) {
|
||||||
|
|
@ -285,6 +289,16 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||||
return;
|
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
|
// Detect any custom bean name generation strategy supplied through the enclosing application context
|
||||||
SingletonBeanRegistry singletonRegistry = null;
|
SingletonBeanRegistry singletonRegistry = null;
|
||||||
if (registry instanceof SingletonBeanRegistry) {
|
if (registry instanceof SingletonBeanRegistry) {
|
||||||
|
|
@ -301,9 +315,10 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||||
this.metadataReaderFactory, this.problemReporter, this.environment,
|
this.metadataReaderFactory, this.problemReporter, this.environment,
|
||||||
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
|
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
|
||||||
|
|
||||||
|
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
|
||||||
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
|
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
|
||||||
do {
|
do {
|
||||||
parser.parse(configCandidates);
|
parser.parse(candidates);
|
||||||
parser.validate();
|
parser.validate();
|
||||||
|
|
||||||
Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
|
Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
|
||||||
|
|
@ -318,7 +333,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||||
this.reader.loadBeanDefinitions(configClasses);
|
this.reader.loadBeanDefinitions(configClasses);
|
||||||
alreadyParsed.addAll(configClasses);
|
alreadyParsed.addAll(configClasses);
|
||||||
|
|
||||||
configCandidates.clear();
|
candidates.clear();
|
||||||
if (registry.getBeanDefinitionCount() > candidateNames.length) {
|
if (registry.getBeanDefinitionCount() > candidateNames.length) {
|
||||||
String[] newCandidateNames = registry.getBeanDefinitionNames();
|
String[] newCandidateNames = registry.getBeanDefinitionNames();
|
||||||
Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
|
Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
|
||||||
|
|
@ -331,14 +346,14 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||||
BeanDefinition beanDef = registry.getBeanDefinition(candidateName);
|
BeanDefinition beanDef = registry.getBeanDefinition(candidateName);
|
||||||
if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory) &&
|
if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory) &&
|
||||||
!alreadyParsedClasses.contains(beanDef.getBeanClassName())) {
|
!alreadyParsedClasses.contains(beanDef.getBeanClassName())) {
|
||||||
configCandidates.add(new BeanDefinitionHolder(beanDef, candidateName));
|
candidates.add(new BeanDefinitionHolder(beanDef, candidateName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
candidateNames = newCandidateNames;
|
candidateNames = newCandidateNames;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (!configCandidates.isEmpty());
|
while (!candidates.isEmpty());
|
||||||
|
|
||||||
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
|
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
|
||||||
if (singletonRegistry != null) {
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.io.IOException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
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.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||||
import org.springframework.core.Conventions;
|
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.AnnotationMetadata;
|
||||||
import org.springframework.core.type.StandardAnnotationMetadata;
|
import org.springframework.core.type.StandardAnnotationMetadata;
|
||||||
import org.springframework.core.type.classreading.MetadataReader;
|
import org.springframework.core.type.classreading.MetadataReader;
|
||||||
|
|
@ -49,6 +53,9 @@ abstract class ConfigurationClassUtils {
|
||||||
private static final String CONFIGURATION_CLASS_ATTRIBUTE =
|
private static final String CONFIGURATION_CLASS_ATTRIBUTE =
|
||||||
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
|
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);
|
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)) {
|
if (isFullConfigurationCandidate(metadata)) {
|
||||||
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
|
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -173,4 +185,16 @@ abstract class ConfigurationClassUtils {
|
||||||
return CONFIGURATION_CLASS_LITE.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.ScopedObject;
|
||||||
import org.springframework.aop.scope.ScopedProxyUtils;
|
import org.springframework.aop.scope.ScopedProxyUtils;
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
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.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.componentscan.simple.SimpleComponent;
|
import org.springframework.context.annotation.componentscan.simple.SimpleComponent;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.core.env.StandardEnvironment;
|
import org.springframework.core.env.StandardEnvironment;
|
||||||
import org.springframework.core.io.DescriptiveResource;
|
import org.springframework.core.io.DescriptiveResource;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
@ -271,6 +273,35 @@ public class ConfigurationClassPostProcessorTests {
|
||||||
beanFactory.getBean("bar", TestBean.class);
|
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
|
@Test
|
||||||
public void scopedProxyTargetMarkedAsNonAutowireCandidate() {
|
public void scopedProxyTargetMarkedAsNonAutowireCandidate() {
|
||||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||||
|
|
@ -485,6 +516,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@Order(1)
|
||||||
static class SingletonBeanConfig {
|
static class SingletonBeanConfig {
|
||||||
|
|
||||||
public @Bean
|
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 Foo {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class ExtendedFoo extends Foo {
|
||||||
|
}
|
||||||
|
|
||||||
static class Bar {
|
static class Bar {
|
||||||
|
|
||||||
final Foo foo;
|
final Foo foo;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue