可以将字节码作为一种非反射方式来收集[field] / [method return]值作为集合或映射吗?

时间:2019-04-15 11:59:01

标签: java byte-buddy

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

我已经完成了以下实现:

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);
}

我想知道是否存在一种方法,可以通过反射性地获取批注来迭代字段和获取器并匹配Accessible对象,但是基于此,我将能够构成接口方法实现,以迭代匹配的字段并为结果图,假想的生成代码如下:

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

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

稍后我可以添加注解属性,以使键看起来更好。

我看过

的示例
MethodDelegation.to(interceptor)

但是,现在我要看看如何做到无反射。

我知道如何使用Javassist做到这一点,您实际上可以在其中编写稍后再编译的代码,但是我不确定如何用字节伙伴来做到这一点。我已经使用字节预算通过配置文件中动态定义的简单getter扩展了POJO,它看起来不错。仅使用一个字节操作工具会更清洁。 感谢您的任何建议。

1 个答案:

答案 0 :(得分:0)

这在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());

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

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来返回地图。

您可以查看Byte Buddy中的其他Implementation来详细了解它的工作原理,ASM为字节码基础知识提供了很好的教程,以解释字节码模式需要遵循的堆栈隐喻。