Allow generics-based injection in case of nested unresolved type variable
Issue: SPR-11471
This commit is contained in:
parent
49d7bda722
commit
793cab4f80
|
|
@ -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…
Reference in New Issue