web-dev-qa-db-fra.com

Spring 3.2 types génériques Autowire

J'ai donc un certain nombre de génériques au printemps 3.2 et, idéalement, mon architecture devrait ressembler à ceci.

class GenericDao<T>{}

class GenericService<T, T_DAO extends GenericDao<T>>
{
    // FAILS
    @Autowired
    T_DAO;
}

@Component
class Foo{}

@Repository
class FooDao extends GenericDao<Foo>{}

@Service
FooService extends GenericService<Foo, FooDao>{}

Malheureusement, avec plusieurs implémentations des génériques, le câblage automatique génère une erreur concernant plusieurs définitions de beans correspondantes. Je suppose que c'est parce que @Autowired traite avant l'effacement du type. Chaque solution que j'ai trouvée ou que j'ai trouvée me semble laide ou refuse inexplicablement de travailler. Quel est le meilleur moyen de contourner ce problème?

27
Stephen Lujan

Pourquoi ne pas ajouter un constructeur à la GenericService et déplacer le câblage automatique vers la classe extensible, par ex.

class GenericService<T, T_DAO extends GenericDao<T>> {
    private final T_DAO tDao;

    GenericService(T_DAO tDao) {
        this.tDao = tDao;
    }
}

@Service
FooService extends GenericService<Foo, FooDao> {

    @Autowired
    FooService(FooDao fooDao) {
        super(fooDao);
    }
}

Mise à jour:

À partir de Spring 4.0 RC1 , il est possible d’autowire sur la base d’un type générique, ce qui signifie que vous pouvez écrivez un service générique comme

class GenericService<T, T_DAO extends GenericDao<T>> {

    @Autowired
    private T_DAO tDao;
}

et créer plusieurs haricots de printemps différents comme:

@Service
class FooService extends GenericService<Foo, FooDao> {
}
25
matsev

Vous pouvez supprimer l'annotation @autowire et effectuer un «autowire» différé à l'aide de @PostConstruct et ServiceLocatorFactoryBean.
Votre GenericService ressemblera à ceci

    public class GenericService<T, T_DAO extends GenericDao<T>>{

        @Autowired
        private DaoLocator daoLocatorFactoryBean;

        //No need to autowried, autowireDao() will do this for you 
        T_DAO dao;


        @SuppressWarnings("unchecked")
        @PostConstruct
        protected void autowireDao(){
        //Read the actual class at run time
        final Type type; 
        type = ((ParameterizedType) getClass().getGenericSuperclass())
                                              .getActualTypeArguments()[1]; 
        //figure out the class of the fully qualified class name
        //this way you can know the bean name to look for
        final String typeClass = type.toString();      
        String daoName = typeClass.substring(typeClass.lastIndexOf('.')+1
                                            ,typeClass.length());
        daoName = Character.toLowerCase(daoName.charAt(0)) + daoName.substring(1);
        this.dao = (T_DAO) daoLocatorFactoryBean.lookup(daoName);
       }

daoLocatorFactoryBean fait la magie pour vous.
Pour l'utiliser, vous devez ajouter une interface similaire à celle ci-dessous:

 public interface DaoLocator {
        public GenericDao<?> lookup(String serviceName);           
 }    

Vous devez ajouter l'extrait de code suivant à votre applicationContext.xml.

  <bean id="daoLocatorFactoryBean" 
      class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
      <property name="serviceLocatorInterface"
              value="org.haim.springframwork.stackoverflow.DaoLocator" />
    </bean>

C'est une astuce intéressante qui vous épargnera de petites classes standard.
B.T.W Je ne vois pas ce code passe-partout comme un gros problème et le projet pour lequel je travaille utilise la même approche.

4
Haim Raman

Pourquoi voulez-vous un service générique? Les classes de service sont destinées à des unités de travail spécifiques impliquant plusieurs entités. Vous pouvez simplement injecter un référentiel directement dans un contrôleur. 

Voici un exemple de référentiel générique avec un argument constructeur, vous pouvez également créer chaque méthode générique à la place et ne pas avoir d’argument constructeur. Mais chaque appel de méthode nécessiterait la classe en tant que paramètre:

public class DomainRepository<T> {

   @Resource(name = "sessionFactory")
   protected SessionFactory sessionFactory;

   public DomainRepository(Class genericType) {
        this.genericType = genericType;
   }

   @Transactional(readOnly = true)
   public T get(final long id) {
       return (T) sessionFactory.getCurrentSession().get(genericType, id);
   }

Exemple de définition de bean pour le référentiel générique - vous pouvez avoir plusieurs beans différents, en utilisant différents arguments de convertisseur.

<bean id="tagRepository" class="com.yourcompnay.data.DomainRepository">
        <constructor-arg value="com.yourcompnay.domain.Tag"/>
</bean>

Depdncy injection de haricot en utilisant une annotation de ressource

@Resource(name = "tagRepository")
private DomainRepository<Tag> tagRepository;

Et cela permet à Domainreposiroty d’être sous-classé pour des entités/méthodes spécifiques, ce qui permet un autowiring:

public class PersonRepository extends DomainRepository<Person> {
    public PersonRepository(){
        super(Person.class);
    }
    ...
3
NimChimpsky

Voici une solution la plus proche. Les DAO spécialisés sont annotés au niveau de la couche de gestion. Comme dans la question de OP, le mieux serait de faire figurer un DAO annoté dans le modèle générique EntityDAO lui-même. L'effacement des types ne semble pas permettre que les informations de types spécialisés soient transmises aux usines de printemps (ce qui entraîne la création de rapports sur les beans correspondants de toutes les DAO spécialisées).

Le modèle DAO d'entité générique

public class EntityDAO<T> 
{
    @Autowired
    SessionFactory factory;

    public Session getCurrentSession()
    {
        return factory.getCurrentSession();
    }

    public void create(T record)
    {
        getCurrentSession().save(record);
    }

    public void update(T record)
    {
        getCurrentSession().update(record);
    }

    public void delete(T record)
    {
        getCurrentSession().delete(record);
    }

    public void persist(T record)
    {
        getCurrentSession().saveOrUpdate(record);
    }

    public T get(Class<T> clazz, Integer id)
    {
        return (T) getCurrentSession().get(clazz, id);
    }
}

Le modèle de couche de gestion générique basé sur une entité

public abstract class EntityBusinessService<T>
implements Serializable
{
    public abstract EntityDAO<T> getDAO();

    //Rest of code.
}

Un exemple d'entité spécialisée DAO

@Transactional
@Repository
public class UserDAO
extends EntityDAO<User>
{
}

Un exemple de classe métier d'entité spécialisée

@Transactional
@Service
@Scope("prototype")
public class UserBusinessService
extends EntityBusinessService<User>
{
    @Autowired
    UserDAO dao;

    @Override
    public EntityDAO<User> getDAO() 
    {
        return dao;
    }

    //Rest of code
}
3
maggu

Pour cette question, il faut comprendre ce qu'est autowire. En termes courants, nous pouvons dire que via autowire, nous créons une instance d'objet/un haricot au moment du déploiement de l'application Web. Alors maintenant, allez à la question si vous déclarez l'auto-câblage dans plusieurs endroits avec le même nom. Alors cette erreur vient. Le câblage automatique peut être effectué de différentes manières. Par conséquent, si vous utilisez plusieurs types de technique de transfert automatique, vous risquez également d'obtenir cette erreur.

1
Abhishek

Vous devez utiliser le câblage automatique dans les classes qui développent ces génériques.

1
Moesio

Solution générique complète using Spring 4 :


Classe de domaine

@Component
class Foo{
}

@Component
class Bar{
}

Couche DAO

interface GenericDao<T>{
//list of methods
}

class GenericDaoImpl<T> implements GenericDao<T>{
 @Autowired
 SessionFactory factory;

 private Class<T> domainClass; // Get Class Type of <T>

 public Session getCurrentSession(){
    return factory.getCurrentSession();
 }

 public DaoImpl() {
    this.domainClass = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), DaoImpl.class);
 }
 //implementation of methods
}

interface FooDao extends GenericDao<Foo>{
//Define extra methods if required
}

interface BarDao extends GenericDao<Bar>{
//Define extra methods if required
}

@Repository
class FooDao extends GenericDaoImpl<Foo> implements FooDao{
 //implementation of extra methods
}

@Repository
class BarDao extends GenericDaoImpl<Bar> implements BarDao{
 //implementation of extra methods
}

Couche de service

interface GenericService<T>{
//List of methods
}

class GenericServiceImpl<T> implements GenericService<T>{
 @Autowire
 protected GenericDao<T> dao; //used to access DAO layer
}

class FooService extends GenericService<Foo>{
//Add extra methods of required
}

class BarService extends GenericService<Bar>{
//Add extra methods of required
}

@Service
class FooServiceImpl extends GenericServiceImpl<Foo> implements GenericService<Foo>{
//implementation of extra methods
}

@Service
class BarServiceImpl extends GenericServiceImpl<Bar> implements GenericService<Bar>{
//implementation of extra methods
}
1
Ayman Al-Absi