Spring IoC и универсальный тип интерфейса

Я пытаюсь использовать Spring IoC с таким интерфейсом:

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

Может ли Spring предоставить IoC на основе аргумента универсального типа T? Я имею в виду что-то вроде этого:

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

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

Конечно, мой пример выше не работает:

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)

Мой вопрос: возможно ли предоставить аналогичную функциональность с минимальными изменениями либо в интерфейсе, либо в реализующих классах? Я знаю, например, что могу использовать @Qualifiers, но я хочу, чтобы все было как можно проще.


person Miguel Ping    schedule 02.02.2009    source источник
comment
Кажется, теперь это возможно с Spring 4.0. См. этот ответ SO и статью Spring под названием Spring Framework 4.0 и Java Generics.   -  person chrisjleu    schedule 10.11.2014


Ответы (6)


Я не верю, что это возможно из-за стирания. Обычно мы переключались на строго типизированные подинтерфейсы при переходе на полное автоматическое связывание:

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

Сделав этот переключатель, мы обнаружили, что нам это очень понравилось, потому что оно позволяет нам намного лучше отслеживать «найти использование», чего вы теряете с интерфейсами дженериков.

person krosenvold    schedule 02.02.2009
comment
А как вы справляетесь с дублированием кода? Наверное, есть средний класс, который тоже общий, нет? спасибо - person Miguel Ping; 02.02.2009
comment
Единственное необходимое дублирование — это интерфейс маркера. Но вы не показываете свои классы реализации, поэтому я не могу сказать, делаете ли вы что-то, чего я не вижу! - person krosenvold; 02.02.2009
comment
Реализация подойдет для другого вопроса SO :) - person Miguel Ping; 02.02.2009

я не думаю, что это возможно без Qualifier

я попытаюсь показать свои решения с помощью genericDAO, извините, если это немного подробно

Определение класса интерфейса и реализации

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

определение Spring bean, обратите внимание на 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>

Использование этого универсального DAO без специального класса реализации

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

обратите внимание на аргумент конструктора с конкретным классом, если вы работаете с аннотацией Spring, вам нужно сделать:

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

различать различные версии genericDao Beans (обратите внимание на квалификатор с прямой ссылкой на имя Beanname)

Использование этого genericDAO со специальным классом реализации

Интерфейс и класс

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

Bean Definition, обратите внимание на "родительскую" ссылку на абстрактный genericDAO выше.

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

и использование с Spring Annotation

@Autowired
private TestHibernateParentDAO ToParentDAO;
person Michael Pralow    schedule 04.02.2009
comment
Отличный ответ, касающийся использования как аннотаций, так и XML. Сработало ли это решение для вас в долгосрочной перспективе? Есть ли какие-либо недостатки в использовании этой методологии, такие как возможность более легкого отслеживания кода (упомянутого Кросенволдом в 1-м посте)? - person HipsterZipster; 15.03.2012
comment
это скорее организационная проблема/проблема знаний, если разработчики знают, как работает этот процесс, с этим общим методом, безусловно, сложнее справиться, чем с использованием (маркерных) интерфейсов. - person Michael Pralow; 15.03.2012

Это можно сделать с помощью стирания, если общий тип полностью материализован во время компиляции. В этом случае информация о типе доступна через один из следующих способов:

Class#getGenericInterfaces()
Class#getGenericSuperclass()

Это главная особенность Guice, которой нет в Spring.

person JasonQR    schedule 16.02.2011

Не делайте свой интерфейс универсальным. Вместо этого сделайте свои методы:

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

Надеюсь, поможет.

person Razvan    schedule 05.11.2010

Когда вы делаете это с определенными слоями сохраняемости, Spring Data сделает это за вас. Spring Data — действительно отличный инструмент для экономии времени и упрощения, если вы используете JPA, Neo4j, MongoDB или что-то еще, что он поддерживает.

person CorayThan    schedule 16.05.2013

Другой вариант - аннотировать интерфейс, реализующий bean-компонент, с именем с одной стороны и аннотировать квалификатором, указывающим на созданное имя, с другой стороны:) Вот краткий пример, который я использую в своем проекте:

 public interface IDAO<T> {

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

Абстрактный класс как предшественник:

public abstract class AbstractHibernateDAO {

         protected SessionFactory sessionFactory;

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

Реализация абстрактного класса для пользователя сущности:

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

}

И, наконец, правильная реализация:

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