使用 Spring 注入 Map 实现策略模式-一个接口多个实现(附完整示例)

在实际开发中,我们经常会遇到需要根据不同的业务类型选择不同处理逻辑的情况。比如:

  • 根据用户选择的支付方式(支付宝、微信、银联)执行不同的支付逻辑
  • 根据报告类型(PDF、Word、Excel)生成不同格式的文件
  • 根据大模型名称(GPT、Claude、Qwen)调用不同模型服务

这时候,使用 策略模式(Strategy Pattern) 是一个非常合适的选择。

本文将介绍如何利用 Spring 框架中 @Resource 注入 Map 的特性,优雅地实现策略模式。


✅ 一、什么是策略模式?

策略模式是一种行为设计模式,它允许定义一系列算法,并将每个算法封装起来,使它们可以互换使用。

通俗点说:

我们有一组“相似”的处理实现类,通过传入不同的参数,动态选择其中某一个实现类来执行。


✅ 二、Spring 如何注入 Map?

使用@Resource自动将容器中所有 SomeService 类型的 Bean 收集起来

Spring 提供了一个强大的能力:
当你声明如下字段时:

@Resource
private Map<String, SomeService> someServiceMap;

Spring 会自动将容器中所有 SomeService 类型的 Bean 收集起来,并以 Bean 名称 -> Bean 实例 的形式放入这个 Map 中。

这为我们实现策略模式提供了极大的便利。

为什么用 @Resource 而不是 @Autowired

注解说明
@Resource默认按 名称 注入(name 属性),也可以指定 name。适合用来注入 Map、List 等集合类型。
@Autowired默认按 类型 注入,不能直接用于 Map 注入多个同类型 Bean

所以在这里,我们要注入的是:

Map<String, LlmHandlerService>

只有 @Resource 可以自动识别并注入这些 Bean 到 Map 中。


✅ 三、完整实现步骤

1️⃣ 定义统一接口

public interface ReportGenerator {
    void generate();
}

2️⃣ 编写多个实现类并注册为 Spring Bean

@Service("pdfReportGenerator")
public class PdfReportGenerator implements ReportGenerator {
    @Override
    public void generate() {
        System.out.println("Generating PDF report...");
    }
}

@Service("excelReportGenerator")
public class ExcelReportGenerator implements ReportGenerator {
    @Override
    public void generate() {
        System.out.println("Generating Excel report...");
    }
}

@Service("wordReportGenerator")
public class WordReportGenerator implements ReportGenerator {
    @Override
    public void generate() {
        System.out.println("Generating Word report...");
    }
}

3️⃣ 创建策略管理器(核心)

@Component
public class ReportGenerationStrategyManager {

    @Resource
    private Map<String, ReportGenerator> reportGeneratorMap;

    public ReportGenerator getStrategy(String type) {
        ReportGenerator strategy = reportGeneratorMap.get(type);
        if (strategy == null) {
            throw new IllegalArgumentException("Unsupported report type: " + type);
        }
        return strategy;
    }
}

4️⃣ 使用方式

@RestController
@RequestMapping("/report")
public class ReportController {

    @Autowired
    private ReportGenerationStrategyManager strategyManager;

    @GetMapping("/{type}")
    public String generateReport(@PathVariable String type) {
        ReportGenerator generator = strategyManager.getStrategy(type);
        generator.generate();
        return "Report generated with type: " + type;
    }
}

✅ 四、调用测试

访问以下 URL:

GET /report/pdf
GET /report/excel
GET /report/word

输出结果分别为:

Generating PDF report...
Generating Excel report...
Generating Word report...

如果传入不支持的类型,如 /report/html,会抛出异常:

IllegalArgumentException: Unsupported report type: html

✅ 五、优化建议(推荐做法)

虽然上面的方式已经足够简洁,但为了增强可读性安全性,我们可以进一步优化:

✅ 使用枚举代替字符串参数

public enum ReportType {
    PDF,
    EXCEL,
    WORD
}

然后修改策略管理器:

public ReportGenerator getStrategy(ReportType type) {
    String beanName = type.name().toLowerCase() + "ReportGenerator";
    ReportGenerator strategy = reportGeneratorMap.get(beanName);
    if (strategy == null) {
        throw new IllegalArgumentException("Unsupported report type: " + type);
    }
    return strategy;
}

这样我们在调用时就可以使用枚举,避免拼写错误:

generator = strategyManager.getStrategy(ReportType.PDF);

✅ 六、优势总结

优势描述
✅ 简洁易维护不需要硬编码 switch-case 或 if-else 判断
✅ 可扩展性强新增策略只需添加新实现类,无需改动已有代码
✅ 高内聚低耦合所有策略统一管理,解耦业务逻辑与实现细节
✅ 强类型安全推荐结合枚举使用,避免字符串拼写错误

✅ 七、适用场景

  • 多种支付方式切换
  • 多种消息通知渠道(短信、邮件、企业微信)
  • 多语言翻译适配
  • 多个 AI 模型服务调用
  • 多个数据源适配器

✅ 八、注意事项

  • 确保每个策略类都加上了 @Service 并指定了唯一的 bean name
  • 推荐显式命名 bean,避免 Spring 自动生成的名称不可控
  • 如果策略类较多,建议配合日志打印加载情况,方便排查问题
  • 可以考虑加入缓存机制或懒加载策略
Spring Boot中,策略模式常用于根据不同的条件选择不同的实现类。通过将实现注入一个Map中,可以方便地在运行时根据条件选择合适的实现类。以下是如何在Spring Boot中实现策略模式注入Map的步骤: 1. **定义策略接口**:首先,定义一个策略接口,所有策略实现类都需要实现这个接口。 ```java public interface Strategy { void execute(); } ``` 2. **实现策略接口**:创建多个实现类,实现策略接口。 ```java @Component("strategyA") public class StrategyA implements Strategy { @Override public void execute() { System.out.println("Executing Strategy A"); } } @Component("strategyB") public class StrategyB implements Strategy { @Override public void execute() { System.out.println("Executing Strategy B"); } } ``` 3. **注入Map**:在需要使用策略模式的地方,通过将所有实现注入一个Map中。 ```java @Service public class StrategyService { private final Map<String, Strategy> strategies; @Autowired public StrategyService(Map<String, Strategy> strategies) { this.strategies = strategies; } public void executeStrategy(String strategyName) { Strategy strategy = strategies.get(strategyName); if (strategy != null) { strategy.execute(); } else { System.out.println("No strategy found for the given name"); } } } ``` 4. **使用策略服务**:在需要使用策略模式的地方,调用策略服务的方法并传入策略名称。 ```java @RestController public class StrategyController { @Autowired private StrategyService strategyService; @GetMapping("/executeStrategy") public void executeStrategy(@RequestParam String strategyName) { strategyService.executeStrategy(strategyName); } } ``` 在上述示例中,`StrategyService` 通过构造函数注入了所有实现了 `Strategy` 接口的Bean,并将其存储在一个Map中。`StrategyController` 通过HTTP请求调用 `executeStrategy` 方法,传入策略名称。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苍煜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值