Open In App

Autowiring an Interface With Multiple Implementations in Spring Boot

Last Updated : 23 Jul, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

In Spring Boot, dependency injection is a key feature that allows developers to wire components (beans) together. Often, multiple implementations of an interface may exist, and the application needs to decide which implementation to inject at runtime. This is common when designing modular systems with different strategies, such as payment services or notification services.

In this article, we will learn how to autowire an interface with multiple implementations using the Spring @Qualifier annotation to resolve ambiguity.

Autowire an Interface With Multiple Implementations

In Spring Boot, autowiring refers to the automatic injection of dependencies. When a class depends on an interface, Spring attempts to inject an implementation of that interface. However, when there are multiple implementations of a single interface, Spring needs clarification on which implementation to inject.

For example: A payment system might have several ways to process payments, such as credit cards or PayPal. In such scenarios, Spring doesn’t automatically know which implementation to inject, so we use strategies like the @Qualifier annotation to specify it.

Implementation of Autowiring an Interface With Multiple Implementations

We will walk through a simple project that demonstrates autowiring an interface with multiple implementations in a Spring Boot project.

Step 1: Create a New Spring Boot Project

Create a new Spring Boot project using IntelliJ IDEA or any IDE of your choice. Choose the following options:

  • Name: autowiring-example
  • Language: Java
  • Type: Maven
  • Packaging: Jar

Click on the Next button.

Project Metadata

Step 2: Add the Dependencies

Add the following dependencies into the Spring Boot project.

  • Spring Web
  • Spring Boot DevTools
  • Lombok

Click on the Create button.

Add Dependencies

Project Structure

After the project creation done, the project folder structure will look like the below image.

Project Folder Structure

Step 3: Configure Application Properties

In the application.properties file, set up basic configurations:

spring.application.name=autowiring-example
server.port=8080

This sets the application name and port for the project.

Step 4: Create the PaymentService Interface

Create a common interface, PaymentService, to define the payment methods that multiple services will implement.

PaymentService.java

Java
package com.gfg.autowiringexample.service;

// Common interface for payment services
public interface PaymentService {
    // Method that processes payments, implemented by each payment service
    String processPayment(double amount);
}

The PaymentService interface defines a single method processPayment(double amount) that all payment services will implement.

Step 5: Create the CreditCardPaymentService Class

This class implements PaymentService for credit card payments.

CreditCardPaymentService.java

Java
package com.gfg.autowiringexample.service;

import org.springframework.stereotype.Service;

// Service that implements PaymentService for credit card payments
@Service("creditCardService")
public class CreditCardPaymentService implements PaymentService {

    @Override
    public String processPayment(double amount) {
        // Implementation of payment processing using credit card
        return "Paid " + amount + " using Credit Card";
    }
}

The CreditCardPaymentService implements the processPayment method for credit card payments. It is annotated with @Service and given a custom name "creditCardService" to differentiate it from other implementations.

Step 6: Create the PayPalPaymentService Class

This class implements PaymentService for PayPal payments.

PayPalPaymentService.java

Java
package com.gfg.autowiringexample.service;

import org.springframework.stereotype.Service;

// Service that implements PaymentService for PayPal payments
@Service("payPalService")
public class PayPalPaymentService implements PaymentService {

    @Override
    public String processPayment(double amount) {
        // Implementation of payment processing using PayPal
        return "Paid " + amount + " using PayPal";
    }
}

The PayPalPaymentService provides the processPayment method for PayPal payments and is annotated with @Service("payPalService") to distinguish it from other implementations.

Step 7: Create the PaymentConfig Configuration

The @Qualifier annotation is used to specify which implementation to inject. In this configuration, we can define and qualify beans manually.

PaymentConfig.java

Java
package com.gfg.autowiringexample.config;

import com.gfg.autowiringexample.service.PaymentService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// Configuration class to define beans
@Configuration
public class PaymentConfig {

    // Define CreditCardPaymentService as a bean with a qualifier
    @Bean
    @Qualifier("creditCard")
    public PaymentService creditCardPaymentService() {
        return new com.gfg.autowiringexample.service.CreditCardPaymentService();
    }

    // Define PayPalPaymentService as a bean with a qualifier
    @Bean
    @Qualifier("payPal")
    public PaymentService payPalPaymentService() {
        return new com.gfg.autowiringexample.service.PayPalPaymentService();
    }
}

In the PaymentConfig class, we manually register CreditCardPaymentService and PayPalPaymentService beans, each qualified with unique names ("creditCard" and "payPal").

Step 8: Create the PaymentController Class

In the controller, we will use the @Qualifier annotation to inject specific implementations.

PaymentController.java

Java
package com.gfg.autowiringexample.controller;

import com.gfg.autowiringexample.service.PaymentService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

// REST controller for payment operations
@RestController
public class PaymentController {

    // Inject specific payment services using qualifiers
    private final PaymentService creditCardService;
    private final PaymentService payPalService;

    // Constructor-based dependency injection with @Qualifier
    public PaymentController(@Qualifier("creditCardService") PaymentService creditCardService,
                             @Qualifier("payPalService") PaymentService payPalService) {
        this.creditCardService = creditCardService;
        this.payPalService = payPalService;
    }

    // Endpoint for credit card payments
    @GetMapping("/pay")
    public String payUsingCreditCard(@RequestParam double amount) {
        return creditCardService.processPayment(amount);
    }

    // Endpoint for PayPal payments
    @GetMapping("/pay-paypal")
    public String payUsingPayPal(@RequestParam double amount) {
        return payPalService.processPayment(amount);
    }
}
  • The PaymentController class uses constructor-based dependency injection with the @Qualifier annotation to inject the CreditCardPaymentService and PayPalPaymentService implementations.
  • There are two endpoints: /pay for credit card payments and /pay-paypal for PayPal payments.

Step 9: Main Application Class

No changes are required in the main class, as Spring Boot will automatically discover the beans.

Java
package com.gfg.autowiringexample;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// Main class for Spring Boot application
@SpringBootApplication
public class AutowiringExampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(AutowiringExampleApplication.class, args);
    }
}

pom.xml File

XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.gfg</groupId>
    <artifactId>autowiring-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>autowiring-example</name>
    <description>autowiring-example</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Step 10: Run the Application

Run the Spring Boot application using your IDE or by using the command:

mvn spring-boot:run

It will start and run at port 8080.

Application Runs

Step 11: Testing the Endpoints

Use Postman or a browser to test the endpoints.

1. Credit Card Endpoint

http://localhost:8080/pay?amount=1000

Response:

Credit Card Endpoint


2. PayPal Endpoint

http://localhost:8080/pay-paypal?amount=2000

Response:

PayPal Endpoint

Conclusion

In this article, we covered how to autowire an interface with multiple implementations in Spring Boot using the @Qualifier annotation. We implemented multiple payment services and injected the specific services into the controller to handle different payment methods. This pattern is useful for scenarios where an application needs to support different strategies or algorithms, giving developers control over which implementation to inject.


Explore