diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
index 50430f75712..32ac333e0b0 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2023 the original author or authors.
+ * Copyright 2002-2024 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.
@@ -835,11 +835,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
/**
* This implementation attempts to query the FactoryBean's generic parameter metadata
* if present to determine the object type. If not present, i.e. the FactoryBean is
- * declared as a raw type, checks the FactoryBean's {@code getObjectType} method
+ * declared as a raw type, it checks the FactoryBean's {@code getObjectType} method
* on a plain instance of the FactoryBean, without bean properties applied yet.
- * If this doesn't return a type yet, and {@code allowInit} is {@code true} a
- * full creation of the FactoryBean is used as fallback (through delegation to the
- * superclass's implementation).
+ * If this doesn't return a type yet and {@code allowInit} is {@code true}, full
+ * creation of the FactoryBean is attempted as fallback (through delegation to the
+ * superclass implementation).
*
The shortcut check for a FactoryBean is only applied in case of a singleton
* FactoryBean. If the FactoryBean instance itself is not kept as singleton,
* it will be fully created to check the type of its exposed object.
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
index 61747b4fb24..43c4c624fad 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
@@ -1695,7 +1695,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
* already. The implementation is allowed to instantiate the target factory bean if
* {@code allowInit} is {@code true} and the type cannot be determined another way;
* otherwise it is restricted to introspecting signatures and related metadata.
- *
If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} if set on the bean definition
+ *
If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} is set on the bean definition
* and {@code allowInit} is {@code true}, the default implementation will create
* the FactoryBean via {@code getBean} to call its {@code getObjectType} method.
* Subclasses are encouraged to optimize this, typically by inspecting the generic
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanBeanEarlyDeductionTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanEarlyDeductionTests.java
similarity index 70%
rename from spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanBeanEarlyDeductionTests.java
rename to spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanEarlyDeductionTests.java
index 833e357f006..1290c38a086 100644
--- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanBeanEarlyDeductionTests.java
+++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanEarlyDeductionTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2024 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.
@@ -29,7 +29,9 @@ import org.springframework.beans.factory.support.AbstractBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
+import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.core.ResolvableType;
import org.springframework.core.type.AnnotationMetadata;
import static org.assertj.core.api.Assertions.assertThat;
@@ -39,51 +41,62 @@ import static org.assertj.core.api.Assertions.assertThat;
* {@link FactoryBean FactoryBeans} defined in the configuration.
*
* @author Phillip Webb
+ * @author Juergen Hoeller
*/
-public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
+class ConfigurationWithFactoryBeanEarlyDeductionTests {
@Test
- public void preFreezeDirect() {
+ void preFreezeDirect() {
assertPreFreeze(DirectConfiguration.class);
}
@Test
- public void postFreezeDirect() {
+ void postFreezeDirect() {
assertPostFreeze(DirectConfiguration.class);
}
@Test
- public void preFreezeGenericMethod() {
+ void preFreezeGenericMethod() {
assertPreFreeze(GenericMethodConfiguration.class);
}
@Test
- public void postFreezeGenericMethod() {
+ void postFreezeGenericMethod() {
assertPostFreeze(GenericMethodConfiguration.class);
}
@Test
- public void preFreezeGenericClass() {
+ void preFreezeGenericClass() {
assertPreFreeze(GenericClassConfiguration.class);
}
@Test
- public void postFreezeGenericClass() {
+ void postFreezeGenericClass() {
assertPostFreeze(GenericClassConfiguration.class);
}
@Test
- public void preFreezeAttribute() {
+ void preFreezeAttribute() {
assertPreFreeze(AttributeClassConfiguration.class);
}
@Test
- public void postFreezeAttribute() {
+ void postFreezeAttribute() {
assertPostFreeze(AttributeClassConfiguration.class);
}
@Test
- public void preFreezeUnresolvedGenericFactoryBean() {
+ void preFreezeTargetType() {
+ assertPreFreeze(TargetTypeConfiguration.class);
+ }
+
+ @Test
+ void postFreezeTargetType() {
+ assertPostFreeze(TargetTypeConfiguration.class);
+ }
+
+ @Test
+ void preFreezeUnresolvedGenericFactoryBean() {
// Covers the case where a @Configuration is picked up via component scanning
// and its bean definition only has a String bean class. In such cases
// beanDefinition.hasBeanClass() returns false so we need to actually
@@ -108,14 +121,13 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
}
}
+
private void assertPostFreeze(Class> configurationClass) {
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
- configurationClass);
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configurationClass);
assertContainsMyBeanName(context);
}
- private void assertPreFreeze(Class> configurationClass,
- BeanFactoryPostProcessor... postProcessors) {
+ private void assertPreFreeze(Class> configurationClass, BeanFactoryPostProcessor... postProcessors) {
NameCollectingBeanFactoryPostProcessor postProcessor = new NameCollectingBeanFactoryPostProcessor();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
try {
@@ -138,41 +150,38 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
assertThat(names).containsExactly("myBean");
}
- private static class NameCollectingBeanFactoryPostProcessor
- implements BeanFactoryPostProcessor {
+
+ private static class NameCollectingBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
private String[] names;
@Override
- public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
- throws BeansException {
- this.names = beanFactory.getBeanNamesForType(MyBean.class, true, false);
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ ResolvableType typeToMatch = ResolvableType.forClassWithGenerics(MyBean.class, String.class);
+ this.names = beanFactory.getBeanNamesForType(typeToMatch, true, false);
}
public String[] getNames() {
return this.names;
}
-
}
@Configuration
static class DirectConfiguration {
@Bean
- MyBean myBean() {
- return new MyBean();
+ MyBean myBean() {
+ return new MyBean<>();
}
-
}
@Configuration
static class GenericMethodConfiguration {
@Bean
- FactoryBean myBean() {
- return new TestFactoryBean<>(new MyBean());
+ FactoryBean> myBean() {
+ return new TestFactoryBean<>(new MyBean<>());
}
-
}
@Configuration
@@ -182,13 +191,11 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
MyFactoryBean myBean() {
return new MyFactoryBean();
}
-
}
@Configuration
@Import(AttributeClassRegistrar.class)
static class AttributeClassConfiguration {
-
}
static class AttributeClassRegistrar implements ImportBeanDefinitionRegistrar {
@@ -197,16 +204,34 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(
RawWithAbstractObjectTypeFactoryBean.class).getBeanDefinition();
- definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, MyBean.class);
+ definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE,
+ ResolvableType.forClassWithGenerics(MyBean.class, String.class));
registry.registerBeanDefinition("myBean", definition);
}
+ }
+ @Configuration
+ @Import(TargetTypeRegistrar.class)
+ static class TargetTypeConfiguration {
+ }
+
+ static class TargetTypeRegistrar implements ImportBeanDefinitionRegistrar {
+
+ @Override
+ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
+ RootBeanDefinition definition = new RootBeanDefinition(RawWithAbstractObjectTypeFactoryBean.class);
+ definition.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class,
+ ResolvableType.forClassWithGenerics(MyBean.class, String.class)));
+ definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE,
+ ResolvableType.forClassWithGenerics(MyBean.class, String.class));
+ registry.registerBeanDefinition("myBean", definition);
+ }
}
abstract static class AbstractMyBean {
}
- static class MyBean extends AbstractMyBean {
+ static class MyBean extends AbstractMyBean {
}
static class TestFactoryBean implements FactoryBean {
@@ -218,7 +243,7 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
}
@Override
- public T getObject() throws Exception {
+ public T getObject() {
return this.instance;
}
@@ -226,31 +251,26 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
public Class> getObjectType() {
return this.instance.getClass();
}
-
}
- static class MyFactoryBean extends TestFactoryBean {
+ static class MyFactoryBean extends TestFactoryBean> {
public MyFactoryBean() {
- super(new MyBean());
+ super(new MyBean<>());
}
-
}
static class RawWithAbstractObjectTypeFactoryBean implements FactoryBean