搜索 K
Appearance
Appearance
ModelRegistry 是扫描阶段的唯一出口:
DdlInitializer 把解析好的 ModelDefinition 一个个 register(...) 进来getModel / getAllModels 消费整个注册中心刻意保持薄、只读多写少、零外部依赖。
private final Map<String, ModelDefinition> registry = new ConcurrentHashMap<>();modelName 全局唯一,天然是 Map 形态ApplicationRunner 的单线程内,但读取行为可能由 Web 线程、Scheduled 线程等并发触发;ConcurrentHashMap 提供的 happens-before 语义保证 put-then-get 的正确性为什么不用 HashMap + synchronized?
本场景写入只在启动期一次性发生,读取却是高并发的。ConcurrentHashMap 的分段读优化在这种 write-once-read-many 的访问模式下性能优于 synchronizedMap,且代码更简洁。
public void register(ModelDefinition definition) {
if (definition == null || definition.getModelName() == null) {
throw new IllegalArgumentException("ModelDefinition and modelName must not be null");
}
ModelDefinition previous = registry.put(definition.getModelName(), definition);
if (previous != null) {
log.warn("Duplicate model registration overwritten: modelName={}", definition.getModelName());
}
}两条规则:
null 或 modelName == null 直接 IllegalArgumentException —— 这是契约违反,不容静默为什么是覆盖而不是抛错?
@Model 是合法但可疑的场景,告警而非阻塞更适合一个还在迭代的框架@ConditionalOnProperty("forge.ddl.strict-duplicate=true") 的开关切换为抛错public Collection<ModelDefinition> getAllModels() {
return Collections.unmodifiableCollection(registry.values());
}这里有一个细节:返回的是 Collections.unmodifiableCollection(...),调用方拿到的视图:
add / remove —— 否则 UnsupportedOperationExceptionunmodifiableCollection 只是写保护,不是快照阶段①扫描完成后注册中心不再变化,上述区别不重要;阶段③(DDL 执行)启动后可能需要快照,那时再视情况切换为 List.copyOf(...) 即可。
ModelDefinition.sourceClass字段被@ToString.Exclude标记,避免日志里把整个Class信息打印出来,保持日志清爽。
| 后续阶段 | 可能新增的注册中心能力 |
|---|---|
| SQL 生成 | Collection<ModelDefinition> getStoreModels() —— 只取 STORE 类型 |
| DDL 执行 | 引入版本号,支持 snapshot() 拿一致性视图 |
| 继承组合 | register(...) 之前先做 ABSTRACT 父模型合并 |
当前阶段刻意不引入这些接口,避免无用泛化。