Spring Boot @Qualifier
last modified July 20, 2023
Spring Boot @Qualifier tutorial shows how to differentiate beans of
the same type with @Qualifier. It can also be used to annotate
other custom annotations that can then be used as qualifiers.
Spring is a popular Java application framework and Spring Boot is an evolution of Spring which helps create stand-alone, production-grade Spring based applications with minimal effort.
The following three applications are command line Spring Boot applications.
Differentiating Person beans
In our application, we have two beans of Person type: Student and
Manager. We use the @Qualifier annotation to distinguish between them.
build.gradle
...
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ └───model
│ │ Manager.java
│ │ Person.java
│ │ Student.java
│ └───resources
└───test
└───java
This is the project structure of the Spring Boot application.
plugins {
id 'org.springframework.boot' version '3.1.1'
id 'io.spring.dependency-management' version '1.1.0'
id 'java'
}
group = 'com.zetcode'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
}
This is the Gradle build file. The spring-boot-starter is the core
starter that includes auto-configuration support, logging, and YAML. The
application is packaged into a JAR file.
package com.zetcode.model;
public interface Person {
String info();
}
We have an interface that defines the Person type.
package com.zetcode.model;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
@Qualifier("student")
public class Student implements Person {
@Override
public String info() {
return "Student";
}
}
Student inherits from Person.
@Component is a basic Spring annotation that allows Student
to be detected by Spring containter. The @Qualifier("student") uniquely
identifies this bean with the "student" string.
package com.zetcode.model;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
@Qualifier("manager")
public class Manager implements Person {
@Override
public String info() {
return "Manager";
}
}
We have another bean called Manager. This bean is also identified
with the @Qualifier("manager") annotation.
package com.zetcode;
import com.zetcode.model.Person;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);
@Autowired
@Qualifier("student")
private Person p1;
@Autowired
@Qualifier("manager")
private Person p2;
@Override
public void run(String... args) throws Exception {
logger.info("{}", p1.info());
logger.info("{}", p2.info());
}
}
The CommandLineRunner interface indicates that a bean should run
when it is contained within a SpringApplication. It can be used to
create command line applications in Spring Boot.
@Component
public class MyRunner implements CommandLineRunner {
The CommandLineRunner is also a Spring bean and is decorated with
the @Component annotation; it is auto-detected by Spring.
@Autowired
@Qualifier("student")
private Person p1;
We inject a Person bean into the p1 field. The
@Qualifier("student") specifies that it is a Student
bean.
@Autowired
@Qualifier("manager")
private Person p2;
Likewise, we inject the Manager bean into the p2 field.
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The Application sets up the Spring Boot application.
The @SpringBootApplication annotation enables auto-configuration
and component scanning.
Using factory to create beans
In the second application, we use a factory class to generate beans.
The build.gradle, Person.java,
Application.java, MyRunner.java remain unchanged.
build.gradle
...
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ ├───conf
│ │ │ PersonFactory.java
│ │ └───model
│ │ Manager.java
│ │ Person.java
│ │ Student.java
│ └───resources
└───test
└───java
This is the project structure.
package com.zetcode.model;
public class Manager implements Person {
@Override
public String info() {
return "Manager";
}
}
The annotations are removed from the Manager
class.
package com.zetcode.model;
public class Student implements Person {
@Override
public String info() {
return "Student";
}
}
Likewise, there are no annotations for the Student class.
package com.zetcode.conf;
import com.zetcode.model.Manager;
import com.zetcode.model.Person;
import com.zetcode.model.Student;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PersonFactory {
@Bean
@Qualifier("student")
public Person createStudent() {
return new Student();
}
@Bean
@Qualifier("manager")
public Person createManager() {
return new Manager();
}
}
In the previous example, the beans were auto-detected by Spring. Here, the
PersonFactory creates two beans with the help of the
@Bean annotation.
@Bean
@Qualifier("student")
public Person createStudent() {
return new Student();
}
The @Bean annotation marks methods that define beans.
The @Qualifier("student") tells which implementation
of the Person to create.
Creating custom @Qualifier annotation
To reduce code, we can create custom @Qualifier annotations.
build.gradle
...
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ │ MyRunner.java
│ │ ├───conf
│ │ │ PersonFactory.java
│ │ ├───model
│ │ │ Manager.java
│ │ │ Person.java
│ │ │ Student.java
│ │ └───qualifier
│ │ PersonQ.java
│ └───resources
└───test
└───java
This is the project structure; we list all files except for
build.gradle, which is listed in the first application.
package com.zetcode.model;
public interface Person {
String info();
}
This is the Person type.
package com.zetcode.model;
import org.springframework.stereotype.Component;
@Component
public class Manager implements Person {
@Override
public String info() {
return "Manager";
}
}
The Manager class is decorated with @Component
annotation; it will be auto-detected by Spring.
package com.zetcode.model;
import org.springframework.stereotype.Component;
@Component
public class Student implements Person {
@Override
public String info() {
return "Student";
}
}
The same applies for the Student.
package com.zetcode.qualifier;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.annotation.Qualifier;
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface PersonQ {
String value();
}
Here we define a new @PersonQ qualifier.
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
The @Targer annotation tells where the annotation can be applied.
In our case, it can be applied to fields, methods, and parameters.
@Retention(RetentionPolicy.RUNTIME)
The @Retention annotation specifies how the marked annotation is
stored. With RetentionPolicy.RUNTIME the marked annotation is
retained by the JVM so it can be used by the runtime environment.
public @interface PersonQ {
The @interface keyword is used to declare a new annotation type.
package com.zetcode.conf;
import com.zetcode.model.Manager;
import com.zetcode.model.Person;
import com.zetcode.model.Student;
import com.zetcode.qualifier.PersonQ;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PersonFactory {
@PersonQ("student")
public Person createStudent() {
return new Student();
}
@PersonQ("manager")
public Person createManager() {
return new Manager();
}
}
In the PersonFactory we use the @PersonQ
to identify what kind of beans are created.
package com.zetcode;
import com.zetcode.model.Person;
import com.zetcode.qualifier.PersonQ;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MyRunner.class);
@Autowired
@PersonQ("student")
private Person p1;
@Autowired
@PersonQ("manager")
private Person p2;
@Override
public void run(String... args) throws Exception {
logger.info("{}", p1.info());
logger.info("{}", p2.info());
}
}
In the MyRunner, we inject beans with @Autowired
and @PersonQ annotations.
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
In Application, we set up the Spring Boot application.
In this article we have worked with the @Qualifier annotation.