无法为类的现有字段添加赋值函数



我正在尝试为现有的private final字段添加一个赋值函数。我可以转换字段修饰符以删除final规范并添加访问器方法:

// accessor interface
public interface UniqueIdAccessor {
Serializable getUniqueId();
}
// mutator interface
public interface UniqueIdMutator {
void setUniqueId(Serializable uniqueId);
}

...
// fragment of Java agent implementation
return new AgentBuilder.Default()
.type(hasSuperType(named("org.junit.runner.Description")))
.transform(new Transformer() {
@Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule module) {
return builder.field(named("fUniqueId")).transform(ForField.withModifiers(FieldManifestation.PLAIN))
.implement(UniqueIdAccessor.class).intercept(FieldAccessor.ofField("fUniqueId"))
//            .implement(UniqueIdMutator.class).intercept(FieldAccessor.ofField("fUniqueId"))
.implement(Hooked.class);
}
})
.installOn(instrumentation);
...

这里有一个方法,它使用反射来检查目标字段的修饰符,并调用访问器来获取字段的值。

private static void injectProxy(Description description) {
try {
Field bar = Description.class.getDeclaredField("fUniqueId");
System.out.println("isFinal: " + ((bar.getModifiers() & Modifier.FINAL) != 0));
} catch (NoSuchFieldException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Serializable uniqueId = ((UniqueIdAccessor) description).getUniqueId();
System.out.println("uniqueId: " + uniqueId);
}
// isFinal: false
// uniqueId: <description-unique-id>

但是如果我取消注释第二个";实现";表达式添加突变子,转换会爆炸:

// isFinal: true
// java.lang.ClassCastException: 
//     class org.junit.runner.Description cannot be cast to class com.nordstrom.automation.junit.UniqueIdAccessor
//     (org.junit.runner.Description and com.nordstrom.automation.junit.UniqueIdAccessor
//     are in unnamed module of loader 'app')

我可以通过反射来设置字段值,但这首先违背了使用Byte Buddy的目的!

这种方法的问题是字段访问器在修改之前会考虑输入类型。Byte Buddy禁止这样做,因为它不认为突变是合法的,也不知道删除的修饰语。结果,转换整体失败,您会看到错误。(注册侦听器以查看此错误。(

为了避免这种情况,可以使用FieldAccess(不带(实现自定义Implementation。您可以查看更方便的FieldAccessor,看看它是如何实现的,只需要取消有效性检查。

感谢您为我指明了正确的方向!我用以下内容组装了定义mutator方法的StackManipulation对象:

final TypeDescription description = TypePool.Default.ofSystemLoader().describe("org.junit.runner.Description").resolve();

final Generic _void_ = TypeDescription.VOID.asGenericType();
final Generic serializable = TypePool.Default.ofSystemLoader().describe("java.io.Serializable").resolve().asGenericType();

final MethodDescription.Token setUniqueIdToken = new MethodDescription.Token("setUniqueId", Modifier.PUBLIC, _void_, Arrays.asList(serializable));
final MethodDescription setUniqueId = new MethodDescription.Latent(description, setUniqueIdToken);

final Token fUniqueIdToken = new FieldDescription.Token("fUniqueId", Modifier.PRIVATE, serializable);
final FieldDescription fUniqueId = new FieldDescription.Latent(description, fUniqueIdToken);

final StackManipulation setUniqueIdImpl = new StackManipulation.Compound(
MethodVariableAccess.loadThis(),
MethodVariableAccess.load(setUniqueId.getParameters().get(0)),
Assigner.DEFAULT.assign(serializable, serializable, Typing.STATIC),
FieldAccess.forField(fUniqueId).write(),
MethodReturn.VOID
);

我用这个转换目标类:

return new AgentBuilder.Default()
.type(hasSuperType(named("org.junit.runner.Description")))
.transform(new Transformer() {
@Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule module) {
return builder.field(named("fUniqueId")).transform(ForField.withModifiers(FieldManifestation.PLAIN))
.implement(AnnotationsAccessor.class).intercept(FieldAccessor.ofField("fAnnotations"))
.implement(UniqueIdAccessor.class).intercept(FieldAccessor.ofField("fUniqueId"))
.implement(UniqueIdMutator.class).intercept(new Implementation.Simple(setUniqueIdImpl));
}
})
.installOn(instrumentation);

以下是转换中使用的三个接口的定义:

// annotations accessor interface
public interface AnnotationsAccessor {
Annotation[] annotations();
}
// unique ID accessor interface
public interface UniqueIdAccessor {
Serializable getUniqueId();
}
// unique ID mutator interface
public interface UniqueIdMutator {
void setUniqueId(Serializable uniqueId);
}

最新更新