java - ioc - inversion of control vs dependency injection
Spring IoC y tipo de interfaz genérica (6)
Esta pregunta ya tiene una respuesta aquí:
Intento utilizar Spring IoC con una interfaz como esta:
public interface ISimpleService<T> {
void someOp(T t);
T otherOp();
}
¿Puede Spring proporcionar IoC basado en el argumento de tipo genérico T? Quiero decir, algo como esto:
public class SpringIocTest {
@Autowired
ISimpleService<Long> longSvc;
@Autowired
ISimpleService<String> strSvc;
//...
}
Por supuesto, mi ejemplo anterior no funciona:
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)
Mi pregunta: ¿es posible proporcionar una funcionalidad similar con modificaciones mínimas a la interfaz o las clases de implementación? Sé, por ejemplo, que puedo usar @Qualifiers, pero quiero que todo sea lo más simple posible.
Al hacer esto con ciertas capas de persistencia, Spring Data hace esto por usted. Spring Data es una gran herramienta de ahorro de tiempo y simplificación si está utilizando JPA, o Neo4j, o MongoDB, o cualquier otra cosa que admita.
Es posible hacer esto con borrado, si el tipo genérico está completamente reificado en tiempo de compilación. En este caso, la información de tipo está disponible a través de cualquiera de:
Class#getGenericInterfaces()
Class#getGenericSuperclass()
Esta es la característica principal de Guice que falta en Spring.
No creo que esto sea posible debido al borrado. Por lo general, cambiamos a subinterfaces fuertemente tipados cuando íbamos a realizar un autoenvío completo:
public interface LongService extends ISimpleService<Long> {}
public interface StringService extends ISimpleService<String> {}
Al hacer este cambio descubrimos que realmente nos gustaba bastante, porque nos permite hacer un seguimiento de "encontrar uso" mucho mejor, algo que se pierde con las interfaces genéricas.
No hagas que tu interfaz sea genérica. Haga sus métodos, en cambio:
public interface ISimpleService {
public <T> T doSomething(T param);
}
Espero eso ayude.
Otra opción es anotar la interfaz implementando bean con nombre en un lado y anotar con calificador apuntando al nombre creado en el otro lado :) Aquí hay un ejemplo rápido que estoy usando en mi proyecto:
public interface IDAO<T> {
public void insert(T model);
public void update(T model);
public void delete(T model);
}
Clase abstracta como predecesor:
public abstract class AbstractHibernateDAO {
protected SessionFactory sessionFactory;
protected Session currentSession() {
return sessionFactory.getCurrentSession();
}
}
Implementación de la clase abstracta para el usuario de la entidad:
@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);
}
}
Y finalmente inyectando la implementación correcta:
@Resource
@Qualifier(value = "userRepository")
IDAO<User> userPersistence;
no creo que eso sea posible sin Calificador
Trataré de mostrar mis Soluciones con unDADA genérico, lo siento si es un poco detallado
la interfaz y la definición de clase de implementación
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;
}
la definición de la primavera, note el resumen = "verdadero"
<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>
Usando esteDAO genérico sin implementación especial Clase
<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>
fíjate en el constructor-arg con una Clase concreta, si trabajas con Spring Annotation necesitas hacer:
@Autowired
@Qualifier(value = "testHibernateChildDao")
private GenericDAO<TOChild, Integer> ToChildDAO;
para distinguir las diferentes versiones de generic Beans de golfo (observe el calificador con referencia directa al nombre de Bean)
Usando esteDAO genérico con implementación especial Clase
la interfaz y clase
public interface TestHibernateParentDAO extends GenericDAO<TOParent, Integer>{
void foo();
}
public class TestHibernateParentDAOImpl extends GenericDAOImpl<TOParent, Integer>
implements TestHibernateParentDAO {
@Override
public void foo() {
//* no-op */
}
}
la definición de Bean, observe la referencia "principal" al abstract genericDAO arriba
<bean id="testHibernateParentDao" class="de.optimum24.av.pers.test.hibernate.dao.TestHibernateParentDAOImpl"
parent="genericHibernateDAO" />
y uso con Spring Annotation
@Autowired
private TestHibernateParentDAO ToParentDAO;