Fundo:
Eu tenho um aplicativo Spring 2.5 / Java / Tomcat. Existe o seguinte bean, que é usado em todo o aplicativo em muitos lugares
public class HibernateDeviceDao implements DeviceDao
e o seguinte bean que é novo:
public class JdbcDeviceDao implements DeviceDao
O primeiro bean está configurado para isso (todos os beans do pacote estão incluídos)
<context:component-scan base-package="com.initech.service.dao.hibernate" />
O segundo (novo) bean é configurado separadamente
<bean id="jdbcDeviceDao" class="com.initech.service.dao.jdbc.JdbcDeviceDao">
<property name="dataSource" ref="jdbcDataSource">
</bean>
Isso resulta (é claro) em uma exceção ao iniciar o servidor:
a exceção aninhada é org.springframework.beans.factory.NoSuchBeanDefinitionException: nenhum bean exclusivo do tipo [com.sevenp.mobile.samplemgmt.service.dao.DeviceDao] é definido: bean correspondente único esperado, mas localizado 2: [deviceDao, jdbcDeviceDao]
de uma classe tentando autowire o bean como este
@Autowired
private DeviceDao hibernateDevicDao;
porque existem dois beans implementando a mesma interface.
A questão:
É possível configurar os beans para que
1. Não preciso fazer alterações nas classes existentes, que já possuem o HibernateDeviceDao
cabeamento automático
2. ainda sendo capaz de usar o segundo (novo) bean como este:
@Autowired
@Qualifier("jdbcDeviceDao")
Ou seja, eu precisaria de uma maneira de configurar o HibernateDeviceDao
bean como o padrão a ser conectado automaticamente, permitindo simultaneamente o uso de um JdbcDeviceDao
quando especificando explicitamente isso com a @Qualifier
anotação.
O que eu já tentei:
Eu tentei definir a propriedade
autowire-candidate="false"
na configuração do bean para JdbcDeviceDao:
<bean id="jdbcDeviceDao" class="com.initech.service.dao.jdbc.JdbcDeviceDao" autowire-candidate="false">
<property name="dataSource" ref="jdbcDataSource"/>
</bean>
porque a documentação do Spring diz que
Indica se esse bean deve ou não ser considerado ao procurar candidatos correspondentes para satisfazer os requisitos de conexão automática de outro bean. Observe que isso não afeta as referências explícitas por nome, que serão resolvidas mesmo que o bean especificado não esteja marcado como candidato a fio automático. *
o que eu interpretei como significando que eu ainda podia fazer a ligação automática JdbcDeviceDao
usando a @Qualifier
anotação e ter o HibernateDeviceDao
bean como padrão. Aparentemente, minha interpretação não estava correta, pois isso resulta na seguinte mensagem de erro ao iniciar o servidor:
Dependência não satisfeita do tipo [classe com.sevenp.mobile.samplemgmt.service.dao.jdbc.JdbcDeviceDao]: esperado pelo menos 1 bean correspondente
vindo da classe em que tentei fazer a fiação automática do bean com um qualificador:
@Autowired
@Qualifier("jdbcDeviceDao")
Solução:
A sugestão de skaffman de tentar a anotação @Resource funcionou. Portanto, a configuração tem candidato a fio automático definido como false para jdbcDeviceDao e, ao usar o jdbcDeviceDao, refiro-me a ela usando a anotação @Resource (em vez de @Qualifier):
@Resource(name = "jdbcDeviceDao")
private JdbcDeviceListItemDao jdbcDeviceDao;
fonte
Respostas:
Eu sugiro marcação da classe Hibernate DAO com
@Primary
, ou seja, (supondo que você usou@Repository
emHibernateDeviceDao
):Dessa forma, ele será selecionado como o candidato a arame automático padrão, sem necessidade
autowire-candidate
do outro bean.Além disso, em vez de usar
@Autowired @Qualifier
, acho mais elegante usar@Resource
para escolher feijões específicos, ou seja,fonte
@Resource
anotação, como também sugeri.@Resource
e@Qualifier
, além do fato de que o primeiro é relativamente mais novo que o último?Que tal
@Primary
?Ou se você deseja que sua versão do Jdbc seja usada por padrão:
@Primary
também é excelente para testes de integração quando você pode substituir facilmente o bean de produção pela versão stubbed anotando-o.fonte
primary=""
atributo estava disponível anteriormente. Apenas declareHibernateDeviceDao
em XML e exclua-o da varredura de componente / anotação.Para a Primavera 2.5, não há
@Primary
. A única maneira é usar@Qualifier
.fonte
fonte
O motivo pelo qual @Resource (name = "{nome da sua classe filha}") funciona, mas @Autowired às vezes não funciona, é devido à diferença de sua sequência de correspondência
Sequência de correspondência de @Autowire
Type, Qualifier, Name
Sequência correspondente de @Resource
Name, Type, Qualifier
A explicação mais detalhada pode ser encontrada aqui:
Injetar e Recurso e anotações com conexão automática
Nesse caso, uma classe filha diferente herdada da classe ou interface pai confunde @Autowire, porque são do mesmo tipo; Como o @Resource usa Name como primeira prioridade correspondente, ele funciona.
fonte