使用 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 自动生成的名称不可控
  • 如果策略类较多,建议配合日志打印加载情况,方便排查问题
  • 可以考虑加入缓存机制或懒加载策略
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

苍煜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值