5 Ways to Inject Spring Boot Implementations (Qualifier, Collections, Profile)
When a Spring Boot interface has several implementations, injecting the correct bean can cause NoUniqueBeanDefinitionException; this article explains five injection techniques—@Qualifier with constructor injection, @Primary, @Resource, collection or Map injection, and @Profile-based environment isolation—showing code examples, usage rules, and best‑practice recommendations.
Problem
Injecting an interface that has multiple implementations with @Autowired causes NoUniqueBeanDefinitionException because Spring cannot decide which bean to inject.
Method 1 – @Qualifier (most precise, most common)
Specify the bean name to resolve ambiguity. Works with field, constructor (recommended), or setter injection.
@Service
public class OrderService {
private final PaymentService paymentService;
// Constructor injection + @Qualifier
public OrderService(@Qualifier("wechatPayService") PaymentService paymentService) {
this.paymentService = paymentService;
}
public void checkout(double amount) {
System.out.println(paymentService.pay(amount));
}
}If @Qualifier is omitted, the default bean name is the class name with a lower‑case first letter (e.g., wechatPayService).
Method 2 – @Primary (set default implementation)
When multiple candidates exist and no @Qualifier is used, the bean marked with @Primary is selected.
@Component
@Primary
public class AlipayService implements PaymentService {
@Override
public String pay(double amount) {
return "[Default] 支付宝支付:" + amount;
}
}
@Component
public class WechatPayService implements PaymentService {
// ... implementation
}
@Service
public class OrderService {
@Autowired
private PaymentService paymentService; // Injects AlipayService
}If both @Qualifier and @Primary are present, @Qualifier takes precedence.
Method 3 – @Resource (JSR‑250)
Java standard annotation (in javax.annotation or jakarta.annotation). It injects by name first, then by type, and cannot be combined with @Qualifier. The bean name can be set via the name attribute.
import jakarta.annotation.Resource;
@Service
public class OrderService {
@Resource(name = "alipayService") // Directly specify bean name
private PaymentService paymentService;
public void checkout(double amount) {
System.out.println(paymentService.pay(amount));
}
}Comparison of matching mechanisms: @Autowired resolves by type then by name. @Resource resolves by name then by type.
Method 4 – Collection / Map Injection (Strategy Pattern)
Spring can inject all implementations of an interface into a List or Map. Useful for strategy or routing patterns where the concrete algorithm is chosen at runtime.
import java.util.Map;
@Service
public class PaymentStrategyService {
private final Map<String, PaymentService> paymentServiceMap;
// Spring automatically injects bean name as key and bean instance as value
public PaymentStrategyService(Map<String, PaymentService> paymentServiceMap) {
this.paymentServiceMap = paymentServiceMap;
}
public String executePayment(String beanName, double amount) {
PaymentService service = paymentServiceMap.get(beanName);
if (service == null) {
throw new IllegalArgumentException("Unsupported payment method: " + beanName);
}
return service.pay(amount);
}
}
@Service
public class PaymentListService {
@Autowired
private List<PaymentService> paymentServices; // Contains all implementations
public void printAllPayments(double amount) {
paymentServices.forEach(ps -> System.out.println(ps.pay(amount)));
}
}Method 5 – @Profile (environment isolation)
Activate beans based on the currently active Spring profile (e.g., dev, test, prod). Handy when different implementations are needed in development versus production.
@Profile("dev")
@Component
public class MockPayService implements PaymentService {
@Override
public String pay(double amount) {
return "[Mock] 支付:" + amount;
}
}
@Profile("prod")
@Component
public class RealPayService implements PaymentService {
@Override
public String pay(double amount) {
return "[Real] 扣款成功:" + amount;
}
}Activate the desired profile in application.yml:
spring:
profiles:
active: dev # or prodRecommended practice
Prefer @Qualifier combined with constructor injection. This combination is endorsed by Spring, ensures immutability, and simplifies testing.
public class Client {
private final PaymentService paymentService;
public Client(@Qualifier("wechatPayService") PaymentService paymentService) {
this.paymentService = paymentService;
}
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Senior Xiao Ying
Dedicated to sharing Java backend technical experience and original tutorials, offering career transition advice and resume editing. Recognized as a rising star in CSDN's Java backend community and ranked Top 3 in the 2022 New Star Program for Java backend.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
