You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用ByteBuddy在字节码中为静态字段赋值?

问题:ByteBuddy Plugin生成类时静态常量赋值失败

需要生成包含如下静态常量定义的类:

public static final Value foo = new Value("bar");

其中Value是自定义内部类。

现有插件代码

public class ExamplePlugin implements Plugin.WithInitialization {
    public static class Value {
        private final String foo;

        public Value(String foo) {
            this.foo = foo;
        }
    }
    public Map<TypeDescription, byte[]> initialize(ClassFileLocator classFileLocator) {
        try {
            DynamicType.Builder<Object> type = new ByteBuddy()
                    .subclass(Object.class)
                    .name("com.example.ExampleGenerated")
                    .defineField("foo", TypeDescription.ForLoadedType.of(Value.class),
                            Visibility.PUBLIC, Ownership.STATIC, FieldManifestation.FINAL)
                    .invokable(MethodDescription::isTypeInitializer)
                    .intercept(FixedValue.reference(new Value("foo")));

            try (DynamicType.Unloaded<Object> unloaded = type.make()) {
                return unloaded.getAllTypes();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassFileLocator classFileLocator) {
        return builder;
    }
    public void close() {}
    public boolean matches(TypeDescription target) {
        return false;
    }
}

生成类的反编译结果

public class ExampleGenerated {
    public static final ExamplePlugin.Value foo;

    public ExampleGenerated() {
    }

    static {
        ExamplePlugin.Value var10000 = value$25iplg0;
    }
}

可见赋值操作并未作用于foo字段,而是指向了临时变量,导致foo未被正确初始化。

尝试过的无效方案

方案1:拦截方法调用

.intercept(MethodCall.invoke(Callable.class.getMethod("call")).on(() -> new Value("bar"), Callable.class)
    .setsField(ElementMatchers.named("foo"))
    .withAssigner(Assigner.DEFAULT, Assigner.Typing.DYNAMIC));

该方案生成的静态块会尝试给foo赋值,但lambda对应的Callable实例未被写入字节码,运行时抛出NPE,反编译结果:

static {
        foo = (ExamplePlugin.Value)invocationTarget$j3n12b1.call();
    }

方案2:使用初始化器

.initializer(new LoadedTypeInitializer.ForStaticField("foo", new Value("bar")))

该方案的初始化逻辑是类加载时动态执行的,不会被持久化到字节码中,打包成Jar后无效。

正确实现方式

问题根源在于:

  • FixedValue.reference是让静态初始化器返回实例,但<clinit>是void方法,返回值会被忽略,且未执行字段赋值操作;
  • 拦截方法调用用的lambda无法被序列化到字节码;
  • LoadedTypeInitializer是动态加载逻辑,不写入字节码。

正确的做法是在静态初始化器中直接生成构造Value实例并赋值给foo字段的字节码,修改后的initialize方法如下:

public Map<TypeDescription, byte[]> initialize(ClassFileLocator classFileLocator) {
    try {
        DynamicType.Builder<Object> type = new ByteBuddy()
                .subclass(Object.class)
                .name("com.example.ExampleGenerated")
                .defineField("foo", TypeDescription.ForLoadedType.of(Value.class),
                        Visibility.PUBLIC, Ownership.STATIC, FieldManifestation.FINAL)
                .invokable(MethodDescription::isTypeInitializer)
                .intercept(
                        // 构造Value实例并赋值给foo字段
                        MethodCall.construct(Value.class.getConstructor(String.class))
                                .with("bar")
                                .setsField(ElementMatchers.named("foo"))
                );

        try (DynamicType.Unloaded<Object> unloaded = type.make()) {
            return unloaded.getAllTypes();
        }
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

生成的类反编译后会得到预期的结果:

public class ExampleGenerated {
    public static final ExamplePlugin.Value foo;

    static {
        foo = new ExamplePlugin.Value("bar");
    }

    public ExampleGenerated() {
    }
}

内容的提问来源于stack exchange,提问作者tytk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 05:42:43