如何使用 byte-buddy 在运行时覆盖对象的值
How to override values of object in runtime using byte-buddy
这是我的对象class:
package com.example;
public class Car {
private String name;
private String model;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
这是操作class:
package com.example;
public class Call1 {
public static String callMethod1(){
return "Hello from callMethod1";
}
public static Car callingCall2(){
Call2 call = new Call2();
Car args= call.callMethod2();
return args;
}
}
这是另一个class:
package com.example;
public class Call2 {
public Car callMethod2(){
Car car = new Car();
car.setModel("2009");
car.setName("mustang");
return car;
}
}
这是我的测试class:
package com.example;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.OnMethodEnter;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.DynamicType.Builder;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;
import org.junit.Test;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import static net.bytebuddy.matcher.ElementMatchers.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
public class CallingTest {
@Test
public void givenCall1_whenRedefined_thenReturnFooRedefined() throws
Exception {
premain(null, ByteBuddyAgent.install());
/* Car carTest = new Car();
carTest.setModel("2011");
carTest.setName("Maruti");
ByteBuddyAgent.install();
new ByteBuddy()
.redefine(Call1.class)
.method(named("callingCall2"))
.intercept(FixedValue.value(carTest))
.make()
.load(Car.class.getClassLoader(),
ClassReloadingStrategy.fromInstalledAgent());*/
Call1 f = new Call1();
assertEquals(f.callingCall2().getModel(), "2011");
assertEquals(f.callingCall2().getName(), "Maruti");
}
public static void premain(String arguments, Instrumentation instrumentation) {
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(RedefinitionStrategy.RETRANSFORMATION)
.type(is(Call1.class))
.transform(new AgentBuilder.Transformer() {
/* @Override
public DynamicType.Builder transform(DynamicType.Builder builder,
TypeDescription typeDescription,
ClassLoader classloader) {
return builder.method(named("toString"))
.intercept(FixedValue.value("transformed"));
}*/
@Override
public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule arg3) {
Car carTest = new Car();
carTest.setModel("2011");
carTest.setName("Maruti");
return builder.method(named("callingCall2")).intercept(MethodDelegation.to(MyAdvice.class));
// return builder.visit((AsmVisitorWrapper) Advice.to(Advice.class).on(ElementMatchers.named("callingCall2")));
}
}).installOn(instrumentation);
}
class MyAdvice {
@OnMethodEnter
Car foo() {
Car carTest = new Car();
carTest.setModel("2011");
carTest.setName("Maruti");
return carTest;
}
}
}
要求在此场景中覆盖对象 Car 的值,以便测试 class 应该通过。我尝试使用 byte buddy 和 AgentBuilder 来这样做。当我尝试使用重新定义时,它抛出了错误
'java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)'
当我尝试使用 AgentBuilder 时,它不会影响导致失败的断言。
我刚接触 byte-buddy,所以请帮助实现要求。
FixedValue 只能与字符串、Class 对象、基本类型及其包装器一起使用。为了涵盖您的情况,您可以引入这样的拦截器:
@Test
public void givenCall1_whenRedefined_thenReturnFooRedefined() throws Exception {
ByteBuddyAgent.install();
new ByteBuddy()
.redefine(Call1.class)
.method(named("callingCall2"))
.intercept(to(Interceptor.class))
.make()
.load(ByteBuddyTest2.class.getClassLoader(),
ClassReloadingStrategy.fromInstalledAgent());
Call1 f = new Call1();
assertEquals(f.callingCall2().getModel(), "2011");
assertEquals(f.callingCall2().getName(), "Maruti");
}
public static class Interceptor {
public static Car callingCall2() {
Car carTest = new Car();
carTest.setModel("2011");
carTest.setName("Maruti");
return carTest;
}
}
这是我的对象class:
package com.example;
public class Car {
private String name;
private String model;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
这是操作class:
package com.example;
public class Call1 {
public static String callMethod1(){
return "Hello from callMethod1";
}
public static Car callingCall2(){
Call2 call = new Call2();
Car args= call.callMethod2();
return args;
}
}
这是另一个class:
package com.example;
public class Call2 {
public Car callMethod2(){
Car car = new Car();
car.setModel("2009");
car.setName("mustang");
return car;
}
}
这是我的测试class:
package com.example;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.OnMethodEnter;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.DynamicType.Builder;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;
import org.junit.Test;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import static net.bytebuddy.matcher.ElementMatchers.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
public class CallingTest {
@Test
public void givenCall1_whenRedefined_thenReturnFooRedefined() throws
Exception {
premain(null, ByteBuddyAgent.install());
/* Car carTest = new Car();
carTest.setModel("2011");
carTest.setName("Maruti");
ByteBuddyAgent.install();
new ByteBuddy()
.redefine(Call1.class)
.method(named("callingCall2"))
.intercept(FixedValue.value(carTest))
.make()
.load(Car.class.getClassLoader(),
ClassReloadingStrategy.fromInstalledAgent());*/
Call1 f = new Call1();
assertEquals(f.callingCall2().getModel(), "2011");
assertEquals(f.callingCall2().getName(), "Maruti");
}
public static void premain(String arguments, Instrumentation instrumentation) {
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(RedefinitionStrategy.RETRANSFORMATION)
.type(is(Call1.class))
.transform(new AgentBuilder.Transformer() {
/* @Override
public DynamicType.Builder transform(DynamicType.Builder builder,
TypeDescription typeDescription,
ClassLoader classloader) {
return builder.method(named("toString"))
.intercept(FixedValue.value("transformed"));
}*/
@Override
public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule arg3) {
Car carTest = new Car();
carTest.setModel("2011");
carTest.setName("Maruti");
return builder.method(named("callingCall2")).intercept(MethodDelegation.to(MyAdvice.class));
// return builder.visit((AsmVisitorWrapper) Advice.to(Advice.class).on(ElementMatchers.named("callingCall2")));
}
}).installOn(instrumentation);
}
class MyAdvice {
@OnMethodEnter
Car foo() {
Car carTest = new Car();
carTest.setModel("2011");
carTest.setName("Maruti");
return carTest;
}
}
}
要求在此场景中覆盖对象 Car 的值,以便测试 class 应该通过。我尝试使用 byte buddy 和 AgentBuilder 来这样做。当我尝试使用重新定义时,它抛出了错误 'java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)' 当我尝试使用 AgentBuilder 时,它不会影响导致失败的断言。 我刚接触 byte-buddy,所以请帮助实现要求。
FixedValue 只能与字符串、Class 对象、基本类型及其包装器一起使用。为了涵盖您的情况,您可以引入这样的拦截器:
@Test
public void givenCall1_whenRedefined_thenReturnFooRedefined() throws Exception {
ByteBuddyAgent.install();
new ByteBuddy()
.redefine(Call1.class)
.method(named("callingCall2"))
.intercept(to(Interceptor.class))
.make()
.load(ByteBuddyTest2.class.getClassLoader(),
ClassReloadingStrategy.fromInstalledAgent());
Call1 f = new Call1();
assertEquals(f.callingCall2().getModel(), "2011");
assertEquals(f.callingCall2().getName(), "Maruti");
}
public static class Interceptor {
public static Car callingCall2() {
Car carTest = new Car();
carTest.setModel("2011");
carTest.setName("Maruti");
return carTest;
}
}