ResolvableType-based matching consistently respects generic factory method return type (even for pre-initialized raw singleton instance)
Issue: SPR-17524
This commit is contained in:
		
							parent
							
								
									c2e545776d
								
							
						
					
					
						commit
						ebbe14c363
					
				|  | @ -510,12 +510,21 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp | ||||||
| 					// Generics potentially only match on the target class, not on the proxy... | 					// Generics potentially only match on the target class, not on the proxy... | ||||||
| 					RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); | 					RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); | ||||||
| 					Class<?> targetType = mbd.getTargetType(); | 					Class<?> targetType = mbd.getTargetType(); | ||||||
| 					if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance) && | 					if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance)) { | ||||||
| 							typeToMatch.isAssignableFrom(targetType)) { |  | ||||||
| 						// Check raw class match as well, making sure it's exposed on the proxy. | 						// Check raw class match as well, making sure it's exposed on the proxy. | ||||||
| 						Class<?> classToMatch = typeToMatch.resolve(); | 						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; | 			return false; | ||||||
|  |  | ||||||
|  | @ -571,6 +571,10 @@ public class ConfigurationClassPostProcessorTests { | ||||||
| 		beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class)); | 		beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class)); | ||||||
| 		assertEquals(1, beanNames.length); | 		assertEquals(1, beanNames.length); | ||||||
| 		assertEquals("stringRepo", beanNames[0]); | 		assertEquals("stringRepo", beanNames[0]); | ||||||
|  | 
 | ||||||
|  | 		beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class)); | ||||||
|  | 		assertEquals(1, beanNames.length); | ||||||
|  | 		assertEquals("stringRepo", beanNames[0]); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
|  | @ -585,6 +589,78 @@ public class ConfigurationClassPostProcessorTests { | ||||||
| 		beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class)); | 		beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class)); | ||||||
| 		assertEquals(1, beanNames.length); | 		assertEquals(1, beanNames.length); | ||||||
| 		assertEquals("stringRepo", beanNames[0]); | 		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 | 	@Test | ||||||
|  | @ -604,6 +680,10 @@ public class ConfigurationClassPostProcessorTests { | ||||||
| 		assertEquals(1, beanNames.length); | 		assertEquals(1, beanNames.length); | ||||||
| 		assertEquals("stringRepo", beanNames[0]); | 		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"))); | 		assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo"))); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -625,12 +705,16 @@ public class ConfigurationClassPostProcessorTests { | ||||||
| 		assertEquals(1, beanNames.length); | 		assertEquals(1, beanNames.length); | ||||||
| 		assertEquals("stringRepo", beanNames[0]); | 		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"))); | 		assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo"))); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
| 	public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawFactoryMethod() { | 	public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawFactoryMethod() { | ||||||
| 		beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class)); | 		beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class)); | ||||||
| 		new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); | 		new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); | ||||||
| 		DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); | 		DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); | ||||||
| 		autoProxyCreator.setProxyTargetClass(true); | 		autoProxyCreator.setProxyTargetClass(true); | ||||||
|  | @ -646,6 +730,35 @@ public class ConfigurationClassPostProcessorTests { | ||||||
| 		assertEquals(1, beanNames.length); | 		assertEquals(1, beanNames.length); | ||||||
| 		assertEquals("stringRepo", beanNames[0]); | 		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"))); | 		assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo"))); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -665,6 +778,10 @@ public class ConfigurationClassPostProcessorTests { | ||||||
| 		assertEquals(1, beanNames.length); | 		assertEquals(1, beanNames.length); | ||||||
| 		assertEquals("stringRepo", beanNames[0]); | 		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"))); | 		assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo"))); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -685,12 +802,16 @@ public class ConfigurationClassPostProcessorTests { | ||||||
| 		assertEquals(1, beanNames.length); | 		assertEquals(1, beanNames.length); | ||||||
| 		assertEquals("stringRepo", beanNames[0]); | 		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"))); | 		assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo"))); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
| 	public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactoryMethod() { | 	public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactoryMethod() { | ||||||
| 		beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class)); | 		beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class)); | ||||||
| 		new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); | 		new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); | ||||||
| 		DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); | 		DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); | ||||||
| 		autoProxyCreator.setBeanFactory(beanFactory); | 		autoProxyCreator.setBeanFactory(beanFactory); | ||||||
|  | @ -705,6 +826,34 @@ public class ConfigurationClassPostProcessorTests { | ||||||
| 		assertEquals(1, beanNames.length); | 		assertEquals(1, beanNames.length); | ||||||
| 		assertEquals("stringRepo", beanNames[0]); | 		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"))); | 		assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo"))); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -1125,7 +1274,7 @@ public class ConfigurationClassPostProcessorTests { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Configuration | 	@Configuration | ||||||
| 	public static class RawRepositoryConfiguration { | 	public static class RawFactoryMethodRepositoryConfiguration { | ||||||
| 
 | 
 | ||||||
| 		@Bean | 		@Bean | ||||||
| 		public Repository stringRepo() { | 		public Repository stringRepo() { | ||||||
|  | @ -1138,6 +1287,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 | 	@Configuration | ||||||
| 	public static class ScopedRepositoryConfiguration { | 	public static class ScopedRepositoryConfiguration { | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue