Подтвердить что ты не робот

Spring 3.2 Общие типы Autowire

Итак, у меня есть ряд дженериков в Spring 3.2, и в идеале моя архитектура будет выглядеть примерно так.

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

К сожалению, при использовании нескольких реализаций генериков автоувеличивание вызывает ошибку в отношении множественных сопоставлений bean. Я предполагаю, что это связано с тем, что @Autowired обрабатывает до стирания типа. Каждое решение, которое я нашел или придумал, выглядит уродливо для меня или просто необъяснимо отказывается работать. Каков наилучший способ решения этой проблемы?

4b9b3361

Ответ 1

Как добавить конструктор в GenericService и переместить автоустановку в расширяющийся класс, например.

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

Update:

Начиная с Spring 4.0 RC1, можно autwire на основе родового типа, что означает, что вы можете написать общий сервис, например

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

    @Autowired
    private T_DAO tDao;
}

и создайте несколько разных Spring beans, например:

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

Ответ 2

Вы можете удалить аннотацию @autowire и выполнить отложенное "autowire" с помощью @PostConstruct и ServiceLocatorFactoryBean.
Ваш GenericService будет похож на этот

    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 делает магию для вас.
Чтобы использовать его, вам необходимо добавить интерфейс, аналогичный приведенному ниже:

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

Вам нужно добавить следующий фрагмент к вашему applicationContext.xml

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

Это хороший трюк, и это спасет вас от маленьких шаблонов.
B.T.W Я не вижу этот шаблонный код как большую проблему, и проект, над которым я работаю, использует подход matsev.

Ответ 3

Вот самое близкое решение. Специализированные DAO аннотируются на бизнес-уровне. Как и в вопросе OP, наилучшие усилия будут иметь аннотированный DAO в основном шаблоне EntityDAO. Тип стирания, по-видимому, не позволяет передавать информацию специализированного типа на фабрики spring [в результате сообщается о сопоставлении beans со всеми специализированными DAO]

Шаблон DAO общего объекта

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

Шаблон бизнес-уровня общего типа

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

    //Rest of code.
}

Пример специализированной организации DAO

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

Пример специализированного бизнес-класса

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

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

    //Rest of code
}

Ответ 4

Почему вам нужен общий сервис? Классы обслуживания предназначены для определенных единиц работы с участием множества объектов. Вы можете просто ввести репозиторий прямо в контроллер.

Вот пример общего репозитория с аргументом конструктора, вы также можете сделать каждый метод Generic и не иметь аргумента конструктора. Но для каждого вызова метода для класса требуется параметр:

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

Пример определения bean для общего репозитория - у вас может быть несколько различных beans, используя различные аргументы conttructor.

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

Инъекция Depdncy bean с использованием аннотации ресурса

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

И это позволяет Domainreposiroty быть подклассом для определенных сущностей/методов, которые автоматически выполняют autoliving:

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

Ответ 5

Для этого вопроса нужно понять, что такое autowire. В общих чертах мы можем сказать, что с помощью autwire мы создаем экземпляр объекта / bean во время развертывания веб-приложения. Итак, теперь вы переходите к вопросу, если вы объявляете автоувеличивание в нескольких местах с тем же именем. Затем появляется эта ошибка. Autowiring может выполняться несколькими способами, поэтому, если вы используете несколько методов автоувеличивания, тогда также можно получить эту ошибку.

Ответ 6

Вы должны использовать autowiring в классах, которые расширяют эти дженерики

Ответ 7

Полное общее решение с помощью Spring 4:


Класс домена

@Component
class Foo{
}

@Component
class Bar{
}

Уровень 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
}

Уровень обслуживания

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
}