SpringBoot通过Map实现天然的策略模式

在Spring框架中,自动装配(Auto-wiring)是Spring容器自动将对象注入到其他对象中,以减少手动配置的需求。当你使用Map<String, T>类型的依赖时,Spring提供了几种特殊的方式来自动装配这些Map类型的bean

1. 为什么能"自动收集"所有子类

复制代码
  @Autowired
  private Map<String, OssLogic> ossLogicMap;

这是 Spring 的一个内置特性:当注入目标类型为 Map<String, T> 时,Spring 会把容器中所有的 T 类型(及其子类)Bean 全部收集进这个 Map,key 是 bean 名称,value 是对应的 Bean 实例。

所以这里并不需要手动一个个注册。只要某个类满足:

  • 是 OssLogic / BaseLogic 的子类(或本身)
  • 被 Spring 管理为 Bean(项目里用 @Configuration 标注,等价于 @Component)

例如 BjlhOssLogic extends OssLogic(@Configuration),它们都会被自动装进对应的 Map。

2. bean 名称的命名约定

bean 名称由类名首字母小写得到,例如:

  • BjlhOssLogic → beanName = bjlhOssLogic

init() 启动时按命名约定从 beanName 反推 vendorCode

3.运行时如何分发

注册完成后,OssLogic自身注入了 OssVendorRegistry,通过 vendorCode 查表找到对应处理器,找不到则回退到通用逻辑:

复制代码
  public String getRealCalling(String calling, String callType, String oldCalling, String vendorCode) {
    OssLogic handler = vendorRegistry.findOssLogic(vendorCode);
    if (handler != null) {
      return handler.getRealCalling(calling, callType, oldCalling);
    }
    log.debug("运营商 {} 无专门号码标准化处理逻辑,使用原始号码", vendorCode);
    return calling;

新增一个运营商的处理逻辑时,只需新建一个继承 OssLogic的类,用 @Configuration 标注,且类名遵循 {vendorCode}OssLogic 约定即可,无需修改任何注册代码,Spring 启动时会自动把它收进 Map,init() 自动完成注册。

完整代码

复制代码
import lombok.extern.slf4j.Slf4j;
import net.icsoc.ark.api.common.oss.detail.OssLogic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * OSS 运营商注册中心,利用 Spring Map 自动注入收集所有 OssLogic 子类 Bean。
 * <p>
 * 核心机制:
 * <ul>
 *   <li>通过 {@code Map<String, OssLogic>} 自动注入所有子类</li>
 *   <li>按命名约定 {@code {vendorCode}OssLogic 从 beanName 提取 vendorCode</li>
 *   <li>特殊 vendorCode(含 - 等字符)通过別名映射处理</li>
 *   <li>未找到对应的处理器时返回 null,调用方使用通用规则回退</li>
 * </ul>
 *
 * @author hudeyong
 */
@Component
@Slf4j
public class OssVendorRegistry {

  @Autowired
  private Map<String, OssLogic> ossLogicMap;

  private final Map<String, OssLogic> vendorOssLogicCache = new HashMap<>();

  @PostConstruct
  public void init() {
    // 注册 OssLogic 映射
    if (ossLogicMap != null) {
      for (Map.Entry<String, OssLogic> entry : ossLogicMap.entrySet()) {
        String beanName = entry.getKey();
        // beanName 格式如 "bjlhOssLogic",去掉 "OssLogic" 后缀得到 vendorCode
        String vendorCode = beanName.replace("OssLogic", "");
        vendorOssLogicCache.put(vendorCode, entry.getValue());
        log.info("注册 OssLogic: {} -> {}", vendorCode, entry.getValue().getClass().getSimpleName());
      }
    }
  }

  /**
   * 根据 vendorCode 查找 OssLogic
   *
   * @param vendorCode 运营商代码
   * @return 对应的 OssLogic,找不到返回 null
   */
  public OssLogic findOssLogic(String vendorCode) {
    return vendorOssLogicCache.get(vendorCode);
  }
}
相关推荐
Co_Hui2 小时前
Java 并发编程
java
IT_陈寒2 小时前
JavaScript的this又双叒叕让我怀疑人生了
前端·人工智能·后端
陈随易2 小时前
MCP协议第5次更新,从打电话到微信聊天的巨大变革
前端·后端·程序员
65岁退休Coder2 小时前
LangChain v1.3.4 笔记 - 06 RAG 检索增强生成
后端
妙码生花3 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(四十三):前后端数据验证
后端·go·ai编程
long3163 小时前
Java 新手入门与实战开发指南
java·开发语言
YuePeng3 小时前
别再让 AI 直接写 SQL 了:一个注解搞定十亿行数据的语义层
后端·github
半夜里咳嗽的狼3 小时前
Go 1.25 的 WaitGroup.Go 省了两行代码,也补不上这三个并发边界
后端·go
Lihua奏3 小时前
身份验证:登录之后,服务器怎么一直认得你?
后端