如何使用 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;
    }
}