From f037a00146344460792e0ba41d1e4dd311a83583 Mon Sep 17 00:00:00 2001 From: Jakub Kubrynski Date: Sat, 15 Feb 2014 03:47:23 +0100 Subject: [PATCH] @ConditionalOnMissingBean now also checks beans available via BeanFactory --- .../condition/OnBeanCondition.java | 48 +++++++++++++++++++ .../ConditionalOnMissingBeanTests.java | 36 +++++++++++--- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java index 57ccd264d3e5..25b43202178a 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnBeanCondition.java @@ -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; @@ -43,6 +46,7 @@ * * @author Phillip Webb * @author Dave Syer + * @author Jakub Kubrynski */ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondition { @@ -102,6 +106,8 @@ private List 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()) { @@ -118,6 +124,48 @@ private List getMatchingBeans(ConditionContext context, BeanSearchSpec b return beanNames; } + private String[] getBeanNamesFromBeanFactories(ConfigurableListableBeanFactory beanFactory, + String type) { + List beanNames = new ArrayList(); + + 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) { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java index e96531ba5ce3..0509ec6fa21c 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java @@ -16,7 +16,6 @@ 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; @@ -24,6 +23,7 @@ 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; @@ -38,6 +38,7 @@ * * @author Dave Syer * @author Phillip Webb + * @author Jakub Kubrynski */ @SuppressWarnings("resource") public class ConditionalOnMissingBeanTests { @@ -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); } @@ -139,13 +143,31 @@ public String bar() { } @Configuration - protected static class ExampleBeanAndFactoryBeanConfiguration { + @Order(1) + protected static class ExampleBeanFactoryBeanConcreteConfiguration { @Bean public FactoryBean exampleBeanFactoryBean() { return new ExampleFactoryBean("foo"); } + } + + @Configuration + @Order(1) + protected static class ExampleBeanFactoryBeanGenericConfiguration { + + @Bean + public FactoryBean exampleBeanFactoryBean() { + return new ExampleFactoryBean("foo"); + } + + } + + + @Configuration + @Order(2) + protected static class ExampleBeanMissingConfiguration { @Bean @ConditionalOnMissingBean(ExampleBean.class) public ExampleBean createExampleBean() {