ResolvableType-based matching consistently respects generic factory method return type (even for pre-initialized raw singleton instance)
Issue: SPR-17524
(cherry picked from commit ebbe14c363)
This commit is contained in:
@@ -507,12 +507,21 @@ 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();
|
||||
if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance) &&
|
||||
typeToMatch.isAssignableFrom(targetType)) {
|
||||
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();
|
||||
return (classToMatch == null || classToMatch.isInstance(beanInstance));
|
||||
if (classToMatch != null && !classToMatch.isInstance(beanInstance)) {
|
||||
return false;
|
||||
}
|
||||
if (typeToMatch.isAssignableFrom(targetType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ResolvableType resolvableType = mbd.targetType;
|
||||
if (resolvableType == null) {
|
||||
resolvableType = mbd.factoryMethodReturnType;
|
||||
}
|
||||
return (resolvableType != null && typeToMatch.isAssignableFrom(resolvableType));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -1359,6 +1368,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
@Nullable
|
||||
protected Class<?> resolveBeanClass(final RootBeanDefinition mbd, String beanName, final Class<?>... typesToMatch)
|
||||
throws CannotLoadBeanClassException {
|
||||
|
||||
try {
|
||||
if (mbd.hasBeanClass()) {
|
||||
return mbd.getBeanClass();
|
||||
|
||||
@@ -570,6 +570,10 @@ public class ConfigurationClassPostProcessorTests {
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -584,6 +588,78 @@ public class ConfigurationClassPostProcessorTests {
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithEarlyGenericsMatchingAndRawFactoryMethod() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(0, beanNames.length);
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(0, beanNames.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithLateGenericsMatchingAndRawFactoryMethod() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
beanFactory.preInstantiateSingletons();
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithEarlyGenericsMatchingAndRawInstance() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithLateGenericsMatchingAndRawInstance() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
beanFactory.preInstantiateSingletons();
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -603,6 +679,10 @@ public class ConfigurationClassPostProcessorTests {
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
|
||||
}
|
||||
|
||||
@@ -624,12 +704,16 @@ public class ConfigurationClassPostProcessorTests {
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawFactoryMethod() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class));
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
autoProxyCreator.setProxyTargetClass(true);
|
||||
@@ -645,6 +729,35 @@ public class ConfigurationClassPostProcessorTests {
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawInstance() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
autoProxyCreator.setProxyTargetClass(true);
|
||||
autoProxyCreator.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(autoProxyCreator);
|
||||
beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
|
||||
beanFactory.preInstantiateSingletons();
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
|
||||
}
|
||||
|
||||
@@ -664,6 +777,10 @@ public class ConfigurationClassPostProcessorTests {
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
|
||||
}
|
||||
|
||||
@@ -684,12 +801,16 @@ public class ConfigurationClassPostProcessorTests {
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactoryMethod() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class));
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
autoProxyCreator.setBeanFactory(beanFactory);
|
||||
@@ -704,6 +825,34 @@ public class ConfigurationClassPostProcessorTests {
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawInstance() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
autoProxyCreator.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(autoProxyCreator);
|
||||
beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
|
||||
beanFactory.preInstantiateSingletons();
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(RepositoryInterface.class);
|
||||
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("stringRepo", beanNames[0]);
|
||||
|
||||
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
|
||||
}
|
||||
|
||||
@@ -1073,7 +1222,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class RawRepositoryConfiguration {
|
||||
public static class RawFactoryMethodRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public Repository stringRepo() {
|
||||
@@ -1086,6 +1235,21 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class RawInstanceRepositoryConfiguration {
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Bean
|
||||
public Repository<String> stringRepo() {
|
||||
return new Repository() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Repository<String>";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class ScopedRepositoryConfiguration {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user