Override @Autowired property annotation with XML configuration

1.6k views Asked by At

I've a class EntityLoader that's used to fetch some data from a MySQL database using Hibernate. But now the need is to fetch data from two different databases (MySQL and Oracle in this case). So I want to have two beans of EntityLoader but injecting a different SessionFactory in each one.

EntityLoader is defined as follows:

package com.demo

@Component
public class EntityLoader {

    @Autowired
    private SessionFactory sessionFactory;

    /* Code ... */

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }   
}

And the context configuration is:

<context:component-scan base-package="com.demo" />
<bean id="mysqlSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">

So far it works fine. Over this I have done the following changes:

  • Exclude EntityLoader from component-scan in order to avoid the auto creation of a EntityLoader
  • Add mysqlSessionFactory and oracleSessionFactory bean definitions
  • Add mysqlEntityRepoLoader and oracleEntityRepoLoader bean definitions

Note that in mysqlEntityRepoLoader and oracleEntityRepoLoader I've added the attribute autowired="no" hoping that this would tell Spring to not autowire the SessionFactory and use the defined ref instead.

The resulting configuration is:

<context:component-scan base-package="com.demo">
    <context:exclude-filter type="regex" expression="com.demo.EntityLoader"/>
</context:component-scan>

<bean id="mysqlSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <!-- ... config ... -->
</bean>
<bean id="oracleSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <!-- ... config ... -->
</bean>

<bean id="mysqlEntityRepoLoader" class="com.dome.imserso.api.core.data.EntityRepoLoader" autowire="no">
    <property name="sessionFactory" ref="mysqlSessionFactory"/>
</bean> 
<bean id="oracleEntityRepoLoader" class="com.dome.imserso.api.core.data.EntityRepoLoader" autowire="no">
    <property name="sessionFactory" ref="oracleSessionFactory"/>
</bean> 

But it seems that Spring is trying first to autowire the SessionFactory in any case. I get the following error:

No qualifying bean of type [org.hibernate.SessionFactory] is defined: expected single matching bean but found 2: mysqlSessionFactory,oracleSessionFactory

If I remove the @Autowired all works fine. But I would like to maintain it, as this code is part of a generic lib used for other apps where the usual case is to load only from one database.

Is there any way to accomplish it without removing the annotation?

2

There are 2 answers

1
Qianlong On BEST ANSWER

If you are able to modify your lib which contains EntityLoader, following these 2 step will do the trip :

  1. In EntityLoader make your @Autowired optional:

    @Autowired(required=false)

  2. In XML configuration, exclude mysqlSessionFactory and oracleSessionFactory beans from autowire candidates, add autowire-candidate="false" to each sessionFactory:

<bean id="mysqlSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" autowire-candidate="false">
    <!-- ... config ... -->
</bean>
<bean id="oracleSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" autowire-candidate="false">
    <!-- ... config ... -->
</bean>

Voilà!

2
David Lavender On

It's the component-scan that's causing problems. Your beans are already manually created, but Spring is trying to create another EntityLoader because the component scan is picking up your @Component annotation.

You can tell the component scan (for just your app) to ignore that class:

<context:component-scan ...>
    <context:exclude-filter expression="com\.demo\.EntityLoader" type="regex" />
</context:component-scan>