无法为 class 的现有字段添加修改器
Unable to add mutator for an existing field of a class
我正在尝试为现有的 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
看看这是如何实现的,只是你需要放弃有效性检查。
感谢您为我指明正确的方向!我 assemble StackManipulation
对象用这个定义了 mutator 方法:
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
);
...然后我将目标 class 转换为:
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);
}
我正在尝试为现有的 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
看看这是如何实现的,只是你需要放弃有效性检查。
感谢您为我指明正确的方向!我 assemble StackManipulation
对象用这个定义了 mutator 方法:
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
);
...然后我将目标 class 转换为:
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);
}