Allow generics-based injection in case of nested unresolved type variable

Issue: SPR-11471
master
Juergen Hoeller 11 years ago
parent 49d7bda722
commit 793cab4f80
  1. 116
      spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java
  2. 7
      spring-core/src/main/java/org/springframework/core/ResolvableType.java

@ -1647,6 +1647,59 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertSame(ngr, bean.integerRepositoryMap.get("simpleRepo"));
}
@Test
public void testGenericsBasedInjectionIntoMatchingTypeVariable() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
RootBeanDefinition bd = new RootBeanDefinition(GenericInterface1Impl.class);
bd.setFactoryMethodName("create");
bf.registerBeanDefinition("bean1", bd);
bf.registerBeanDefinition("bean2", new RootBeanDefinition(GenericInterface2Impl.class));
GenericInterface1Impl bean1 = (GenericInterface1Impl) bf.getBean("bean1");
GenericInterface2Impl bean2 = (GenericInterface2Impl) bf.getBean("bean2");
assertSame(bean2, bean1.gi2);
}
@Test
public void testGenericsBasedInjectionIntoUnresolvedTypeVariable() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
RootBeanDefinition bd = new RootBeanDefinition(GenericInterface1Impl.class);
bd.setFactoryMethodName("createPlain");
bf.registerBeanDefinition("bean1", bd);
bf.registerBeanDefinition("bean2", new RootBeanDefinition(GenericInterface2Impl.class));
GenericInterface1Impl bean1 = (GenericInterface1Impl) bf.getBean("bean1");
GenericInterface2Impl bean2 = (GenericInterface2Impl) bf.getBean("bean2");
assertSame(bean2, bean1.gi2);
}
@Test
public void testGenericsBasedInjectionIntoTypeVariableSelectingBestMatch() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
RootBeanDefinition bd = new RootBeanDefinition(GenericInterface1Impl.class);
bd.setFactoryMethodName("create");
bf.registerBeanDefinition("bean1", bd);
bf.registerBeanDefinition("bean2", new RootBeanDefinition(GenericInterface2Impl.class));
bf.registerBeanDefinition("bean2a", new RootBeanDefinition(ReallyGenericInterface2Impl.class));
bf.registerBeanDefinition("bean2b", new RootBeanDefinition(PlainGenericInterface2Impl.class));
GenericInterface1Impl bean1 = (GenericInterface1Impl) bf.getBean("bean1");
GenericInterface2Impl bean2 = (GenericInterface2Impl) bf.getBean("bean2");
assertSame(bean2, bean1.gi2);
}
public static class ResourceInjectionBean {
@ -2534,4 +2587,67 @@ public class AutowiredAnnotationBeanPostProcessorTests {
}
}
public interface GenericInterface1<T> {
public String doSomethingGeneric(T o);
}
public static class GenericInterface1Impl<T> implements GenericInterface1<T>{
@Autowired
private GenericInterface2<T> gi2;
@Override
public String doSomethingGeneric(T o) {
return gi2.doSomethingMoreGeneric(o) + "_somethingGeneric_" + o;
}
public static GenericInterface1<String> create(){
return new StringGenericInterface1Impl();
}
public static GenericInterface1 createPlain(){
return new GenericInterface1Impl();
}
}
public static class StringGenericInterface1Impl extends GenericInterface1Impl<String> {
}
public interface GenericInterface2<K> {
public String doSomethingMoreGeneric(K o);
}
public static class GenericInterface2Impl implements GenericInterface2<String>{
@Override
public String doSomethingMoreGeneric(String o) {
return "somethingMoreGeneric_" + o;
}
}
public static class ReallyGenericInterface2Impl implements GenericInterface2<Object> {
@Override
public String doSomethingMoreGeneric(Object o) {
return "somethingMoreGeneric_" + o;
}
}
public static class PlainGenericInterface2Impl implements GenericInterface2{
@Override
public String doSomethingMoreGeneric(Object o) {
return "somethingMoreGeneric_" + o;
}
}
}

@ -220,6 +220,7 @@ public final class ResolvableType implements Serializable {
}
// Main assignability check about to follow
boolean exactMatch = checkingGeneric;
boolean checkGenerics = true;
Class<?> ourResolved = null;
if (this.type instanceof TypeVariable) {
@ -241,6 +242,10 @@ public final class ResolvableType implements Serializable {
}
}
}
if (ourResolved == null) {
// Unresolved type variable, potentially nested -> never insist on exact match
exactMatch = false;
}
}
if (ourResolved == null) {
ourResolved = resolve(Object.class);
@ -249,7 +254,7 @@ public final class ResolvableType implements Serializable {
// We need an exact type match for generics
// List<CharSequence> is not assignable from List<String>
if (checkingGeneric ? !ourResolved.equals(typeResolved) : !ClassUtils.isAssignable(ourResolved, typeResolved)) {
if (exactMatch ? !ourResolved.equals(typeResolved) : !ClassUtils.isAssignable(ourResolved, typeResolved)) {
return false;
}

Loading…
Cancel
Save