Reputation: 852
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?
Upvotes: 3
Views: 1686
Reputation: 298908
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" :-)
Upvotes: 7
Reputation: 11802
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.
Upvotes: -1