在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);
}
}