From 709ac28f29ea1b8a98c6a292f27d3079954fbcd4 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 21 Jan 2014 01:26:30 +0100 Subject: [PATCH] Consider wildcard type without bounds as eligible for fallback match too Issue: SPR-11250 --- .../ConfigurationClassPostProcessorTests.java | 108 +++++++++++++++++- .../springframework/core/ResolvableType.java | 19 ++- 2 files changed, 123 insertions(+), 4 deletions(-) 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 c5025c55f05..0dc3d58cca6 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -254,7 +254,34 @@ public class ConfigurationClassPostProcessorTests { ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); pp.postProcessBeanFactory(beanFactory); - assertSame(beanFactory.getBean("repo"), beanFactory.getBean("repoConsumer")); + assertSame(beanFactory.getBean("rawRepo"), beanFactory.getBean("repoConsumer")); + } + + @Test + public void testGenericsBasedInjectionWithWildcardMatch() { + beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(WildcardMatchingConfiguration.class)); + ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); + pp.postProcessBeanFactory(beanFactory); + + assertSame(beanFactory.getBean("genericRepo"), beanFactory.getBean("repoConsumer")); + } + + @Test + public void testGenericsBasedInjectionWithWildcardWithExtendsMatch() { + beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(WildcardWithExtendsConfiguration.class)); + ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); + pp.postProcessBeanFactory(beanFactory); + + assertSame(beanFactory.getBean("stringRepo"), beanFactory.getBean("repoConsumer")); + } + + @Test + public void testGenericsBasedInjectionWithWildcardWithGenericExtendsMatch() { + beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(WildcardWithGenericExtendsConfiguration.class)); + ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); + pp.postProcessBeanFactory(beanFactory); + + assertSame(beanFactory.getBean("genericRepo"), beanFactory.getBean("repoConsumer")); } @@ -362,6 +389,16 @@ public class ConfigurationClassPostProcessorTests { } }; } + + @Bean + public Repository genericRepo() { + return new Repository() { + @Override + public String toString() { + return "Repository"; + } + }; + } } @@ -387,6 +424,16 @@ public class ConfigurationClassPostProcessorTests { } }; } + + @Bean @Scope("prototype") + public Repository genericRepo() { + return new Repository() { + @Override + public String toString() { + return "Repository"; + } + }; + } } @@ -461,7 +508,22 @@ public class ConfigurationClassPostProcessorTests { public static class RawMatchingConfiguration { @Bean - public Repository repo() { + public Repository rawRepo() { + return new Repository(); + } + + @Bean + public Object repoConsumer(Repository repo) { + return repo; + } + } + + + @Configuration + public static class WildcardMatchingConfiguration { + + @Bean + public Repository genericRepo() { return new Repository(); } @@ -471,4 +533,44 @@ public class ConfigurationClassPostProcessorTests { } } + + @Configuration + public static class WildcardWithExtendsConfiguration { + + @Bean + public Repository stringRepo() { + return new Repository(); + } + + @Bean + public Repository numberRepo() { + return new Repository(); + } + + @Bean + public Object repoConsumer(Repository repo) { + return repo; + } + } + + + @Configuration + public static class WildcardWithGenericExtendsConfiguration { + + @Bean + public Repository genericRepo() { + return new Repository(); + } + + @Bean + public Repository numberRepo() { + return new Repository(); + } + + @Bean + public Object repoConsumer(Repository repo) { + return repo; + } + } + } 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 8c678723afc..57160e856b0 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -399,7 +399,7 @@ public final class ResolvableType implements Serializable { } ResolvableType[] generics = getGenerics(); for (ResolvableType generic : generics) { - if (generic.isUnresolvableTypeVariable()) { + if (generic.isUnresolvableTypeVariable() || generic.isWildcardWithoutBounds()) { return true; } } @@ -435,6 +435,23 @@ public final class ResolvableType implements Serializable { return false; } + /** + * Determine whether the underlying type represents a wildcard + * without specific bounds (i.e., equal to {@code ? extends Object}). + */ + private boolean isWildcardWithoutBounds() { + if (this.type instanceof WildcardType) { + WildcardType wt = (WildcardType) this.type; + if (wt.getLowerBounds().length == 0) { + Type[] upperBounds = wt.getUpperBounds(); + if (upperBounds.length == 0 || (upperBounds.length == 1 && Object.class.equals(upperBounds[0]))) { + return true; + } + } + } + return false; + } + /** * Return a {@link ResolvableType} for the specified nesting level. See * {@link #getNested(int, Map)} for details.