From f805427629ff00eddf511fe3356b515174f2fb17 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 26 Dec 2016 11:23:07 +0100 Subject: [PATCH] Detect generic type match behind interface-based proxy as well Issue: SPR-14097 --- .../AbstractAutowireCapableBeanFactory.java | 1 + .../factory/support/AbstractBeanFactory.java | 258 +++++++++--------- .../ConfigurationClassPostProcessorTests.java | 148 +++++++++- .../springframework/core/ResolvableType.java | 26 +- 4 files changed, 295 insertions(+), 138 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index dadb3fba83c..b15e3b8966f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -528,6 +528,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac } final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); + mbd.resolvedTargetType = beanType; // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { 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 529d96f12a0..94839bdb3f7 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 @@ -405,33 +405,31 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp return true; } - else { - // No singleton instance found -> check bean definition. - BeanFactory parentBeanFactory = getParentBeanFactory(); - if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { - // No bean definition found in this factory -> delegate to parent. - return parentBeanFactory.isSingleton(originalBeanName(name)); - } + // No singleton instance found -> check bean definition. + BeanFactory parentBeanFactory = getParentBeanFactory(); + if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { + // No bean definition found in this factory -> delegate to parent. + return parentBeanFactory.isSingleton(originalBeanName(name)); + } - RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); + RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); - // In case of FactoryBean, return singleton status of created object if not a dereference. - if (mbd.isSingleton()) { - if (isFactoryBean(beanName, mbd)) { - if (BeanFactoryUtils.isFactoryDereference(name)) { - return true; - } - FactoryBean factoryBean = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName); - return factoryBean.isSingleton(); - } - else { - return !BeanFactoryUtils.isFactoryDereference(name); + // In case of FactoryBean, return singleton status of created object if not a dereference. + if (mbd.isSingleton()) { + if (isFactoryBean(beanName, mbd)) { + if (BeanFactoryUtils.isFactoryDereference(name)) { + return true; } + FactoryBean factoryBean = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName); + return factoryBean.isSingleton(); } else { - return false; + return !BeanFactoryUtils.isFactoryDereference(name); } } + else { + return false; + } } @Override @@ -449,32 +447,31 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp // In case of FactoryBean, return singleton status of created object if not a dereference. return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(beanName, mbd)); } - else { - // Singleton or scoped - not a prototype. - // However, FactoryBean may still produce a prototype object... - if (BeanFactoryUtils.isFactoryDereference(name)) { - return false; - } - if (isFactoryBean(beanName, mbd)) { - final FactoryBean fb = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName); - if (System.getSecurityManager() != null) { - return AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Boolean run() { - return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean) fb).isPrototype()) || - !fb.isSingleton()); - } - }, getAccessControlContext()); - } - else { - return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean) fb).isPrototype()) || - !fb.isSingleton()); - } + + // Singleton or scoped - not a prototype. + // However, FactoryBean may still produce a prototype object... + if (BeanFactoryUtils.isFactoryDereference(name)) { + return false; + } + if (isFactoryBean(beanName, mbd)) { + final FactoryBean fb = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName); + if (System.getSecurityManager() != null) { + return AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Boolean run() { + return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean) fb).isPrototype()) || + !fb.isSingleton()); + } + }, getAccessControlContext()); } else { - return false; + return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean) fb).isPrototype()) || + !fb.isSingleton()); } } + else { + return false; + } } @Override @@ -493,78 +490,91 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp return typeToMatch.isInstance(beanInstance); } } - else { - return (!BeanFactoryUtils.isFactoryDereference(name) && typeToMatch.isInstance(beanInstance)); + else if (!BeanFactoryUtils.isFactoryDereference(name)) { + if (typeToMatch.isInstance(beanInstance)) { + // Direct match for exposed instance? + return true; + } + 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)) { + // Check raw class match as well, making sure it's exposed on the proxy. + Class classToMatch = typeToMatch.resolve(); + return (classToMatch == null || classToMatch.isInstance(beanInstance)); + } + } } + return false; } else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) { // null instance registered return false; } - else { - // No singleton instance found -> check bean definition. - BeanFactory parentBeanFactory = getParentBeanFactory(); - if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { - // No bean definition found in this factory -> delegate to parent. - return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch); - } + // No singleton instance found -> check bean definition. + BeanFactory parentBeanFactory = getParentBeanFactory(); + if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { + // No bean definition found in this factory -> delegate to parent. + return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch); + } - // Retrieve corresponding bean definition. - RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); + // Retrieve corresponding bean definition. + RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); - Class classToMatch = typeToMatch.resolve(); - if (classToMatch == null) { - classToMatch = FactoryBean.class; - } - Class[] typesToMatch = (FactoryBean.class == classToMatch ? - new Class[] {classToMatch} : new Class[] {FactoryBean.class, classToMatch}); - - // Check decorated bean definition, if any: We assume it'll be easier - // to determine the decorated bean's type than the proxy's type. - BeanDefinitionHolder dbd = mbd.getDecoratedDefinition(); - if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) { - RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd); - Class targetClass = predictBeanType(dbd.getBeanName(), tbd, typesToMatch); - if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) { - return typeToMatch.isAssignableFrom(targetClass); - } + Class classToMatch = typeToMatch.resolve(); + if (classToMatch == null) { + classToMatch = FactoryBean.class; + } + Class[] typesToMatch = (FactoryBean.class == classToMatch ? + new Class[] {classToMatch} : new Class[] {FactoryBean.class, classToMatch}); + + // Check decorated bean definition, if any: We assume it'll be easier + // to determine the decorated bean's type than the proxy's type. + BeanDefinitionHolder dbd = mbd.getDecoratedDefinition(); + if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) { + RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd); + Class targetClass = predictBeanType(dbd.getBeanName(), tbd, typesToMatch); + if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) { + return typeToMatch.isAssignableFrom(targetClass); } + } - Class beanType = predictBeanType(beanName, mbd, typesToMatch); - if (beanType == null) { - return false; - } + Class beanType = predictBeanType(beanName, mbd, typesToMatch); + if (beanType == null) { + return false; + } - // Check bean class whether we're dealing with a FactoryBean. - if (FactoryBean.class.isAssignableFrom(beanType)) { - if (!BeanFactoryUtils.isFactoryDereference(name)) { - // If it's a FactoryBean, we want to look at what it creates, not the factory class. - beanType = getTypeForFactoryBean(beanName, mbd); - if (beanType == null) { - return false; - } - } - } - else if (BeanFactoryUtils.isFactoryDereference(name)) { - // Special case: A SmartInstantiationAwareBeanPostProcessor returned a non-FactoryBean - // type but we nevertheless are being asked to dereference a FactoryBean... - // Let's check the original bean class and proceed with it if it is a FactoryBean. - beanType = predictBeanType(beanName, mbd, FactoryBean.class); - if (beanType == null || !FactoryBean.class.isAssignableFrom(beanType)) { + // Check bean class whether we're dealing with a FactoryBean. + if (FactoryBean.class.isAssignableFrom(beanType)) { + if (!BeanFactoryUtils.isFactoryDereference(name)) { + // If it's a FactoryBean, we want to look at what it creates, not the factory class. + beanType = getTypeForFactoryBean(beanName, mbd); + if (beanType == null) { return false; } } - - ResolvableType resolvableType = mbd.targetType; - if (resolvableType == null) { - resolvableType = mbd.factoryMethodReturnType; - } - if (resolvableType != null && resolvableType.resolve() == beanType) { - return typeToMatch.isAssignableFrom(resolvableType); + } + else if (BeanFactoryUtils.isFactoryDereference(name)) { + // Special case: A SmartInstantiationAwareBeanPostProcessor returned a non-FactoryBean + // type but we nevertheless are being asked to dereference a FactoryBean... + // Let's check the original bean class and proceed with it if it is a FactoryBean. + beanType = predictBeanType(beanName, mbd, FactoryBean.class); + if (beanType == null || !FactoryBean.class.isAssignableFrom(beanType)) { + return false; } - return typeToMatch.isAssignableFrom(beanType); } + + ResolvableType resolvableType = mbd.targetType; + if (resolvableType == null) { + resolvableType = mbd.factoryMethodReturnType; + } + if (resolvableType != null && resolvableType.resolve() == beanType) { + return typeToMatch.isAssignableFrom(resolvableType); + } + return typeToMatch.isAssignableFrom(beanType); } @Override @@ -591,43 +601,41 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp return null; } - else { - // No singleton instance found -> check bean definition. - BeanFactory parentBeanFactory = getParentBeanFactory(); - if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { - // No bean definition found in this factory -> delegate to parent. - return parentBeanFactory.getType(originalBeanName(name)); - } + // No singleton instance found -> check bean definition. + BeanFactory parentBeanFactory = getParentBeanFactory(); + if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { + // No bean definition found in this factory -> delegate to parent. + return parentBeanFactory.getType(originalBeanName(name)); + } - RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); + RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); - // Check decorated bean definition, if any: We assume it'll be easier - // to determine the decorated bean's type than the proxy's type. - BeanDefinitionHolder dbd = mbd.getDecoratedDefinition(); - if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) { - RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd); - Class targetClass = predictBeanType(dbd.getBeanName(), tbd); - if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) { - return targetClass; - } + // Check decorated bean definition, if any: We assume it'll be easier + // to determine the decorated bean's type than the proxy's type. + BeanDefinitionHolder dbd = mbd.getDecoratedDefinition(); + if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) { + RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd); + Class targetClass = predictBeanType(dbd.getBeanName(), tbd); + if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) { + return targetClass; } + } - Class beanClass = predictBeanType(beanName, mbd); + Class beanClass = predictBeanType(beanName, mbd); - // Check bean class whether we're dealing with a FactoryBean. - if (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass)) { - if (!BeanFactoryUtils.isFactoryDereference(name)) { - // If it's a FactoryBean, we want to look at what it creates, not at the factory class. - return getTypeForFactoryBean(beanName, mbd); - } - else { - return beanClass; - } + // Check bean class whether we're dealing with a FactoryBean. + if (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass)) { + if (!BeanFactoryUtils.isFactoryDereference(name)) { + // If it's a FactoryBean, we want to look at what it creates, not at the factory class. + return getTypeForFactoryBean(beanName, mbd); } else { - return (!BeanFactoryUtils.isFactoryDereference(name) ? beanClass : null); + return beanClass; } } + else { + return (!BeanFactoryUtils.isFactoryDereference(name) ? beanClass : null); + } } @Override 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 6af30c6fddd..85e888672cc 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 @@ -26,9 +26,12 @@ import javax.annotation.PostConstruct; import org.junit.Before; import org.junit.Test; +import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; +import org.springframework.aop.interceptor.SimpleTraceInterceptor; import org.springframework.aop.scope.ScopedObject; import org.springframework.aop.scope.ScopedProxyUtils; import org.springframework.aop.support.AopUtils; +import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.FactoryBean; @@ -524,6 +527,19 @@ public class ConfigurationClassPostProcessorTests { assertSame(beanFactory.getBean("genericRepo"), beanFactory.getBean("repoConsumer")); } + @Test + public void genericsBasedInjectionWithEarlyGenericsMatching() { + beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.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]); + } + @Test public void genericsBasedInjectionWithLateGenericsMatching() { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class)); @@ -539,9 +555,35 @@ public class ConfigurationClassPostProcessorTests { } @Test - public void genericsBasedInjectionWithEarlyGenericsMatching() { + public void genericsBasedInjectionWithEarlyGenericsMatchingOnCglibProxy() { + beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.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())); + + 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]); + + assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo"))); + } + + @Test + public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxy() { beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.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")); @@ -549,6 +591,88 @@ public class ConfigurationClassPostProcessorTests { 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)); + 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]); + + assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo"))); + } + + @Test + public void genericsBasedInjectionWithEarlyGenericsMatchingOnJdkProxy() { + beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class)); + new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory); + DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); + autoProxyCreator.setBeanFactory(beanFactory); + beanFactory.addBeanPostProcessor(autoProxyCreator); + beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor())); + + 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]); + + assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo"))); + } + + @Test + public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxy() { + beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.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]); + + assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo"))); + } + + @Test + public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactoryMethod() { + beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.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]); + + assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo"))); } @Test @@ -752,7 +876,12 @@ public class ConfigurationClassPostProcessorTests { } } - public static class Repository { + public interface RepositoryInterface { + + String toString(); + } + + public static class Repository implements RepositoryInterface { } public static class GenericRepository extends Repository { @@ -819,6 +948,21 @@ public class ConfigurationClassPostProcessorTests { } } + + @Configuration + public static class RawRepositoryConfiguration { + + @Bean + public Repository stringRepo() { + return new Repository() { + @Override + public String toString() { + return "Repository"; + } + }; + } + } + @Configuration public static class ScopedRepositoryConfiguration { diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index 0f99c04cd8a..528d7cfdbfb 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -624,19 +624,19 @@ public class ResolvableType implements Serializable { * @see #resolveGenerics() */ public ResolvableType getGeneric(int... indexes) { - try { - if (indexes == null || indexes.length == 0) { - return getGenerics()[0]; - } - ResolvableType generic = this; - for (int index : indexes) { - generic = generic.getGenerics()[index]; + ResolvableType[] generics = getGenerics(); + if (indexes == null || indexes.length == 0) { + return (generics.length == 0 ? NONE : generics[0]); + } + ResolvableType generic = this; + for (int index : indexes) { + generics = generic.getGenerics(); + if (index < 0 || index >= generics.length) { + return NONE; } - return generic; - } - catch (IndexOutOfBoundsException ex) { - return NONE; + generic = generics[index]; } + return generic; } /** @@ -947,6 +947,10 @@ public class ResolvableType implements Serializable { */ public static ResolvableType forRawClass(Class clazz) { return new ResolvableType(clazz) { + @Override + public ResolvableType[] getGenerics() { + return EMPTY_TYPES_ARRAY; + } @Override public boolean isAssignableFrom(Class other) { return ClassUtils.isAssignable(getRawClass(), other);