From 03cd38f1b1393bda5f2490a95e12dd64580689d6 Mon Sep 17 00:00:00 2001 From: currenjin Date: Wed, 5 Mar 2025 10:24:23 +0900 Subject: [PATCH] Fix: type matching for request-scope generic beans Signed-off-by: currenjin --- .../factory/support/AbstractBeanFactory.java | 22 +++++++++ .../support/GenericTypeMatchingTests.java | 46 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 spring-beans/src/test/java/org/springframework/beans/factory/support/GenericTypeMatchingTests.java 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 32af62487c..ede0a1469e 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.Set; 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 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp // 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 0000000000..9678a25f59 --- /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.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(); + + RootBeanDefinition targetDef = new RootBeanDefinition(SomeGenericSupplier.class); + targetDef.setScope("request"); + factory.registerBeanDefinition("scopedTarget.wordBean", targetDef); + + RootBeanDefinition proxyDef = new RootBeanDefinition(); + proxyDef.setScope("singleton"); + proxyDef.setTargetType(ResolvableType.forClassWithGenerics(Supplier.class, String.class)); + proxyDef.setAttribute("targetBeanName", "scopedTarget.wordBean"); + factory.registerBeanDefinition("wordBean", proxyDef); + + ResolvableType supplierType = ResolvableType.forClassWithGenerics(Supplier.class, String.class); + + boolean isMatch = factory.isTypeMatch("wordBean", supplierType); + + + assertThat(isMatch).isTrue(); + + String[] names = factory.getBeanNamesForType(supplierType); + assertThat(names).contains("wordBean"); + } + + static class SomeGenericSupplier implements Supplier { + @Override + public String get() { + return "value"; + } + } +} \ No newline at end of file