问题描述
使用 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
是如何完成的(假设这是用于此目的的正确工具)。
解决方法
您将提供一个 Advice.OffsetMapping
实例,其中您拥有所有字段作为参数值提供的检测类型。
从该映射中,您将为此字段返回一个 Advice.OffsetMapping.Target.ForField.ReadWrite
实例,除非您有自定义应用。
好的,我想我想出了两种方法。很高兴听到哪一个被认为是“规范的”。
- 创建另一个仅创建字段的
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,_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);
现在有两个代理,一个是三个简单的创建字段,第二个是重新定义相同的类,运行的时候可以看到addField
创建的字段。不知道执行顺序有没有保证。
- 创建对字段的
Latent
引用,而不是按名称查找。
TypeDescription.Generic latField = new TypeDescription.Generic.OfNonGenericType.ForLoadedType(LatencyEvent.class);
...
ab = ab.type(named(type))
.transform((builder,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
这个只适用于一个代理。
但这是只读引用 - 不能写回字段。
为了写,需要参考略有不同:
- 与上面的 (2) 相同,除了
bind
的Lat.class
注释应该是:
...
.bind(Lat.class,new Advice.OffsetMapping.ForField.Unresolved.WithImplicitType(latField,false,Assigner.Typing.STATIC,"$lat$"))
...
所以,仍然是 bind
,只需要一个 ForField...WithImplicitType
而不是 Latent
引用。