36 votes

Spring IoC et le type d'interface générique

J'essaie d'utiliser Spring IoC avec une interface comme celle-ci :

public interface ISimpleService<T> {
    void someOp(T t);
    T otherOp();
}

Spring peut-il fournir un IoC basé sur l'argument de type générique T ? Je veux dire, quelque chose comme ça :

public class SpringIocTest {
    @Autowired
    ISimpleService<Long> longSvc;

    @Autowired
    ISimpleService<String> strSvc;
    //...
}

Bien sûr, mon exemple ci-dessus ne fonctionne pas :

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)

Ma question : est-il possible de fournir une fonctionnalité similaire en modifiant le moins possible l'interface ou les classes d'implémentation ? Je sais par exemple que je peux utiliser @Qualifiers, mais je veux garder les choses aussi simples que possible.

24voto

krosenvold Points 35979

Je ne pense pas que cela soit possible en raison de l'effacement. Nous sommes généralement passés à des sous-interfaces fortement typées lorsque nous avons opté pour un câblage automatique complet :

public interface LongService extends ISimpleService<Long> {}
public interface StringService extends ISimpleService<String> {}

En effectuant ce changement, nous avons constaté que nous aimions bien cette méthode, car elle nous permet de faire un meilleur suivi de l'utilisation, ce que l'on perd avec les interfaces génériques.

14voto

Michael Pralow Points 3315

Je ne pense pas que ce soit possible sans Qualifier.

Je vais essayer de montrer mes solutions avec un DAO générique, désolé si c'est un peu détaillé.

la définition des classes d'interface et d'implémentation

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 définition du spring bean, remarquez l'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>

Utilisation de ce genericDAO sans implémentation spéciale Classe

 <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>

remarquer l'argument constructeur avec une classe concrète, si vous travaillez avec Spring Annotation vous devez le faire :

@Autowired
@Qualifier(value = "testHibernateChildDao")
private GenericDAO<TOChild, Integer> ToChildDAO;

pour distinguer les différentes versions des Beans genericDao (remarquez le qualificatif avec référence directe au Beanname)

Utilisation de ce genericDAO avec une implémentation spéciale Class

l'interface et la classe

public interface TestHibernateParentDAO extends GenericDAO<TOParent, Integer>{
  void foo();
}
public class TestHibernateParentDAOImpl extends GenericDAOImpl<TOParent, Integer>
                              implements TestHibernateParentDAO {
  @Override
  public void foo() {
      //* no-op */
  }
}

Dans la définition du bean, remarquez la référence "parent" au genericDAO abstrait ci-dessus.

<bean id="testHibernateParentDao" class="de.optimum24.av.pers.test.hibernate.dao.TestHibernateParentDAOImpl"
      parent="genericHibernateDAO" />

et utilisation avec Spring Annotation

@Autowired
private TestHibernateParentDAO ToParentDAO;

4voto

JasonQR Points 41

Il est possible de le faire avec l'effacement, si le type générique est entièrement réifié au moment de la compilation. Dans ce cas, l'information sur le type est disponible via l'un ou l'autre :

Class#getGenericInterfaces()
Class#getGenericSuperclass()

C'est la principale caractéristique de Guice qui manque à Spring.

3voto

Razvan Points 41

Ne rendez pas votre interface générique. Faites plutôt vos méthodes :

public interface ISimpleService {
    public <T> T doSomething(T param);
}

J'espère que cela vous aidera.

0voto

CorayThan Points 2190

Lorsque vous faites cela avec certaines couches de persistance, Données de printemps fait cela pour vous. Spring Data est un excellent outil de simplification et de gain de temps si vous utilisez JPA, Neo4j, MongoDB ou tout autre outil qu'il prend en charge.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X