[java] Autowiring two beans implementing same interface - how to set default bean to autowire?

Background:

I have a Spring 2.5/Java/Tomcat application. There is the following bean, which is used throughout the application in many places

public class HibernateDeviceDao implements DeviceDao

and the following bean which is new:

public class JdbcDeviceDao implements DeviceDao

The first bean is configured so (all beans in the package are included)

<context:component-scan base-package="com.initech.service.dao.hibernate" />

The second (new) bean is configured separately

<bean id="jdbcDeviceDao" class="com.initech.service.dao.jdbc.JdbcDeviceDao">
    <property name="dataSource" ref="jdbcDataSource">
</bean>

This results (of course) in an exception when starting the server:

nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.sevenp.mobile.samplemgmt.service.dao.DeviceDao] is defined: expected single matching bean but found 2: [deviceDao, jdbcDeviceDao]

from a class trying to autowire the bean like this

@Autowired
private DeviceDao hibernateDevicDao;

because there are two beans implementing the same interface.

The question:

Is it possible to configure the beans so that

1. I don't have to make changes to existing classes, which already have the HibernateDeviceDao autowired

2. still being able to use the second (new) bean like this:

@Autowired
@Qualifier("jdbcDeviceDao")

I.e. i would need a way to configure the HibernateDeviceDao bean as the default bean to be autowired, simultaneously allowing the usage of a the JdbcDeviceDao when explicitly specifying so with the @Qualifier annotation.

What I've already tried:

I tried setting the property

autowire-candidate="false"

in the bean configuration for JdbcDeviceDao:

<bean id="jdbcDeviceDao" class="com.initech.service.dao.jdbc.JdbcDeviceDao" autowire-candidate="false">
    <property name="dataSource" ref="jdbcDataSource"/>
</bean>

because the Spring documentation says that

Indicates whether or not this bean should be considered when looking for matching candidates to satisfy another bean's autowiring requirements. Note that this does not affect explicit references by name, which will get resolved even if the specified bean is not marked as an autowire candidate.*

which I interpreted to mean that I could still autowire JdbcDeviceDao using the @Qualifier annotation and have the HibernateDeviceDao as default bean. Apparently my interpretation was not correct, though, as this results in the following error message when starting the server:

Unsatisfied dependency of type [class com.sevenp.mobile.samplemgmt.service.dao.jdbc.JdbcDeviceDao]: expected at least 1 matching bean

coming from the class where I've tried autowiring the bean with a qualifier:

@Autowired
@Qualifier("jdbcDeviceDao")

Solution:

skaffman's suggestion to try the @Resource annotation worked. So the configuration has autowire-candidate set to false for jdbcDeviceDao and when using the jdbcDeviceDao I refer to it using the @Resource annotation (instead of @Qualifier):

@Resource(name = "jdbcDeviceDao")
private JdbcDeviceListItemDao jdbcDeviceDao;

This question is related to java spring spring-mvc autowired

The answer is


For Spring 2.5, there's no @Primary. The only way is to use @Qualifier.


The reason why @Resource(name = "{your child class name}") works but @Autowired sometimes don't work is because of the difference of their Matching sequence

Matching sequence of @Autowire
Type, Qualifier, Name

Matching sequence of @Resource
Name, Type, Qualifier

The more detail explanation can be found here:
Inject and Resource and Autowired annotations

In this case, different child class inherited from the parent class or interface confuses @Autowire, because they are from same type; As @Resource use Name as first matching priority , it works.


What about @Primary?

Indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one 'primary' bean exists among the candidates, it will be the autowired value. This annotation is semantically equivalent to the <bean> element's primary attribute in Spring XML.

@Primary
public class HibernateDeviceDao implements DeviceDao

Or if you want your Jdbc version to be used by default:

<bean id="jdbcDeviceDao" primary="true" class="com.initech.service.dao.jdbc.JdbcDeviceDao">

@Primary is also great for integration testing when you can easily replace production bean with stubbed version by annotating it.


The use of @Qualifier will solve the issue.
Explained as below example : 
public interface PersonType {} // MasterInterface

@Component(value="1.2") 
public class Person implements  PersonType { //Bean implementing the interface
@Qualifier("1.2")
    public void setPerson(PersonType person) {
        this.person = person;
    }
}

@Component(value="1.5")
public class NewPerson implements  PersonType { 
@Qualifier("1.5")
    public void setNewPerson(PersonType newPerson) {
        this.newPerson = newPerson;
    }
}

Now get the application context object in any component class :

Object obj= BeanFactoryAnnotationUtils.qualifiedBeanOfType((ctx).getAutowireCapableBeanFactory(), PersonType.class, type);//type is the qualifier id

you can the object of class of which qualifier id is passed.

Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to spring

Are all Spring Framework Java Configuration injection examples buggy? Two Page Login with Spring Security 3.2.x Access blocked by CORS policy: Response to preflight request doesn't pass access control check Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean Failed to auto-configure a DataSource: 'spring.datasource.url' is not specified Spring Data JPA findOne() change to Optional how to use this? After Spring Boot 2.0 migration: jdbcUrl is required with driverClassName The type WebMvcConfigurerAdapter is deprecated No converter found capable of converting from type to type

Examples related to spring-mvc

Two Page Login with Spring Security 3.2.x ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean Spring 5.0.3 RequestRejectedException: The request was rejected because the URL was not normalized The type WebMvcConfigurerAdapter is deprecated RestClientException: Could not extract response. no suitable HttpMessageConverter found Spring boot: Unable to start embedded Tomcat servlet container UnsatisfiedDependencyException: Error creating bean with name 8080 port already taken issue when trying to redeploy project from Spring Tool Suite IDE Error creating bean with name 'entityManagerFactory' defined in class path resource : Invocation of init method failed Difference between the annotations @GetMapping and @RequestMapping(method = RequestMethod.GET)

Examples related to autowired

intellij incorrectly saying no beans of type found for autowired repository How do I mock an autowired @Value field in Spring with Mockito? @Autowired - No qualifying bean of type found for dependency Spring can you autowire inside an abstract class? Why is my Spring @Autowired field null? Understanding Spring @Autowired usage @Autowired and static method Injecting @Autowired private field during testing Spring expected at least 1 bean which qualifies as autowire candidate for this dependency Exclude subpackages from Spring autowiring?