Handling Multiple Autowire Candidates with Spring @Primary

In spring, dependency injection is done default by type, this means that when there are multiple dependencies with the same type a NoUniqueBeanDefinitionException exception will be thrown. Indicating that only one candidate can be applied. You can take control over the selection process. In this tutorial we’ll see how to accomplish this with spring’s @Primary annotation or the primary xml attribute of the <bean/> element. This indicates that a particular bean definition should be given preference when multiple beans are candidate to be autowired.

Beans

Let’s define some beans to demonstrate this. First we have the Person bean which has an autowired field of type Job. Later we’ll be creating multiple Job beans as candidates to inject in this bean.

package com.memorynotfound.spring.core.autowired;

import org.springframework.beans.factory.annotation.Autowired;

public class Person {

    @Autowired
    private Job job;

    @Override
    public String toString() {
        return "Person{" +
                "job=" + job +
                '}';
    }
}

This bean will be injected in the Person bean. Multiple beans will be created.

package com.memorynotfound.spring.core.autowired;

public class Job {

    private String name;

    public Job(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Job{" +
                "name='" + name + '\'' +
                '}';
    }
}

Using @Primary annotation and primary attribute

We are creating mutliple beans of the same type, so technically spring will throw a NoUniqueBeanDefinitionException exception if we do not give a preference to one of the beans. We configure this using the primary xml attribute of the <bean/> element. This tells spring when there are multiple types of the same bean, give preference to this bean. If only one bean with the primary attribute is found, then it will be autowired. If there are multiple beans of the same type with primary an exception will be thrown and the application will not start.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="com.memorynotfound.spring.core.autowired.Person"/>

    <bean class="com.memorynotfound.spring.core.autowired.Job" primary="true">
        <constructor-arg name="name" value="Java Developer"/>
    </bean>

    <bean class="com.memorynotfound.spring.core.autowired.Job">
        <constructor-arg name="name" value="Front End Developer"/>
    </bean>

</beans>

The equivalent to the primary attribute of the <bean/> element is the @Primary of the java configuration. This does the exact same thing.

package com.memorynotfound.spring.core.autowired;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
@ComponentScan(basePackages = "com.memorynotfound")
public class Config {

    @Bean
    public Person person(){
        return new Person();
    }

    @Bean
    @Primary
    public Job javaDeveloper(){
        return new Job("Java Developer");
    }

    @Bean
    public Job frontEndDeveloper(){
        return new Job("Front end Developer");
    }
}

Running The Application

We bootstrap spring using the ClassPathXmlApplicationContext, if we would have loaded the java config we should have used the AnnotationConfigApplicationContext and provide the class name of the configuration file as argument.

package com.memorynotfound.spring.core.autowired;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Run {

    public static void main(String... args) {
        ApplicationContext xmlContext = new ClassPathXmlApplicationContext("app-config.xml");
        Person javaDeveloper = xmlContext.getBean(Person.class);
        System.out.println(javaDeveloper);
    }
}

Output

The bean with the primary attribute has been autowired.

Person{job=Job{name='Java Developer'}}

References

Download

You may also like...