Properly honor conditions on overriding bean methods

Issue: SPR-12694
This commit is contained in:
Juergen Hoeller 2015-02-10 19:26:19 +01:00
parent 918bc3b103
commit 981aefc2c0
3 changed files with 73 additions and 19 deletions

View File

@ -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.
@ -16,6 +16,7 @@
package org.springframework.context.annotation; package org.springframework.context.annotation;
import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
@ -63,6 +64,8 @@ final class ConfigurationClass {
private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars = private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
new LinkedHashMap<ImportBeanDefinitionRegistrar, AnnotationMetadata>(); new LinkedHashMap<ImportBeanDefinitionRegistrar, AnnotationMetadata>();
final Set<String> skippedBeans = new HashSet<String>();
/** /**
* Create a new {@link ConfigurationClass} with the given name. * Create a new {@link ConfigurationClass} with the given name.

View File

@ -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.
@ -182,16 +182,37 @@ class ConfigurationClassBeanDefinitionReader {
* with the BeanDefinitionRegistry based on its contents. * with the BeanDefinitionRegistry based on its contents.
*/ */
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) { private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
if (this.conditionEvaluator.shouldSkip(beanMethod.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
// Consider name and any aliases
AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
List<String> names = new ArrayList<String>(Arrays.asList(bean.getStringArray("name")));
String beanName = (names.size() > 0 ? names.remove(0) : beanMethod.getMetadata().getMethodName());
// Do we need to mark the bean as skipped by its condition?
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeans.add(beanName);
return;
}
if (configClass.skippedBeans.contains(beanName)) {
return; return;
} }
ConfigurationClass configClass = beanMethod.getConfigurationClass(); // Register aliases even when overridden
MethodMetadata metadata = beanMethod.getMetadata(); for (String alias : names) {
this.registry.registerAlias(beanName, alias);
}
// Has this effectively been overridden before (e.g. via XML)?
if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
return;
}
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata); ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
beanDef.setResource(configClass.getResource()); beanDef.setResource(configClass.getResource());
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource())); beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
if (metadata.isStatic()) { if (metadata.isStatic()) {
// static @Bean method // static @Bean method
beanDef.setBeanClassName(configClass.getMetadata().getClassName()); beanDef.setBeanClassName(configClass.getMetadata().getClassName());
@ -205,19 +226,6 @@ class ConfigurationClassBeanDefinitionReader {
beanDef.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); beanDef.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE); beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
// Consider name and any aliases
AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
List<String> names = new ArrayList<String>(Arrays.asList(bean.getStringArray("name")));
String beanName = (names.size() > 0 ? names.remove(0) : beanMethod.getMetadata().getMethodName());
for (String alias : names) {
this.registry.registerAlias(beanName, alias);
}
// Has this effectively been overridden before (e.g. via XML)?
if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
return;
}
AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata); AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
Autowire autowire = bean.getEnum("autowire"); Autowire autowire = bean.getEnum("autowire");

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 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.
@ -39,6 +39,7 @@ import static org.junit.Assert.*;
* Test for {@link Conditional} beans. * Test for {@link Conditional} beans.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Juergen Hoeller
*/ */
@SuppressWarnings("resource") @SuppressWarnings("resource")
public class ConfigurationClassWithConditionTests { public class ConfigurationClassWithConditionTests {
@ -117,6 +118,19 @@ public class ConfigurationClassWithConditionTests {
ctx.refresh(); ctx.refresh();
} }
@Test
public void conditionOnOverriddenMethodHonored() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigWithBeanSkipped.class);
assertEquals(0, context.getBeansOfType(ExampleBean.class).size());
}
@Test
public void noConditionOnOverriddenMethodHonored() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigWithBeanReactivated.class);
assertEquals(1, context.getBeansOfType(ExampleBean.class).size());
}
@Configuration @Configuration
static class BeanOneConfiguration { static class BeanOneConfiguration {
@Bean @Bean
@ -197,6 +211,7 @@ public class ConfigurationClassWithConditionTests {
} }
static class NeverCondition implements Condition { static class NeverCondition implements Condition {
@Override @Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return false; return false;
@ -261,4 +276,32 @@ public class ConfigurationClassWithConditionTests {
static class ExampleBean { static class ExampleBean {
} }
@Configuration
private static class ConfigWithBeanActive {
@Bean
public ExampleBean baz() {
return new ExampleBean();
}
}
private static class ConfigWithBeanSkipped extends ConfigWithBeanActive {
@Override
@Bean
@Conditional(NeverCondition.class)
public ExampleBean baz() {
return new ExampleBean();
}
}
private static class ConfigWithBeanReactivated extends ConfigWithBeanSkipped {
@Override
@Bean
public ExampleBean baz() {
return new ExampleBean();
}
}
} }