diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java index 3a3c84b11a1..72ab84d4f8e 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.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. @@ -108,7 +108,8 @@ public class GenericTypeAwareAutowireCandidateResolver extends SimpleAutowireCan Class resolvedClass = targetType.resolve(); if (resolvedClass != null && FactoryBean.class.isAssignableFrom(resolvedClass)) { Class typeToBeMatched = dependencyType.resolve(); - if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched)) { + if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched) && + !typeToBeMatched.isAssignableFrom(resolvedClass)) { targetType = targetType.getGeneric(); if (descriptor.fallbackMatchAllowed()) { // Matching the Class-based type determination for FactoryBean diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java index 366a1549474..13bf0a9275f 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java @@ -32,6 +32,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.TypedStringValue; @@ -850,6 +851,23 @@ class BeanFactoryGenericsTests { bf.getBean("store2", NumberStore.class), bf.getBean("store1", NumberStore.class)); } + @Test // gh-32489 + void genericMatchingAgainstFactoryBeanClass() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.setAutowireCandidateResolver(new GenericTypeAwareAutowireCandidateResolver()); + + RootBeanDefinition bd = new RootBeanDefinition(MyFactoryBean.class); + // Replicate org.springframework.data.repository.config.RepositoryConfigurationDelegate#registerRepositoriesIn + // behavior of setting targetType, required to hit other branch in + // org.springframework.beans.factory.support.GenericTypeAwareAutowireCandidateResolver.checkGenericTypeMatch + bd.setTargetType(ResolvableType.forClassWithGenerics(MyFactoryBean.class, String.class)); + bf.registerBeanDefinition("myFactoryBean", bd); + bf.registerBeanDefinition("myFactoryBeanHolder", + new RootBeanDefinition(MyFactoryBeanHolder.class, AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR, false)); + + assertThat(bf.getBean(MyFactoryBeanHolder.class).factoryBeans).contains(bf.getBean(MyFactoryBean.class)); + } + /** * Mimics and delegates to {@link Mockito#mock(Class)} -- created here to avoid factory @@ -966,4 +984,32 @@ class BeanFactoryGenericsTests { } } + + public interface MyGenericInterfaceForFactoryBeans { + } + + + public static class MyFactoryBean implements FactoryBean, MyGenericInterfaceForFactoryBeans { + + @Override + public T getObject() { + throw new UnsupportedOperationException(); + } + + @Override + public Class getObjectType() { + return String.class; + } + } + + + public static class MyFactoryBeanHolder { + + List> factoryBeans; // Requested type is not a FactoryBean type + + public MyFactoryBeanHolder(List> factoryBeans) { + this.factoryBeans = factoryBeans; + } + } + }