This question already has an answer here:
-
How to Autowire Bean of generic type <T> in Spring?
5 answers
I'm trying to use Spring IoC with an interface like this:
public interface ISimpleService<T> {
void someOp(T t);
T otherOp();
}
Can Spring provide IoC based on the generic type argument T? I mean, something like this:
public class SpringIocTest {
@Autowired
ISimpleService<Long> longSvc;
@Autowired
ISimpleService<String> strSvc;
//...
}
Of course, my example above does not work:
expected single matching bean but found 2: [serviceLong, serviceString]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessAfterInstantiation(AutowiredAnnotationBeanPostProcessor.java:243)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:957)
My Question: is it possible to provide a similar functionality with minimum modifications to either the Interface or the implementing classes? I know for instance I can use @Qualifiers, but I want to keep things as simple as possible.
I do not believe this is possible due to erasure. We generally switched to strongly typed sub-interfaces when going for full-autowiring:
public interface LongService extends ISimpleService<Long> {}
public interface StringService extends ISimpleService<String> {}
Upon doing this switch we found we actually liked this pretty well, because it allows us to do "find usage" tracking much better, something you loose with the generics interfaces.
i don't think thats possible without Qualifier
ill try to show my Solutions with a genericDAO, sorry if it's a bit detailed
the Interface and Implementation Class Definition
public interface GenericDAO<T, ID extends Serializable> (...)
public class GenericDAOImpl<T, ID extends Serializable>
implements GenericDAO<T, ID>
(...) important is this constructor
public GenericDAOImpl(Class<T> persistentClass) {
this.persistentClass = persistentClass;
}
the spring bean definition, notice the abstract="true"
<bean id="genericHibernateDAO" class="de.optimum24.av.pers.ext.hibernate.dao.GenericDAOImpl"
abstract="true">
<description>
<![CDATA[
Definition des GenericDAO.
]]>
</description>
<property name="sessionFactory" ref="sessionFactory" />
</bean>
Using this genericDAO without special implementation Class
<bean id="testHibernateChildDao" class="de.optimum24.av.pers.ext.hibernate.dao.GenericDAOImpl">
<property name="sessionFactory" ref="sessionFactory" />
<constructor-arg>
<value>de.optimum24.av.pers.test.hibernate.domain.TOChild</value>
</constructor-arg>
</bean>
notice the constructor-arg with an concrete Class, if you work with Spring Annotation you need to do:
@Autowired
@Qualifier(value = "testHibernateChildDao")
private GenericDAO<TOChild, Integer> ToChildDAO;
to distinguish the various versions of genericDao Beans (notice the Qualifier with direct Reference to the Beanname)
Using this genericDAO with special implementation Class
the Interface and Class
public interface TestHibernateParentDAO extends GenericDAO<TOParent, Integer>{
void foo();
}
public class TestHibernateParentDAOImpl extends GenericDAOImpl<TOParent, Integer>
implements TestHibernateParentDAO {
@Override
public void foo() {
//* no-op */
}
}
the Bean Definition, notice the "parent" Reference to the abstract genericDAO above
<bean id="testHibernateParentDao" class="de.optimum24.av.pers.test.hibernate.dao.TestHibernateParentDAOImpl"
parent="genericHibernateDAO" />
and usage with Spring Annotation
@Autowired
private TestHibernateParentDAO ToParentDAO;
It is possible to do this with erasure, if the generic type is fully reified at compile-time. In this case the type information is available via either of:
Class#getGenericInterfaces()
Class#getGenericSuperclass()
This is the major feature of Guice which is missing from Spring.
Don't make your interface generic. Make your methods, instead:
public interface ISimpleService {
public <T> T doSomething(T param);
}
Hope it helps.
When doing this with certain persistence layers, Spring Data does this for you. Spring Data is a really great time-saving and simplification tool if you are using JPA, or Neo4j, or MongoDB, or something else that it supports.
Another option is to annotate the interface implementing bean with name on one side and to annotate with qualifier pointing to created name on the other side :) Here is a quick example I am using in my project :
public interface IDAO<T> {
public void insert(T model);
public void update(T model);
public void delete(T model);
}
Abstract class as predecessor :
public abstract class AbstractHibernateDAO {
protected SessionFactory sessionFactory;
protected Session currentSession() {
return sessionFactory.getCurrentSession();
}
}
Implementation of abstract class for entity user:
@Repository(value = "userRepository")
public class UserDAO extends AbstractHibernateDAO implements IDAO<User> {
@Autowired
public UserDAO(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
public void insert(User user) {
currentSession().save(user);
}
@Override
public void update(User user) {
currentSession().update(user);
}
@Override
public void delete(User user) {
currentSession().delete(user);
}
}
And finally injecting right implementation:
@Resource
@Qualifier(value = "userRepository")
IDAO<User> userPersistence;