2. 默认行为问题

当接口存在多个实现类时,直接自动装配会踩坑——Spring会抛出异常:required a single bean, but X were found。这是因为Spring无法确定该注入哪个实现。不过Spring提供了多种解决方案来精确控制。

3. 使用限定符(@Qualifier)解决歧义

通过@Qualifier注解可以明确指定要注入的实现类:

3.1 自定义限定符名称

@Service
@Qualifier("goodServiceA-custom-name")
public class GoodServiceA implements GoodService {
    // 实现代码
}

3.2 在注入时指定实现

@Autowired
public SimpleQualifierController(
    @Qualifier("goodServiceA-custom-name") GoodService niceServiceA,
    @Qualifier("goodServiceB") GoodService niceServiceB,
    GoodService goodServiceC
) {
    this.goodServiceA = niceServiceA;
    this.goodServiceB = niceServiceB;
    this.goodServiceC = goodServiceC;
}

3.3 限定符匹配规则

自定义限定符:优先使用@Qualifier指定的名称
类名匹配:未指定时,使用类名首字母小写的驼峰形式(如GoodServiceBgoodServiceB
参数名匹配:未使用@Qualifier时,Spring会尝试按参数名匹配(如goodServiceC参数匹配GoodServiceC类)

4. 主实现类(@Primary)方案

使用@Primary标记默认实现,当存在多个候选Bean且未指定限定符时,Spring会优先注入该实现:

@Primary
@Service
public class GoodServiceC implements GoodService {
    // 实现代码
}

适用场景:某个实现被频繁使用时,可避免大量@Qualifier注解。

5. 基于环境(Profile)的动态选择

通过Spring Profile实现不同环境使用不同实现:

@Service
@Profile("dev")
public class AzureFileStorage implements FileStorage {
    // 开发环境实现
}

@Service
@Profile("prod")
public class S3FileStorage implements FileStorage {
    // 生产环境实现
}

典型用例:文件存储服务在开发环境使用本地存储,生产环境使用云存储。

6. 批量注入所有实现

Spring支持将接口的所有实现类注入集合:

6.1 注入List/Set/Array

@Autowired
public SimpleCollectionController(List<GoodService> goodServices) {
    this.goodServices = goodServices; // 包含所有激活的GoodService实现
}

6.2 注入Map(Bean名→实例)

@Autowired
public SimpleCollectionController(Map<String, GoodService> goodServiceMap) {
    this.goodServiceMap = goodServiceMap;
}

public void printAllHellos() {
    String messageA = goodServiceMap.get("goodServiceA").getHelloMessage();
    String messageB = goodServiceMap.get("goodServiceB").getHelloMessage();
    // 使用消息
}

⚠️ 重要:集合注入会忽略未激活的Profile Bean,但会包含所有符合条件的Bean(无论限定符)。

7. 高级控制技巧

7.1 条件化装配(@Conditional)

通过自定义条件控制Bean是否参与自动装配:

public class OnWindowsCondition implements Condition {
    @Override 
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment()
            .getProperty("os.name")
            .toLowerCase()
            .contains("windows");
    } 
}

@Component 
@Conditional(OnWindowsCondition.class) 
public class WindowsFileService implements FileService {
    // 仅在Windows系统生效
}

7.2 可选注入处理

当条件可能不满足时,使用可选注入避免异常:

// 方式1:显式设为可选(需手动检查null)
@Autowired(required = false)
private GoodService goodService;

// 方式2:使用Optional(推荐)
@Autowired
private Optional<GoodService> goodService;

7.3 集合注入排序

使用@Order控制集合中Bean的顺序:

@Order(2) 
public class GoodServiceA implements GoodService { 
    // 后加载
} 

@Order(1) 
public class GoodServiceB implements GoodService {
    // 优先加载
} 

生效范围:仅对List/Array有效,对Set无效(Set本身无序)。

8. 总结

Spring提供了多维度控制多实现接口自动装配的能力:

  • 基础方案@Qualifier精确指定 + @Primary默认选择
  • 环境隔离@Profile实现环境相关实现
  • 批量处理:集合注入获取所有实现
  • 高级控制@Conditional条件化 + @Order排序

⚠️ 使用建议:避免过度设计,简单场景优先用@Qualifier/@Primary,复杂场景再考虑条件化装配。不当使用会增加维护成本。

示例代码可在GitHub仓库获取完整实现。


原始标题:Autowiring an Interface With Multiple Implementations | Baeldung