首页 > 解决方案 > byte-buddy 可以用作非反射方式来收集 [field]/[method return] 值作为集合或映射吗?

问题描述

我试图找到可用的答案,但没有运气。我想知道是否可以使用 byte-buddy 进行以下操作:假设我们有一个带有多个值的 POJO。对于某些特定类型的处理,我只对一些感兴趣。我可以用注释标记它们,比如我可以放在 getter 或字段上的 @ConditionalData。然后我可以创建和接口,比如说 NVPProvider,它将返回地图字段名称 - 值。可以使用反射来执行此操作,但性能不太好。我希望我可以使用字节伙伴通过接口和实现方法扩展类,但我并没有真正找到如何构造实现来实现这一点。

我经历了以下实现:

net.bytebuddy.implementation.Implementation

并尝试在网页周围搜索一些示例,但我还没有认识到正确的方法。

public interface NVPProvider {
    Map<String, Object> getDataAsNVP();
}

public <O> Builder<O> instrumentType(Builder<O> builder) {
    builder.implement(NVPProvider.class).method(net.bytebuddy.matcher.ElementMatchers.named("getDataAsNVP")).intercept( ??? );        
    return builder.implement(NVPProvider.class);
}

我想知道是否有一种方法可以通过反射式获取注释来迭代字段和获取器并匹配可访问对象,但是基于此我将能够编写接口方法实现迭代匹配的字段并为结果映射做出贡献,虚构的生成代码如下所示:

Map<String, Object> result = new HashMap<>();
Object obj0 = getValue001();
result.put("getValue001", obj0);

Object ob10 = accesibleField1;
result.put("ob10 ", ob10 );

后来我可以添加注释属性以获得更好看的键。

我见过的例子

MethodDelegation.to(interceptor)

但是,我现在确实知道如何在没有反思的情况下做到这一点。

我知道如何使用 Javassist 来实现它,您实际上可以在其中编写稍后编译的代码片段,但我不确定如何使用 byte-buddy 来实现。我已经使用 byte-budy 通过在配置文件中动态定义的简单 getter 来扩展 POJO,它看起来不错。只有一个字节操作工具会更干净。感谢您的任何建议。

标签: javabyte-buddy

解决方案


这在 Byte Buddy 中是可能的,但您必须通过实现您自己的Implementation或其底层的ByteCodeAppender. 您可以直接使用 ASM,因为这样访问者会MethodVisitor更容易地公开他们的信息,您还可以使用net.bytebuddy.implementation.bytecode包中提供的更高级别的构造。对于您描述的方法,您将首先实例化哈希映射:

List<StackManipulation> m = new ArrayList<>();

// new HashMap<>();
m.add(TypeCreation.of(HashMap.class));
m.add(Duplication.SINGLE);
m.add(MethodInvocation.invoke(new MethodDescription.ForLoadedConstructor(HashMap.class.getConstructor());

随后,aByteCodeAppender提供了检测类型的描述,您可以从中导航到所有字段和方法,例如:

FieldDescription field = ...

// map.put(fieldName, fieldValue);
m.add(Duplication.SINGLE);
m.add(new TextConstant(field.getName());
m.add(MethodVariableAccess.loadThis());
m.add(FieldAccess.forField(field).read());
m.add(MethodInvocation.invoke(Map.class.getMethod("put", Object.class, Object.class)));
m.add(Removal.SINGLE);

上面的代码首先在堆栈上复制哈希映射以保留它以供进一步访问,然后将字段名称添加到堆栈中,加载字段值,然后调用该Map::put方法。最后,它删除了 put 方法的返回值。

最后,您需要通过添加返回地图MethodReturn.REFERENCE

您可以查看ImplementationByte Buddy 中的其他 s 以详细了解其工作原理,并且 ASM 提供了一个很好的字节码基础教程来解释字节码模式需要遵循的堆栈隐喻。


推荐阅读