Skip to content

实战与速查

注解写好后,元数据全部存活在 ModelRegistry 里 —— 后续无论是生成 SQL、给前端低代码渲染表单、还是写运维巡检脚本,都是从这里拿数据。本章给出编程式访问元数据的最小示例,并附核心类速查表便于回查方法签名。

实战:在业务代码里访问元数据

ModelRegistry 是普通 Spring Bean,注入即可使用:

java
import cn.cvking.forge.ddl.model.FieldDefinition;
import cn.cvking.forge.ddl.model.ModelDefinition;
import cn.cvking.forge.ddl.registry.ModelRegistry;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collection;

@RestController
@RequiredArgsConstructor
public class MetadataInspectorController {

    private final ModelRegistry registry;

    /** 列出全部已注册模型 */
    @GetMapping("/_meta/models")
    public Collection<ModelDefinition> all() {
        return registry.getAllModels();
    }

    /** 按 modelName 查单个模型 */
    @GetMapping("/_meta/models/{name}")
    public ModelDefinition byName(@PathVariable String name) {
        return registry.getModel(name);
    }
}

启动后访问 GET /_meta/models 即可得到形如:

json
[
  {
    "modelName": "user",
    "tableName": "user",
    "modelType": "STORE",
    "displayName": "用户",
    "fields": [
      { "fieldName": "name", "columnName": "user_name",
        "javaType": "java.lang.String", "fieldType": "STRING",
        "length": 64, "nullable": true, "store": true }
    ]
  }
]

ModelDefinitionFieldDefinition 都是 @Data @Builder POJO + 实现 Serializable,Jackson 可直接序列化。

典型用途

  • 低代码平台:根据 fields 元数据驱动表单/列表渲染
  • 运维巡检脚本:扫描 store=true 的字段,检查是否在数据库已建表
  • 测试夹具生成:从模型元数据反推 fixture 数据结构

核心类速查

所有类位于 cn.cvking.forge.ddl 包及其子包。

DdlAutoConfiguration

包:cn.cvking.forge.ddl

java
@AutoConfiguration
@ComponentScan(basePackageClasses = DdlAutoConfiguration.class)
public class DdlAutoConfiguration {}

自动装配入口,通过 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 注册到 Spring Boot。详细机制见 前置知识 · Spring Boot 自动装配

DdlInitializer

包:cn.cvking.forge.ddl.runnerimplements ApplicationRunner,由 Spring Boot 在启动尾部自动触发。

方法签名用途
runvoid run(ApplicationArguments args)主流程入口:取扫描包 → 扫描 → 解析 → 注册
fetchScanPackagesprivate String[] fetchScanPackages()通过 AutoConfigurationPackages.get(beanFactory) 取扫描根
parseModelprivate ModelDefinition parseModel(Class<?>)把类解析为 ModelDefinition
parseFieldsprivate List<FieldDefinition> parseFields(Class<?>)遍历声明字段,跳过 static
buildFieldDefinitionprivate FieldDefinition buildFieldDefinition(Field)单字段转换;无相关注解时返回 null
inferTypeFromJavaTypeprivate FieldType inferTypeFromJavaType(Field)Java 类型兜底推导;未命中抛 IllegalStateException
fillTypeAttributesprivate void fillTypeAttributes(...)按字段类型填充 length / precision / scale / dateType

构造期注入 ClazzScanner / ModelRegistry / BeanFactory

ClazzScanner

包:cn.cvking.forge.ddl.scanner

方法签名用途
scanSet<Class<?>> scan(String... packages)扫描指定包路径下所有 @Model 类;返回保持扫描顺序的 LinkedHashSet
buildProviderprivate ClassPathScanningCandidateComponentProvider buildProvider()构造扫描器:关闭默认 filter、加 @AnnotationTypeFilter(Model.class)、排除内部类
isUnsupportedClassprivate boolean isUnsupportedClass(Class<?>)反射兜底过滤:成员/匿名/局部/抽象/接口

底层使用 ClassPathScanningCandidateComponentProvider,详见 前置知识 · Spring 包扫描

ModelRegistry

包:cn.cvking.forge.ddl.registry

java
private final Map<String, ModelDefinition> registry = new ConcurrentHashMap<>();
方法签名行为
registervoid register(ModelDefinition definition)注册;nullmodelName == nullIllegalArgumentException;重名 WARN + 覆盖
getModelModelDefinition getModel(String modelName)单查询,未命中返回 null
getAllModelsCollection<ModelDefinition> getAllModels()返回 Collections.unmodifiableCollection(registry.values())

详细策略与并发选型见 设计文档 · 模型注册中心

ModelDefinition

包:cn.cvking.forge.ddl.model@Data @Builder,实现 Serializable

字段类型说明
modelNameString模型唯一标识
tableNameString数据库表名
modelTypeModelTypeEnum模型类型
displayNameString展示名
summaryString描述摘要
sourceClassClass<?>原始 Class(@ToString.Exclude
fieldsList<FieldDefinition>字段定义列表

FieldDefinition

包:cn.cvking.forge.ddl.model@Data @Builder,实现 Serializable

字段类型说明
fieldNameStringJava 字段名
columnNameString数据库列名
javaTypeClass<?>Java 类型
fieldTypeFieldType业务字段类型
lengthInteger字符串长度(仅 STRING
precisionInteger总位数(仅 BIG_DECIMAL
scaleInteger小数位(仅 BIG_DECIMAL
dateTypeDateType日期语义(仅 DATE
nullableBoolean是否可空(基本类型 false,包装/引用类型 true
storeBoolean是否持久化(来自 @Field.Advanced.store

NamingUtils

包:cn.cvking.forge.ddl.utils,工具类(final,私有构造)。

静态方法签名用途
computeTableNameString computeTableName(Model.Advanced advanced, String modelName)优先用 @Model.Advanced.table;否则 camelToUnderscore(modelName)
resolveColumnNameString resolveColumnName(Field.Advanced advanced, String fieldName)优先用 @Field.Advanced.column;否则 camelToUnderscore(fieldName)
camelToUnderscoreString camelToUnderscore(String name)驼峰转下划线小写

FieldTypeHelper

包:cn.cvking.forge.ddl.utils,工具类。

静态方法签名用途
findByAnnotationFieldType findByAnnotation(Field reflectField)按字段上的类型子注解查 FieldType;未命中返回 null
findByJavaTypeFieldType findByJavaType(Class<?> javaType)按 Java 类型查 FieldType;未命中返回 null

内部维护两张静态 Map:

  • ANNOTATION_TO_TYPE:8 个类型子注解 → FieldType
  • JAVA_TYPE_TO_TYPE:14 种 Java 类型 → FieldType(含基本类型与包装类型)

详见 设计文档 · 类型推导