Skip to content

Commit 905346d

Browse files
committed
Consider @bean methods with args to determine type created by factory
Previously, BeanTypeRegistry would only look for a @bean method with no arguments when trying to determine the type that will be created by a factory bean. This meant that the type produced by a factory bean declared via a @bean that has one or more arguments would be unknown and any on missing bean conditions look for a bean of the type produced by the factory bean would match in error. This commit updates BeanTypeRegistry to, where possible, use the factory method metadata for the bean definition when determining the type that will be created. This allows it to determine the type for factory bean created by @bean methods that take arguments and also avoids the use reflection to find the factory method. Where factory method metadata is not available, the existing reflection-based approach is used as a fallback. Closes gh-3657
1 parent 2c0ec1b commit 905346d

File tree

2 files changed

+44
-6
lines changed

2 files changed

+44
-6
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/BeanTypeRegistry.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@
3333
import org.springframework.beans.factory.FactoryBean;
3434
import org.springframework.beans.factory.ListableBeanFactory;
3535
import org.springframework.beans.factory.SmartInitializingSingleton;
36+
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
3637
import org.springframework.beans.factory.config.BeanDefinition;
3738
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
3839
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
3940
import org.springframework.beans.factory.support.RootBeanDefinition;
4041
import org.springframework.core.ResolvableType;
42+
import org.springframework.core.type.MethodMetadata;
43+
import org.springframework.core.type.StandardMethodMetadata;
4144
import org.springframework.util.ClassUtils;
4245
import org.springframework.util.ReflectionUtils;
4346
import org.springframework.util.StringUtils;
@@ -108,12 +111,7 @@ private Class<?> doGetFactoryBeanGeneric(ConfigurableListableBeanFactory beanFac
108111
private Class<?> getConfigurationClassFactoryBeanGeneric(
109112
ConfigurableListableBeanFactory beanFactory, BeanDefinition definition,
110113
String name) throws Exception {
111-
BeanDefinition factoryDefinition = beanFactory.getBeanDefinition(definition
112-
.getFactoryBeanName());
113-
Class<?> factoryClass = ClassUtils.forName(factoryDefinition.getBeanClassName(),
114-
beanFactory.getBeanClassLoader());
115-
Method method = ReflectionUtils.findMethod(factoryClass,
116-
definition.getFactoryMethodName());
114+
Method method = getFactoryMethod(beanFactory, definition);
117115
Class<?> generic = ResolvableType.forMethodReturnType(method)
118116
.as(FactoryBean.class).resolveGeneric();
119117
if ((generic == null || generic.equals(Object.class))
@@ -124,6 +122,24 @@ private Class<?> getConfigurationClassFactoryBeanGeneric(
124122
return generic;
125123
}
126124

125+
private Method getFactoryMethod(ConfigurableListableBeanFactory beanFactory,
126+
BeanDefinition definition) throws Exception {
127+
if (definition instanceof AnnotatedBeanDefinition) {
128+
MethodMetadata factoryMethodMetadata = ((AnnotatedBeanDefinition) definition)
129+
.getFactoryMethodMetadata();
130+
if (factoryMethodMetadata instanceof StandardMethodMetadata) {
131+
return ((StandardMethodMetadata) factoryMethodMetadata)
132+
.getIntrospectedMethod();
133+
}
134+
}
135+
BeanDefinition factoryDefinition = beanFactory.getBeanDefinition(definition
136+
.getFactoryBeanName());
137+
Class<?> factoryClass = ClassUtils.forName(factoryDefinition.getBeanClassName(),
138+
beanFactory.getBeanClassLoader());
139+
return ReflectionUtils
140+
.findMethod(factoryClass, definition.getFactoryMethodName());
141+
}
142+
127143
private Class<?> getDirectFactoryBeanGeneric(
128144
ConfigurableListableBeanFactory beanFactory, BeanDefinition definition,
129145
String name) throws ClassNotFoundException, LinkageError {

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBeanTests.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818

1919
import org.junit.Test;
2020
import org.springframework.beans.factory.FactoryBean;
21+
import org.springframework.beans.factory.annotation.Value;
2122
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
2223
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
2324
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
25+
import org.springframework.boot.test.EnvironmentTestUtils;
2426
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
2527
import org.springframework.context.annotation.Bean;
2628
import org.springframework.context.annotation.Configuration;
@@ -127,6 +129,17 @@ public void testOnMissingBeanConditionWithFactoryBean() {
127129
equalTo("fromFactory"));
128130
}
129131

132+
@Test
133+
public void testOnMissingBeanConditionWithFactoryBeanWithBeanMethodArguments() {
134+
this.context.register(FactoryBeanWithBeanMethodArgumentsConfiguration.class,
135+
ConditionalOnFactoryBean.class,
136+
PropertyPlaceholderAutoConfiguration.class);
137+
EnvironmentTestUtils.addEnvironment(this.context, "theValue:foo");
138+
this.context.refresh();
139+
assertThat(this.context.getBean(ExampleBean.class).toString(),
140+
equalTo("fromFactory"));
141+
}
142+
130143
@Test
131144
public void testOnMissingBeanConditionWithConcreteFactoryBean() {
132145
this.context.register(ConcreteFactoryBeanConfiguration.class,
@@ -227,6 +240,15 @@ public FactoryBean<ExampleBean> exampleBeanFactoryBean() {
227240
}
228241
}
229242

243+
@Configuration
244+
protected static class FactoryBeanWithBeanMethodArgumentsConfiguration {
245+
@Bean
246+
public FactoryBean<ExampleBean> exampleBeanFactoryBean(
247+
@Value("${theValue}") String value) {
248+
return new ExampleFactoryBean(value);
249+
}
250+
}
251+
230252
@Configuration
231253
protected static class ConcreteFactoryBeanConfiguration {
232254
@Bean

0 commit comments

Comments
 (0)