From e9f7c357b7ebd95c98bc1b159b6c8e665a0a0ec5 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 25 Nov 2018 21:27:19 +0100 Subject: [PATCH] ResolvableType-based matching consistently respects generic factory method return type (even for pre-initialized raw singleton instance) Issue: SPR-17524 (cherry picked from commit ebbe14c3630703d4245960116e0edc3c5813dc2d) --- .../factory/support/AbstractBeanFactory.java | 16 +- .../ConfigurationClassPostProcessorTests.java | 170 +++++++++++++++++- 2 files changed, 180 insertions(+), 6 deletions(-) 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 734aef5ad659..1e021cc904f7 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 @@ -509,12 +509,21 @@ 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(); - 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; @@ -1363,6 +1372,7 @@ public void clearMetadataCache() { */ protected Class resolveBeanClass(final RootBeanDefinition mbd, String beanName, final Class... typesToMatch) throws CannotLoadBeanClassException { + try { if (mbd.hasBeanClass()) { return mbd.getBeanClass(); 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 dcf2f373d51d..dd053d7b44a2 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 @@ -544,6 +544,10 @@ public void genericsBasedInjectionWithEarlyGenericsMatching() { 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 @@ -558,6 +562,78 @@ public void genericsBasedInjectionWithLateGenericsMatching() { 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 @@ -577,6 +653,10 @@ public void genericsBasedInjectionWithEarlyGenericsMatchingOnCglibProxy() { 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"))); } @@ -598,12 +678,16 @@ public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxy() { 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); @@ -619,6 +703,35 @@ public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawFact 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"))); } @@ -638,6 +751,10 @@ public void genericsBasedInjectionWithEarlyGenericsMatchingOnJdkProxy() { 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"))); } @@ -658,12 +775,16 @@ public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxy() { 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); @@ -678,6 +799,34 @@ public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactor 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"))); } @@ -980,7 +1129,7 @@ public String toString() { } @Configuration - public static class RawRepositoryConfiguration { + public static class RawFactoryMethodRepositoryConfiguration { @Bean public Repository stringRepo() { @@ -993,6 +1142,21 @@ public String toString() { } } + @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 {