`Advice.WithCustomMapping` 绑定一个动态添加的字段

`Advice.WithCustomMapping` to bind a dynamically added field

使用 byte-buddy,我想我需要使用 Advice.WithCustomMapping 来引用我根据某些运行时逻辑选择的任意字段。

但是,我有点不知道如何创建对作为检测的一部分添加的字段的引用?

AgentBuilder ab = new AgentBuilder.Default();
...
ab = ab.type(named(type))
       .transform((builder, td, _cl, _m) ->
          builder
             .defineField("$lat$", LatencyEvent.class, Visibility.PUBLIC) // new field; I want to create multiple
             .visit(Advice.withCustomMapping()
             .bind(Lat.class, td.getDeclaredFields().filter(named("$lat$")).getOnly()) // ok, here I want to reference the newly added field
             .to(EndLatAdvice.class)
             .on(isMethod().and(named(until)))) // pick a target method
...
private static class EndLatAdvice {
      @Advice.OnMethodEnter
      static void enter(@Lat LatencyEvent l) { // hoping to have access to the newly added field
...
}

此外,当我们这样做时,我注意到对于 Advice.FieldValue 我们需要指定 readOnly = false,如果我们想写回那个字段。 WithCustomMapping 是如何完成的(假设这是正确的工具)。

好的,我想我想出了两个办法。很高兴听到哪一个可以被认为是“规范的”。

  1. 创建另一个 AgentBuilder 只创建字段:
AgentBuilder addField = new AgentBuilder.Default();
AgentBuilder ab = new AgentBuilder.Default();
...
addField = addField.type(named(type))
       .transform((builder, td, _cl, _m) ->
          builder
             .defineField("$lat$", LatencyEvent.class, Visibility.PUBLIC))
ab = ab.type(named(type))
       .transform((builder, td, _cl, _m) ->
          builder
             .visit(Advice.withCustomMapping()
             .bind(Lat.class, td.getDeclaredFields().filter(named("$lat$")).getOnly()) // ok, this one now finds the field
             .to(EndLatAdvice.class)
             .on(isMethod().and(named(until)))) // pick a target method
...
     addField.with(AgentBuilder.TypeStrategy.Default.REDEFINE)
        .ignore(none())
        .installOn(inst);

      ab.with(AgentBuilder.TypeStrategy.Default.REDEFINE)
        .ignore(none())
        .installOn(inst);

现在有两个agent,一个是三个简单的创建字段,第二个重新定义相同的类,等到运行,就可以看到字段了由 addField 创建。不知道执行顺序有没有保证

  1. 创建 Latent 字段引用而不是按名称查找。
TypeDescription.Generic latField = new TypeDescription.Generic.OfNonGenericType.ForLoadedType(LatencyEvent.class);
...
ab = ab.type(named(type))
       .transform((builder, td, _cl, _m) ->
          builder
             .defineField("$lat$", LatencyEvent.class, Visibility.PUBLIC) // new field; I want to create multiple
             .visit(Advice.withCustomMapping()
             .bind(Lat.class, new FieldDescription.Latent(td, "$lat$", 1, latField, List.of())) // ok, we can reference the field by name
             .to(EndLatAdvice.class)
             .on(isMethod().and(named(until)))) // pick a target method

这个只有一个特工。

但这是一个只读引用 - 无法写回该字段。

为了写作,需要参考略有不同:

  1. 与上面的 (2) 相同,除了 bind 对于 Lat.class 注释应该是:
...
.bind(Lat.class, new Advice.OffsetMapping.ForField.Unresolved.WithImplicitType(latField, false, Assigner.Typing.STATIC, "$lat$"))
...

所以,仍然是 bind,只需要 ForField...WithImplicitType 而不是 Latent 参考。

您将提供一个 Advice.OffsetMapping 的实例,其中您具有检测类型,其中所有字段都作为参数值提供。

根据该映射,您将 return 此字段的 Advice.OffsetMapping.Target.ForField.ReadWrite 实例,除非您要应用一些自定义内容。