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 32af62487c5e..c488bc61766b 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 @@ -29,6 +29,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.function.UnaryOperator; import org.springframework.beans.BeanUtils; @@ -583,6 +584,27 @@ else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) { // Generics potentially only match on the target class, not on the proxy... RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); Class targetType = mbd.getTargetType(); + + String scope = mbd.getScope(); + if (targetType == null && scope != null && !scope.isEmpty()) { + String targetBeanName = "scopedTarget." + beanName; + if (containsBeanDefinition(targetBeanName)) { + RootBeanDefinition targetMbd = getMergedLocalBeanDefinition(targetBeanName); + + ResolvableType targetResolvableType = targetMbd.targetType; + if (targetResolvableType == null) { + targetResolvableType = targetMbd.factoryMethodReturnType; + if (targetResolvableType == null) { + targetResolvableType = ResolvableType.forClass(targetMbd.getBeanClass()); + } + } + + if (typeToMatch.isAssignableFrom(targetResolvableType)) { + return true; + } + } + } + if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance)) { // Check raw class match as well, making sure it's exposed on the proxy. Class classToMatch = typeToMatch.resolve(); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/GenericTypeMatchingTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/GenericTypeMatchingTests.java new file mode 100644 index 000000000000..7a7fb7fd380e --- /dev/null +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/GenericTypeMatchingTests.java @@ -0,0 +1,46 @@ +package org.springframework.beans.factory.support; + +import org.junit.jupiter.api.Test; +import org.springframework.core.ResolvableType; + +import java.util.UUID; +import java.util.function.Supplier; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link AbstractBeanFactory#isTypeMatch} with scoped proxy beans that use generic types. + */ +class ScopedProxyGenericTypeMatchTests { + + @Test + void scopedProxyBeanTypeMatching() { + DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); + + String proxyBeanName = "wordBean-" + UUID.randomUUID(); + String targetBeanName = "scopedTarget." + proxyBeanName; + + RootBeanDefinition targetDef = new RootBeanDefinition(SomeGenericSupplier.class); + targetDef.setScope("request"); + factory.registerBeanDefinition(targetBeanName, targetDef); + + RootBeanDefinition proxyDef = new RootBeanDefinition(); + proxyDef.setScope("singleton"); + proxyDef.setTargetType(ResolvableType.forClassWithGenerics(Supplier.class, String.class)); + proxyDef.setAttribute("targetBeanName", targetBeanName); + factory.registerBeanDefinition(proxyBeanName, proxyDef); + + ResolvableType supplierType = ResolvableType.forClassWithGenerics(Supplier.class, String.class); + + assertThat(factory.isTypeMatch(proxyBeanName, supplierType)).isTrue(); + + assertThat(factory.getBeanNamesForType(supplierType)).contains(proxyBeanName); + } + + static class SomeGenericSupplier implements Supplier { + @Override + public String get() { + return "value"; + } + } +} \ No newline at end of file