Solon v3.10.7

注解提炼与别名

</> markdown
2026年5月29日 上午9:10:07

EggG 通过 Handler 机制将注解解析逻辑集中管理。注册 Handler 后,元数据创建时会自动调用,结果随元数据一起缓存。

1、问题场景

假设你在开发一个 JSON 序列化框架,用户类如下:

public class User {
    @ONodeAttr(alias = "user_name")
    private String userName;

    @ONodeAttr(alias = "created_at")
    private Date createdAt;
}

需要把 userName 字段序列化为 "user_name"。传统做法是在每个使用点写 if-else + getAnnotation()

ONodeAttr attr = field.getAnnotation(ONodeAttr.class);
String alias = (attr != null) ? attr.alias() : field.getName();

字段和注解类型一多,逻辑就散落在各处,维护成本上升。类似的问题也出现在依赖注入(读 @Inject)、ORM(读 @Column)等场景。

EggG 通过 DigestHandlerAliasHandler 将这个逻辑统一收拢:编写一次解析规则,所有元数据自动处理。

2、Handler 体系

DigestHandler — 提炼处理器

DigestHandler 是一个函数式接口,在元数据创建时自动调用,从注解中提炼出自定义信息:

@FunctionalInterface
public interface DigestHandler {
    Object apply(ClassEggg classEggg, AnnotatedEggg source, Object defaultValue);
}

参数说明: - classEggg:当前类的元数据(提供类级别上下文) - source:当前正在处理的注解元素,可能是 FieldEggg、MethodEggg、ParamEggg 等 - defaultValue:默认值(通常为 null)

返回值是提炼出的自定义对象,后续通过 source.getDigest() 获取。

AliasHandler — 别名处理器

AliasHandler 是函数式接口,用于为字段、参数、属性生成别名:

@FunctionalInterface
public interface AliasHandler {
    String apply(ClassEggg classEggg, AnnotatedEggg source, String defaultValue);
}

参数说明: - classEggg:当前类的元数据 - source:当前正在处理的注解元素 - defaultValue:默认别名(通常是字段/参数的原始名称)

返回值是最终的别名。注解没有指定别名时返回 defaultValue,有指定时返回注解中的值。

3、注册 Handler

在创建 Eggg 实例时通过 withXxxHandler 方法注册:

Eggg eggg = new Eggg()
    .withDigestHandler(this::doDigestHandle)
    .withAliasHandler(this::doAliasHandle)
    .withReflectHandler(new CustomReflectHandler());

注册后,每次创建 FieldEggg、MethodEggg、ParamEggg 等元数据时,Handler 会自动被调用,结果直接缓存。

4、实战:Snack4 集成示例

以下代码展示了在 JSON 库 Snack4 中,用 EggG 统一处理 @ONodeAttr 注解:

public class EgggUtil {
    private static final Eggg eggg = new Eggg()
            .withCreatorClass(ONodeCreator.class)
            .withDigestHandler(EgggUtil::doDigestHandle)
            .withAliasHandler(EgggUtil::doAliasHandle);

    /**
     * 别名处理器:从 Digest 中提取别名
     */
    private static String doAliasHandle(ClassEggg cw, AnnotatedEggg s, String ref) {
        if (s.getDigest() instanceof ONodeAttrHolder) {
            return ((ONodeAttrHolder) s.getDigest()).getAlias();
        } else {
            return ref;
        }
    }

    /**
     * 提炼处理器:从注解生成 ONodeAttrHolder
     */
    private static Object doDigestHandle(ClassEggg cw, AnnotatedEggg s, Object ref) {
        ONodeAttr attr = s.getElement().getAnnotation(ONodeAttr.class);

        if (attr == null && ref != null) {
            return ref;
        }

        if (s instanceof FieldEggg) {
            return new ONodeAttrHolder(attr, ((Field) s.getElement()).getName());
        } else if (s instanceof PropertyMethodEggg) {
            return new ONodeAttrHolder(attr,
                Property.resolvePropertyName(((Method) s.getElement()).getName()));
        } else if (s instanceof ParamEggg) {
            return new ONodeAttrHolder(attr, ((Parameter) s.getElement()).getName());
        } else {
            return null;
        }
    }

    public static TypeEggg getTypeEggg(Type type) {
        return eggg.getTypeEggg(type);
    }
}

在业务代码中读取提炼结果:

TypeEggg typeEggg = EgggUtil.getTypeEggg(clazz);

for (FieldEggg fw : typeEggg.getClassEggg().getAllFieldEgggs()) {
    if (fw.isStatic()) {
        continue;
    }

    // 获取提炼物
    ONodeAttrHolder holder = fw.getDigest();

    // 获取泛型信息
    TypeEggg fieldType = fw.getTypeEggg();

    // 获取别名
    String alias = fw.getAlias();
}

5、实战:Solon 框架集成示例

以下代码展示了 Solon 框架如何通过 DigestHandler 将字段和参数统一封装为 FieldSpec / ParamSpec

public class EgggUtil {
    private static final Eggg eggg = new Eggg()
            .withAliasHandler(EgggUtil::doAliasHandle)
            .withDigestHandler(EgggUtil::doDigestHandle)
            .withReflectHandler(new EgggReflectHandler());

    private static String doAliasHandle(ClassEggg cw, AnnotatedEggg s, String ref) {
        if (s.getDigest() instanceof VarSpec) {
            return s.<VarSpec>getDigest().getName();
        }
        return ref;
    }

    private static Object doDigestHandle(ClassEggg cw, AnnotatedEggg s, Object ref) {
        if (s instanceof FieldEggg) {
            return new FieldSpec((FieldEggg) s);
        } else if (s instanceof ParamEggg) {
            return new ParamSpec((ParamEggg) s);
        }
        return ref;
    }

    public static TypeEggg getTypeEggg(Type type) {
        return eggg.getTypeEggg(type);
    }

    public static ClassEggg getClassEggg(Type type) {
        return getTypeEggg(type).getClassEggg();
    }
}

6、Creator 机制

除了 Digest 和 Alias,EggG 还支持 Creator(创造器)机制,用于指定哪个构造器或静态工厂方法负责创建对象。

注解方式

通过 withCreatorClass 注册一个注解类,被该注解标注的构造器或静态方法将被视为创造器:

Eggg eggg = new Eggg()
    .withCreatorClass(ONodeCreator.class);

自定义匹配方式

通过 withCreatorMatcher 注册自定义的匹配规则:

Eggg eggg = new Eggg()
    .withCreatorMatcher((eggg, executable) -> {
        return executable.isAnnotationPresent(MyCreator.class);
    });

获取创造器

ClassEggg cw = eggg.getTypeEggg(MyClass.class).getClassEggg();

ConstrEggg creator = cw.getCreator();

MyClass obj = creator.newInstance();

7、Handler 执行时机

从调用 getTypeEggg 开始,Handler 在元数据创建时自动执行:

Eggg.getTypeEggg(type)
  └→ 创建 TypeEggg
     └→ 获取 ClassEggg 时触发
        ├→ 创建 FieldEggg → 自动触发 DigestHandler → 自动触发 AliasHandler
        ├→ 创建 MethodEggg → 自动触发 DigestHandler
        ├→ 创建 ConstrEggg → 自动触发 DigestHandler
        └→ 创建 ParamEggg → 自动触发 DigestHandler → 自动触发 AliasHandler

Handler 结果缓存在对应的元数据对象中,后续通过 getDigest()getAlias() 直接获取。

8、设计特点

特点说明
统一入口所有注解解析逻辑集中在 Handler 中
一次注册不需要在每个使用点重复写解析代码
可组合Digest + Alias + Creator 三个维度独立配置
可扩展通过 ReflectHandler 可自定义反射策略
缓存友好Handler 结果随元数据一起缓存,不重复计算