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 d46b11437b6..686fbea7fe3 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 @@ -510,12 +510,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; diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java index 3e4a496362e..b8daeec4de3 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java @@ -571,6 +571,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 @@ -585,6 +589,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 @@ -604,6 +680,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"))); } @@ -625,12 +705,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); @@ -646,6 +730,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"))); } @@ -665,6 +778,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"))); } @@ -685,12 +802,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); @@ -705,6 +826,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"))); } @@ -1125,7 +1274,7 @@ public class ConfigurationClassPostProcessorTests { } @Configuration - public static class RawRepositoryConfiguration { + public static class RawFactoryMethodRepositoryConfiguration { @Bean public Repository stringRepo() { @@ -1138,6 +1287,21 @@ public class ConfigurationClassPostProcessorTests { } } + @Configuration + public static class RawInstanceRepositoryConfiguration { + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Bean + public Repository stringRepo() { + return new Repository() { + @Override + public String toString() { + return "Repository"; + } + }; + } + } + @Configuration public static class ScopedRepositoryConfiguration {