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'}}