Autowire в зависимости от подкласса

У меня есть абстрактный класс AbstractService, который имеет ссылку на AbstractDAO

class AbstractService{  
   protected AbstractDAO abstractDAO;  
}

AbstractService будет расширен за счет фактических классов обслуживания, таких как ServiceClassA, ServiceClassB и т. д., а AbstractDAO будет расширен за счет DaoClassA, DaoClassB и т. д.

В зависимости от того, какой класс расширяет AbstractService, abstractDAO должен быть экземпляром DaoClassA, DaoClassB и т. д.

Я могу добиться этого, имея установщик abstractDAO в расширяющем классе, например

class ServiceClassA{    
    @Autowired  
    @Qualifier("daoClassA")  
    public void setAbstractDAO(AbstractDAO abstractDAO) {  
        super.abstractDAO = abstractDAO;  
    }   
}  

Есть ли способ, чтобы сеттер setAbstractDAO был в самом классе AbstractService, а abstractDAO получал Autowired в зависимости от подкласса, возможно, с помощью SPEL + Qualifier и т. д.

Мы не хотим использовать для этого какую-либо конфигурацию XML.


person Jeenson Ephraim    schedule 22.02.2012    source источник
comment
Есть ли причина, по которой вы не можете указать, например. @Autowired DaoClassA dao в ServiceClassA? Почему поле нужно объявлять в AbstractService?   -  person skaffman    schedule 22.02.2012
comment
Отличный вопрос. Я всегда делал что-то похожее на описанный вами подход (немного другой, но та же основная идея), и мне всегда хотелось чего-то более автоматического. Стремясь увидеть, есть ли у кого-нибудь хороший подход.   -  person    schedule 22.02.2012
comment
@skaffman В случаях, которые у меня были, я хотел, чтобы AbstractService имел доступ к AbstractDao, чтобы я мог, среди прочего, писать общие версии операций CRUD.   -  person    schedule 22.02.2012
comment
Будет несколько реализаций AbstractService, и мы не хотим, чтобы это поле было объявлено во всех классах, расширяющих AbstractService.   -  person Jeenson Ephraim    schedule 22.02.2012


Ответы (3)


Я бы не стал так делать. Действительно, есть большая вероятность, что ServiceClassA зависит от какого-то конкретного метода DaoClassA. В этом случае вам придется приводить защищенный AbstractDAO к DaoClassA каждый раз, когда вы хотите вызвать такой конкретный метод.

Я бы сделал его универсальным и изменил способ внедрения зависимостей:

public class AbstractService<T extends AbstractDAO> {  
    protected T dao;

    protected AbstractService(T dao) {
        this.dao = dao;
    }

    // methods common to all the services
}

public class ServiceClassA extends AbstractService<DaoClassA> {
    @Autowired
    public ServiceClassA(DaoClassA dao) {
        super(dao);
    }

    // methods specific to ServiceClassA
}
person JB Nizet    schedule 22.02.2012
comment
Это больше похоже на подход, который я использовал в прошлом, и он удобен по причине, которую вы заметили, — безопасности типов в подклассе. Но у него все еще есть недостаток, заключающийся в том, что для него требуются точки впрыска для конкретного типа. Есть ли хороший способ избежать этого? (Я не думаю, что есть, но если у кого-то есть, я хотел бы знать.) - person ; 22.02.2012
comment
Но все же это похоже на исходный подход, и все подклассы все равно должны будут выполнять работу. - person Jeenson Ephraim; 22.02.2012
comment
Вы можете поместить ссылку DaoClassA в ServiceClassA, и теперь она будет специфичной для типа (без приведения). - person ; 22.02.2012
comment
Это было бы похоже на жесткое кодирование ссылки, аннотации также похожи на жесткое кодирование, но они были бы более чистыми. - person Jeenson Ephraim; 22.02.2012

Я решал аналогичную проблему, как и вы. Я нашел другой способ, вам не нужно создавать методы установки. Вместо этого используйте обычные конструкторы, но используйте автопроводку Spring. Вот полный код:

Классы обслуживания:

public abstract class AbstractService {

    protected final AbstractDAO dao;

    // Constructor forces you to inject dao in subclass
    public AbstractService(final AbstractDAO dao) {
        this.dao = dao;
    }

    public final void service() {
        // you can do something generic here with 'dao'
        // no matter which subclass is injected
        this.dao.doSomething();
    }
}

@Component
public class ServiceClassA extends AbstractService {

    @Autowired
    public ServiceClassA(@Qualifier("daoClassA") final AbstractDAO dao) {
        super(dao);
    }

}

@Component
public class ServiceClassB extends AbstractService {

    @Autowired
    public ServiceClassB(@Qualifier("daoClassB") final AbstractDAO dao) {
        super(dao);
    }

}

Примечание @Qualifier("daoClassA") в конструкторах подклассов

Полевые классы:

public interface AbstractDAO {    
    public void doSomething();
}

@Component
public class DaoClassA implements AbstractDAO {

    @Override
    public void doSomething() {
        System.out.println("I am DaoClassA");
    }    
}

@Component
public class DaoClassB implements AbstractDAO {

    @Override
    public void doSomething() {
        System.out.println("I am DaoClassB");
    }    
}

И, наконец, теперь вы можете вызвать свою общую службу с конкретным классом службы и конкретным классом DAO: (конечно, вы можете их где-нибудь автоматически связать)

((AbstractService) context.getBean("serviceClassA")).service();
((AbstractService) context.getBean("serviceClassB")).service();

будет печатать:

I am DaoClassA
I am DaoClassB
person Gondy    schedule 23.01.2015

Нет, нет. Вы не можете получить доступ к имени класса или компонента, который в настоящее время заполняет AutowiredAnnotationBeanPostProcessor из SPEL.

Вы можете переопределить AbstractBeanFactory.evaluateBeanDefinitionString и добавить beanDefinition как переменную в BeanExpressionContext . Тогда вы можете получить Дао из Служения. используя SPEL в аннотации @Value.

person Jose Luis Martin    schedule 23.02.2012