diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java index c04a36ccca..46905e8f46 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -37,6 +37,7 @@ import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.ResolvableType; @@ -137,8 +138,34 @@ abstract class BeanTypeRegistry { .getBeanDefinition(definition.getFactoryBeanName()); Class factoryClass = ClassUtils.forName(factoryDefinition.getBeanClassName(), beanFactory.getBeanClassLoader()); - return ReflectionUtils.findMethod(factoryClass, - definition.getFactoryMethodName()); + Method uniqueMethod = null; + for (Method candidate : getCandidateFactoryMethods(definition, factoryClass)) { + if (candidate.getName().equals(definition.getFactoryMethodName())) { + if (uniqueMethod == null) { + uniqueMethod = candidate; + } + else if (!matchingArguments(candidate, uniqueMethod)) { + return null; + } + } + } + return uniqueMethod; + } + + private Method[] getCandidateFactoryMethods(BeanDefinition definition, + Class factoryClass) { + return shouldConsiderNonPublicMethods(definition) + ? ReflectionUtils.getAllDeclaredMethods(factoryClass) + : factoryClass.getMethods(); + } + + private boolean shouldConsiderNonPublicMethods(BeanDefinition definition) { + return (definition instanceof AbstractBeanDefinition) + && ((AbstractBeanDefinition) definition).isNonPublicAccessAllowed(); + } + + private boolean matchingArguments(Method candidate, Method current) { + return Arrays.equals(candidate.getParameterTypes(), current.getParameterTypes()); } private Class getDirectFactoryBeanGeneric( 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 138bc924f9..6336a978c3 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 @@ -25,10 +25,15 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.scan.ScannedFactoryBeanConfiguration; +import org.springframework.boot.autoconfigure.condition.scan.ScannedFactoryBeanWithBeanMethodArgumentsConfiguration; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.context.annotation.ImportResource; @@ -140,6 +145,27 @@ public class ConditionalOnMissingBeanTests { .isEqualTo("fromFactory"); } + @Test + public void testOnMissingBeanConditionWithComponentScannedFactoryBean() { + this.context.register(ComponentScannedFactoryBeanBeanMethodConfiguration.class, + ConditionalOnFactoryBean.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + assertThat(this.context.getBean(ExampleBean.class).toString()) + .isEqualTo("fromFactory"); + } + + @Test + public void testOnMissingBeanConditionWithComponentScannedFactoryBeanWithBeanMethodArguments() { + this.context.register( + ComponentScannedFactoryBeanBeanMethodWithArgumentsConfiguration.class, + ConditionalOnFactoryBean.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + assertThat(this.context.getBean(ExampleBean.class).toString()) + .isEqualTo("fromFactory"); + } + @Test public void testOnMissingBeanConditionWithFactoryBeanWithBeanMethodArguments() { this.context.register(FactoryBeanWithBeanMethodArgumentsConfiguration.class, @@ -264,6 +290,18 @@ public class ConditionalOnMissingBeanTests { } + @Configuration + @ComponentScan(basePackages = "org.springframework.boot.autoconfigure.condition.scan", includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = ScannedFactoryBeanConfiguration.class)) + protected static class ComponentScannedFactoryBeanBeanMethodConfiguration { + + } + + @Configuration + @ComponentScan(basePackages = "org.springframework.boot.autoconfigure.condition.scan", includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = ScannedFactoryBeanWithBeanMethodArgumentsConfiguration.class)) + protected static class ComponentScannedFactoryBeanBeanMethodWithArgumentsConfiguration { + + } + @Configuration protected static class FactoryBeanWithBeanMethodArgumentsConfiguration { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/scan/ScannedFactoryBeanConfiguration.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/scan/ScannedFactoryBeanConfiguration.java new file mode 100644 index 0000000000..46ef97a806 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/scan/ScannedFactoryBeanConfiguration.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-2016 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.boot.autoconfigure.condition.scan; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBeanTests.ExampleBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBeanTests.ExampleFactoryBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Configuration for a factory bean produced by a bean method on a configuration class + * found via component scanning. + * + * @author Andy Wilkinson + */ +@Configuration +public class ScannedFactoryBeanConfiguration { + + @Bean + public FactoryBean exampleBeanFactoryBean() { + return new ExampleFactoryBean("foo"); + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/scan/ScannedFactoryBeanWithBeanMethodArgumentsConfiguration.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/scan/ScannedFactoryBeanWithBeanMethodArgumentsConfiguration.java new file mode 100644 index 0000000000..7b83d470ef --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/scan/ScannedFactoryBeanWithBeanMethodArgumentsConfiguration.java @@ -0,0 +1,46 @@ +/* + * Copyright 2012-2016 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.boot.autoconfigure.condition.scan; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBeanTests.ExampleFactoryBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Configuration for a factory bean produced by a bean method with arguments on a + * configuration class found via component scanning. + * + * @author Andy Wilkinson + */ +@Configuration +public class ScannedFactoryBeanWithBeanMethodArgumentsConfiguration { + + @Bean + public Foo foo() { + return new Foo(); + } + + @Bean + public ExampleFactoryBean exampleBeanFactoryBean(Foo foo) { + return new ExampleFactoryBean("foo"); + } + + static class Foo { + + } + +}