Ebooks

Spring Boot @Qualifier tutorial

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.

pom.xml
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.

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zetcode</groupId>
    <artifactId>springbootqualifier</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
    </parent>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

This is the Maven 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.

com/zetcode/model/Person.java
package com.zetcode.model;

public interface Person {

    String info();
}

We have an interface that defines the Person type.

com/zetcode/model/Student.java
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.

com/zetcode/model/Manager.java
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.

com/zetcode/MyRunner.java
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.

com/zetcode/Application.java
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 pom.xml, Person.java, Application.java, MyRunner.java remain unchanged.

pom.xml
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.

com/zetcode/model/Manager.java
package com.zetcode.model;

public class Manager implements Person {

    @Override
    public String info() {

        return "Manager";
    }
}

The annotations are removed from the Manager class.

com/zetcode/model/Student.java
package com.zetcode.model;

public class Student implements Person {

    @Override
    public String info() {
        
        return "Student";
    }
}

Likewise, there are no annotations for the Student class.

com/zetcode/conf/PersonFactory.java
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.

pom.xml
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 pom.xml, which is listed in the first application.

com/zetcode/model/Person.java
package com.zetcode.model;

public interface Person {
    
    String info();
}

This is the Person type.

com/zetcode/model/Manager.java
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.

com/zetcode/model/Student.java
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.

com/zetcode/qualifier/PersonQ.java
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.

com/zetcode/conf/PersonFactory.java
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.

com/zetcode/MyRunner.java
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.

com/zetcode/Application.java
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.

You might also be interested in Java tutorial or list all Spring Boot tutorials.