3

I've a Problem with Spring when using generics. The following code describes the problem pretty good:

public class TestInj<S> {
    S elem;
    public S getElem() {
        return elem;
    }
    public void setElem(S elem) {
        this.elem = elem;
    }
}

@Component
public class First extends TestInj<String> {
    public First() {
        setElem("abc");
    }
}

@Component
public class Second extends TestInj<Integer> {
    public Second() {
        setElem(2);
    }
}



public class BaseTest<T> {
    @Autowired
    protected TestInj<T> test;

}

@Named
public class Test extends BaseTest<String> {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("conf-spring.xml");
        context.refresh();
        Test test = (Test) context.getBean("test", Test.class);
        System.out.println(test.test.getElem());
    }
}

The Problem is, that in class BaseTest the class First should be injected, because it has the generic type String. But spring doesn't get that and tells me, that there are two possible candidates for autowiring. The reason for that is, that spring ignores the generics. Is there a solution for that or a workaround?

itsme
  • 852
  • 1
  • 10
  • 23

2 Answers2

7

Spring is pretty smart about generics, but you're asking too much. Spring analyzes the generics of the class that contains the property to be injected, which is BaseTest. But the type erasure of the dependency test is TestInj<Object>. Only the subclass Test provides more generic information which could be used to limit the injection candidates.

Unfortunately for you, it isn't, Spring just doesn't work that way. When analyzing bean classes, Spring never looks down the hierarchy from where it is. (I've banged my head against that in other cases where I wanted to put methods in abstract super classes and @Transactional annotations on the implementing subclasses.)

So if you need that functionality, you will have to write a replacement for one of Spring's core components (AutowiredAnnotationBeanPostProcessor or one of the helper classes it uses). Since Spring is designed in a modular way, you should be able to do it without breaking anything if you just provide a subclass. But if you don't absolutely need this functionality, the simple answer is: "It don't work that way" :-)

Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
-1

This is due to the type erasure. See:

http://download.oracle.com/javase/tutorial/java/generics/erasure.html

When a generic type is instantiated, the compiler translates those types by a technique called type erasure — a process where the compiler removes all information related to type parameters and type arguments within a class or method. Type erasure enables Java applications that use generics to maintain binary compatibility with Java libraries and applications that were created before generics.

As Spring DI container works in runtime, it can not distinguish those types.

Infeligo
  • 11,715
  • 8
  • 38
  • 50
  • as long as the root bean class is not generic, the container has full type info on all injection points. – irreputable Jul 28 '11 at 10:14
  • But with reflection it's possible to get the types of the generics. But Spring obviously doesn't uses that. So maybe there's a workaround for that? – itsme Jul 28 '11 at 10:26