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);
  }
}
相关推荐
vx-Biye_Design20 分钟前
SSM伴侣动物伴护星小程序06330-计算机课程设计、毕业设计
spring boot·后端·elasticsearch·小程序·架构·课程设计·idea
2分钟速写快排2 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
面向Google编程2 小时前
Apache Iceberg Variant 类型:v3 半结构化数据不再「二选一」
大数据·后端
lhldsg3 小时前
全民健身解决方案小程序开发:从0到1的技术实战
java·数据库·需求分析
jason成都3 小时前
Spring WebFlux 适配达梦新方案|dm‑r2dbc:Netty 异步传输的实验性 R2DBC 驱动
java·后端·spring
泡海椒3 小时前
内置SPI函数库详解:JQuick-Java Builtin工具类实战用法
java·开发语言·python
辰烨chenye3 小时前
LeetCode Hot 100 题解 · 二分篇
java·算法·leetcode
小羊没烦恼!4 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰4 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
小荷才露尖尖角,早有蜻蜓立上头5 小时前
过滤器的4种创建方式
java