web-dev-qa-db-fra.com

Spring IoC et 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 une IoC basée 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 avec un minimum de modifications de l’interface ou des classes d’implémentation? Je sais par exemple que je peux utiliser @Qualifiers, mais je veux garder les choses aussi simples que possible.

35
Miguel Ping

Je ne crois pas que cela soit possible en raison de l'effacement. Nous optons généralement pour des sous-interfaces fortement typées lorsque nous optons pour le câblage automatique:

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

En effectuant ce changement, nous avons constaté que nous l'aimions plutôt bien, car cela nous permet de mieux "suivre l'utilisation", ce que vous perdez avec les interfaces génériques.

22
krosenvold

je ne pense pas que ce soit possible sans Qualifier

malade essayer de montrer mes solutions avec un genericDAO, désolé si c'est un peu détaillé

la définition d'interface et de classe 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 de haricot de printemps, notez le résumé = "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>

remarquez le constructeur-arg avec une classe concrète. Si vous travaillez avec Spring Annotation, vous devez effectuer les tâches suivantes:

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

distinguer les différentes versions de genericDao Beans (notez le qualificatif avec référence directe au nom du bean)

Utilisation de ce genericDAO avec une classe d'implémentation spéciale

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 */
  }
}

la définition de haricot, notez la référence "parent" à l'abrégé genericDAO 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;
14
Michael Pralow

Il est possible de le faire avec effacement si le type générique est complètement réifié au moment de la compilation. Dans ce cas, les informations de type sont disponibles via:

Class#getGenericInterfaces()
Class#getGenericSuperclass()

C'est la principale caractéristique de Guice qui manque au printemps.

4
JasonQR

Ne faites pas votre interface générique. Faites vos méthodes, à la place:

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

J'espère que ça aide.

3
Razvan

Lorsque vous faites cela avec certaines couches de persistance, Spring Data le fait pour vous. Spring Data est un très bon outil de gain de temps et de simplification si vous utilisez JPA, Neo4j ou MongoDB, ou autre chose qu’il prend en charge.

0
CorayThan

Une autre option consiste à annoter l'interface implémentant le bean avec name d'un côté et à annoter avec un qualificatif pointant sur le nom créé de l'autre côté :) Voici un exemple rapide que j'utilise dans mon projet:

 public interface IDAO<T> {

         public void insert(T model);
         public void update(T model);
         public void delete(T model);
  }

Classe abstraite en tant que prédécesseur:

public abstract class AbstractHibernateDAO {

         protected SessionFactory sessionFactory;

         protected Session currentSession() {
             return sessionFactory.getCurrentSession();
         }
    }

Implémentation d'une classe abstraite pour un utilisateur d'entité:

@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);
    } 

}

Et enfin, en injectant la bonne mise en œuvre:

@Resource
@Qualifier(value = "userRepository")
IDAO<User> userPersistence;
0
s0vet