Skip to content

@ConditionalOnMissingBean now also checks beans available via FactoryBeans #355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.ConfigurationCondition;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.util.Assert;
Expand All @@ -43,6 +46,7 @@
*
* @author Phillip Webb
* @author Dave Syer
* @author Jakub Kubrynski
*/
class OnBeanCondition extends SpringBootCondition implements ConfigurationCondition {

Expand Down Expand Up @@ -102,6 +106,8 @@ private List<String> getMatchingBeans(ConditionContext context, BeanSearchSpec b
for (String type : beans.getTypes()) {
beanNames.addAll(Arrays.asList(getBeanNamesForType(beanFactory, type,
context.getClassLoader(), considerHierarchy)));
// add beans available through bean factory
beanNames.addAll(Arrays.asList(getBeanNamesFromBeanFactories(beanFactory, type)));
}

for (String annotation : beans.getAnnotations()) {
Expand All @@ -118,6 +124,48 @@ private List<String> getMatchingBeans(ConditionContext context, BeanSearchSpec b
return beanNames;
}

private String[] getBeanNamesFromBeanFactories(ConfigurableListableBeanFactory beanFactory,
String type) {
List<String> beanNames = new ArrayList<String>();

String[] factoryBeanNames = beanFactory.getBeanNamesForType(FactoryBean.class,
false, false);

for (String factoryBeanName : factoryBeanNames) {
factoryBeanName = BeanFactoryUtils.transformedBeanName(factoryBeanName);
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(factoryBeanName);
if (beanDefinition.getBeanClassName() == null) {
BeanDefinition beanFactoryBeanDefinition = beanFactory.getBeanDefinition(
beanDefinition.getFactoryBeanName());
String beanFactoryClassName = beanFactoryBeanDefinition.getBeanClassName();
Class<?> beanFactoryClass;
try {
beanFactoryClass = ClassUtils.forName(beanFactoryClassName, beanFactory.getBeanClassLoader());
} catch (ClassNotFoundException e) {
return NO_BEANS;
}

try {
Method declaredMethod = beanFactoryClass.getDeclaredMethod(
beanDefinition.getFactoryMethodName());
Class<?> factoryBeanType = GenericTypeResolver.resolveTypeArgument(
declaredMethod.getReturnType(), FactoryBean.class);
if (factoryBeanType == null) {
factoryBeanType = GenericTypeResolver.resolveReturnTypeArgument(
declaredMethod, FactoryBean.class);
}
if (type.equals(factoryBeanType.getName())) {
beanNames.add(factoryBeanName);
}
} catch (NoSuchMethodException e) {
return NO_BEANS;
}
}
}

return StringUtils.toStringArray(beanNames);
}

private boolean containsBean(ConfigurableListableBeanFactory beanFactory,
String beanName, boolean considerHierarchy) {
if (considerHierarchy) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@

package org.springframework.boot.autoconfigure.condition;

import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.util.Assert;

Expand All @@ -38,6 +38,7 @@
*
* @author Dave Syer
* @author Phillip Webb
* @author Jakub Kubrynski
*/
@SuppressWarnings("resource")
public class ConditionalOnMissingBeanTests {
Expand Down Expand Up @@ -111,20 +112,23 @@ public void testAnnotationOnMissingBeanConditionWithEagerFactoryBean() {
}

@Test
@Ignore("This will never work - you need to use XML for FactoryBeans, or else call getObject() inside the @Bean method")
public void testOnMissingBeanConditionWithFactoryBean() {
this.context.register(ExampleBeanAndFactoryBeanConfiguration.class,
public void testAnnotationOnMissingBeanConditionWithConcreteFactoryBeanMethod() {
this.context.register(ExampleBeanFactoryBeanConcreteConfiguration.class,
ExampleBeanMissingConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();

// There should be only one
this.context.getBean(ExampleBean.class);
}

@Test
public void testOnMissingBeanConditionWithFactoryBeanInXml() {
this.context.register(ConfigurationWithFactoryBean.class,
public void testAnnotationOnMissingBeanConditionWithGenericFactoryBeanMethod() {
this.context.register(ExampleBeanFactoryBeanGenericConfiguration.class,
ExampleBeanMissingConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();

// There should be only one
this.context.getBean(ExampleBean.class);
}
Expand All @@ -139,13 +143,31 @@ public String bar() {
}

@Configuration
protected static class ExampleBeanAndFactoryBeanConfiguration {
@Order(1)
protected static class ExampleBeanFactoryBeanConcreteConfiguration {

@Bean
public FactoryBean<ExampleBean> exampleBeanFactoryBean() {
return new ExampleFactoryBean("foo");
}

}

@Configuration
@Order(1)
protected static class ExampleBeanFactoryBeanGenericConfiguration {

@Bean
public FactoryBean<ExampleBean> exampleBeanFactoryBean() {
return new ExampleFactoryBean("foo");
}

}


@Configuration
@Order(2)
protected static class ExampleBeanMissingConfiguration {
@Bean
@ConditionalOnMissingBean(ExampleBean.class)
public ExampleBean createExampleBean() {
Expand Down