Properly evaluate @Conditional in case of multiple imports for same config class
Issue: SPR-11788
This commit is contained in:
parent
be0b69cbf1
commit
52f44b340e
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2014 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.
|
||||||
|
@ -83,13 +83,13 @@ class ConditionEvaluator {
|
||||||
|
|
||||||
for (String[] conditionClasses : getConditionClasses(metadata)) {
|
for (String[] conditionClasses : getConditionClasses(metadata)) {
|
||||||
for (String conditionClass : conditionClasses) {
|
for (String conditionClass : conditionClasses) {
|
||||||
Condition condition = getCondition(conditionClass, context.getClassLoader());
|
Condition condition = getCondition(conditionClass, this.context.getClassLoader());
|
||||||
ConfigurationPhase requiredPhase = null;
|
ConfigurationPhase requiredPhase = null;
|
||||||
if (condition instanceof ConfigurationCondition) {
|
if (condition instanceof ConfigurationCondition) {
|
||||||
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
|
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
|
||||||
}
|
}
|
||||||
if (requiredPhase == null || requiredPhase == phase) {
|
if (requiredPhase == null || requiredPhase == phase) {
|
||||||
if (!condition.matches(context, metadata)) {
|
if (!condition.matches(this.context, metadata)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2014 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.
|
||||||
|
@ -53,7 +53,7 @@ final class ConfigurationClass {
|
||||||
|
|
||||||
private String beanName;
|
private String beanName;
|
||||||
|
|
||||||
private final ConfigurationClass importedBy;
|
private final Set<ConfigurationClass> importedBy = new LinkedHashSet<ConfigurationClass>(1);
|
||||||
|
|
||||||
private final Set<BeanMethod> beanMethods = new LinkedHashSet<BeanMethod>();
|
private final Set<BeanMethod> beanMethods = new LinkedHashSet<BeanMethod>();
|
||||||
|
|
||||||
|
@ -76,7 +76,6 @@ final class ConfigurationClass {
|
||||||
this.metadata = metadataReader.getAnnotationMetadata();
|
this.metadata = metadataReader.getAnnotationMetadata();
|
||||||
this.resource = metadataReader.getResource();
|
this.resource = metadataReader.getResource();
|
||||||
this.beanName = beanName;
|
this.beanName = beanName;
|
||||||
this.importedBy = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,7 +89,7 @@ final class ConfigurationClass {
|
||||||
public ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) {
|
public ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) {
|
||||||
this.metadata = metadataReader.getAnnotationMetadata();
|
this.metadata = metadataReader.getAnnotationMetadata();
|
||||||
this.resource = metadataReader.getResource();
|
this.resource = metadataReader.getResource();
|
||||||
this.importedBy = importedBy;
|
this.importedBy.add(importedBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -105,7 +104,6 @@ final class ConfigurationClass {
|
||||||
this.metadata = new StandardAnnotationMetadata(clazz, true);
|
this.metadata = new StandardAnnotationMetadata(clazz, true);
|
||||||
this.resource = new DescriptiveResource(clazz.toString());
|
this.resource = new DescriptiveResource(clazz.toString());
|
||||||
this.beanName = beanName;
|
this.beanName = beanName;
|
||||||
this.importedBy = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -119,7 +117,7 @@ final class ConfigurationClass {
|
||||||
public ConfigurationClass(Class<?> clazz, ConfigurationClass importedBy) {
|
public ConfigurationClass(Class<?> clazz, ConfigurationClass importedBy) {
|
||||||
this.metadata = new StandardAnnotationMetadata(clazz, true);
|
this.metadata = new StandardAnnotationMetadata(clazz, true);
|
||||||
this.resource = new DescriptiveResource(clazz.toString());
|
this.resource = new DescriptiveResource(clazz.toString());
|
||||||
this.importedBy = importedBy;
|
this.importedBy.add(importedBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -150,16 +148,24 @@ final class ConfigurationClass {
|
||||||
* @see #getImportedBy()
|
* @see #getImportedBy()
|
||||||
*/
|
*/
|
||||||
public boolean isImported() {
|
public boolean isImported() {
|
||||||
return (this.importedBy != null);
|
return !this.importedBy.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the configuration class that imported this class,
|
* Merge the imported-by declarations from the given configuration class into this one.
|
||||||
* or {@code null} if this configuration was not imported.
|
* @since 4.0.5
|
||||||
* @since 4.0
|
*/
|
||||||
|
public void mergeImportedBy(ConfigurationClass otherConfigClass) {
|
||||||
|
this.importedBy.addAll(otherConfigClass.importedBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the configuration classes that imported this class,
|
||||||
|
* or an empty Set if this configuration was not imported.
|
||||||
|
* @since 4.0.5
|
||||||
* @see #isImported()
|
* @see #isImported()
|
||||||
*/
|
*/
|
||||||
public ConfigurationClass getImportedBy() {
|
public Set<ConfigurationClass> getImportedBy() {
|
||||||
return this.importedBy;
|
return this.importedBy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2014 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.
|
||||||
|
@ -391,14 +391,19 @@ class ConfigurationClassBeanDefinitionReader {
|
||||||
Boolean skip = this.skipped.get(configClass);
|
Boolean skip = this.skipped.get(configClass);
|
||||||
if (skip == null) {
|
if (skip == null) {
|
||||||
if (configClass.isImported()) {
|
if (configClass.isImported()) {
|
||||||
if (shouldSkip(configClass.getImportedBy())) {
|
boolean allSkipped = true;
|
||||||
// The config that imported this one was skipped, therefore we are skipped
|
for (ConfigurationClass importedBy : configClass.getImportedBy()) {
|
||||||
|
if (!shouldSkip(importedBy)) {
|
||||||
|
allSkipped = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (allSkipped) {
|
||||||
|
// The config classes that imported this one were all skipped, therefore we are skipped...
|
||||||
skip = true;
|
skip = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (skip == null) {
|
if (skip == null) {
|
||||||
skip = conditionEvaluator.shouldSkip(configClass.getMetadata(),
|
skip = conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN);
|
||||||
ConfigurationPhase.REGISTER_BEAN);
|
|
||||||
}
|
}
|
||||||
this.skipped.put(configClass, skip);
|
this.skipped.put(configClass, skip);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -112,7 +113,8 @@ class ConfigurationClassParser {
|
||||||
|
|
||||||
private final ComponentScanAnnotationParser componentScanParser;
|
private final ComponentScanAnnotationParser componentScanParser;
|
||||||
|
|
||||||
private final Set<ConfigurationClass> configurationClasses = new LinkedHashSet<ConfigurationClass>();
|
private final Map<ConfigurationClass, ConfigurationClass> configurationClasses =
|
||||||
|
new LinkedHashMap<ConfigurationClass, ConfigurationClass>();
|
||||||
|
|
||||||
private final Map<String, ConfigurationClass> knownSuperclasses = new HashMap<String, ConfigurationClass>();
|
private final Map<String, ConfigurationClass> knownSuperclasses = new HashMap<String, ConfigurationClass>();
|
||||||
|
|
||||||
|
@ -189,7 +191,16 @@ class ConfigurationClassParser {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.configurationClasses.contains(configClass) && configClass.getBeanName() != null) {
|
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
|
||||||
|
if (existingClass != null) {
|
||||||
|
if (configClass.isImported()) {
|
||||||
|
if (existingClass.isImported()) {
|
||||||
|
existingClass.mergeImportedBy(configClass);
|
||||||
|
}
|
||||||
|
// Otherwise ignore new imported config class; existing non-imported class overrides it.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
// Explicit bean definition found, probably replacing an import.
|
// Explicit bean definition found, probably replacing an import.
|
||||||
// Let's remove the old one and go with the new one.
|
// Let's remove the old one and go with the new one.
|
||||||
this.configurationClasses.remove(configClass);
|
this.configurationClasses.remove(configClass);
|
||||||
|
@ -199,6 +210,7 @@ class ConfigurationClassParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Recursively process the configuration class and its superclass hierarchy.
|
// Recursively process the configuration class and its superclass hierarchy.
|
||||||
SourceClass sourceClass = asSourceClass(configClass);
|
SourceClass sourceClass = asSourceClass(configClass);
|
||||||
|
@ -207,7 +219,7 @@ class ConfigurationClassParser {
|
||||||
}
|
}
|
||||||
while (sourceClass != null);
|
while (sourceClass != null);
|
||||||
|
|
||||||
this.configurationClasses.add(configClass);
|
this.configurationClasses.put(configClass, configClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -466,13 +478,13 @@ class ConfigurationClassParser {
|
||||||
* @see ConfigurationClass#validate
|
* @see ConfigurationClass#validate
|
||||||
*/
|
*/
|
||||||
public void validate() {
|
public void validate() {
|
||||||
for (ConfigurationClass configClass : this.configurationClasses) {
|
for (ConfigurationClass configClass : this.configurationClasses.keySet()) {
|
||||||
configClass.validate(this.problemReporter);
|
configClass.validate(this.problemReporter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ConfigurationClass> getConfigurationClasses() {
|
public Set<ConfigurationClass> getConfigurationClasses() {
|
||||||
return this.configurationClasses;
|
return this.configurationClasses.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PropertySource<?>> getPropertySources() {
|
public List<PropertySource<?>> getPropertySources() {
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-2014 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.configuration;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.ConditionContext;
|
||||||
|
import org.springframework.context.annotation.Conditional;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.ConfigurationCondition;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class ImportWithConditionTests {
|
||||||
|
|
||||||
|
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void conditionalThenUnconditional() throws Exception {
|
||||||
|
this.context.register(ConditionalThenUnconditional.class);
|
||||||
|
this.context.refresh();
|
||||||
|
assertFalse(this.context.containsBean("beanTwo"));
|
||||||
|
assertTrue(this.context.containsBean("beanOne"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void unconditionalThenConditional() throws Exception {
|
||||||
|
this.context.register(UnconditionalThenConditional.class);
|
||||||
|
this.context.refresh();
|
||||||
|
assertFalse(this.context.containsBean("beanTwo"));
|
||||||
|
assertTrue(this.context.containsBean("beanOne"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Import({ConditionalConfiguration.class, UnconditionalConfiguration.class})
|
||||||
|
protected static class ConditionalThenUnconditional {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BeanOne beanOne;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Import({UnconditionalConfiguration.class, ConditionalConfiguration.class})
|
||||||
|
protected static class UnconditionalThenConditional {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BeanOne beanOne;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Import(BeanProvidingConfiguration.class)
|
||||||
|
protected static class UnconditionalConfiguration {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Conditional(NeverMatchingCondition.class)
|
||||||
|
@Import(BeanProvidingConfiguration.class)
|
||||||
|
protected static class ConditionalConfiguration {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
protected static class BeanProvidingConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
BeanOne beanOne() {
|
||||||
|
return new BeanOne();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final class BeanOne {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final class NeverMatchingCondition implements ConfigurationCondition {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigurationPhase getConfigurationPhase() {
|
||||||
|
return ConfigurationPhase.REGISTER_BEAN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue