Byte Buddy etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
Byte Buddy etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

25 Eylül 2023 Pazartesi

Byte Buddy AgentBuilder Sınıfı

Giriş
Gnellikle şu şekilde kullanılır
AgentBuilder.Default()
.type(...)
.transform(...)
.installOn(...)
Çalıştırılacak kod pre-main olarak düşünülür. 

Örnek
Şöyle yaparız. Burada @Repeat olarak işaretli metod 3 kere çağrılıyor
public class Repeater {

  public static void premain(String arguments, Instrumentation instrumentation) {      //1
    var withRepeatAnnotation = 
      isAnnotatedWith(named("ch.frankel.blog.instrumentation.Repeat")); 	       //2
    new AgentBuilder.Default()                                                         //3
      .type(declaresMethod(withRepeatAnnotation))                                      //4
      .transform((builder, typeDescription, classLoader, module, domain) -> builder    //5
        .method(withRepeatAnnotation)                                                  //6
        .intercept(                                                                    //7
           SuperMethodCall.INSTANCE                                                    //8
            .andThen(SuperMethodCall.INSTANCE)
            .andThen(SuperMethodCall.INSTANCE))
      ).installOn(instrumentation);                                                    //3
  }
}
Açıklaması şöyle
1. Required signature; it’s similar to the main method, with the added Instrumentation argument
2. Match that is annotated with the @Repeat annotation. The DSL reads fluently even if you don't know it (I don't).
3. Byte Buddy provides a builder to create the Java agent
4. Match all types that declare a method with the @Repeat annotation
5. Transform the class accordingly
6. Transform methods annotated with @Repeat
7. Replace the original implementation with the following
8. Call the original implementation three times
Sonra şöyle yaparız. Burada bir fat jar oluşturuluyor
<plugin>
  <artifactId>maven-assembly-plugin</artifactId>                       
  <configuration>
    <descriptorRefs>
      <descriptorRef>jar-with-dependencies</descriptorRef>  
    </descriptorRefs>
    <archive>
      <manifestEntries>
        <Premain-Class>ch.frankel.blog.instrumentation.Repeater</Premain-Class>
      </manifestEntries>
    </archive>
  </configuration>
  <executions>
    <execution>
        <goals>
          <goal>single</goal>
        </goals>
      <phase>package</phase>
    </execution>
  </executions>
</plugin>
ve şöyle yaparız
// create the agent
mvn install

// Run the app with the agent:
java 
  -javaagent:/~/.m2/repository/.../agent-1.0-SNAPSHOT-jar-with-dependencies.jar \ #1
  -cp ./target/classes                                                            #2
  ch.frankel.blog.instrumentation.Main                                          

Örnek
Elimizde şöyle bir kod olsun. Bu instrumentation için kullanılacak kod. Bu koddaki premain metodu jvm'den önce çalışır. TimeFooter sınıfı Advice içindir. transform() metoduyla Advice metodlara takılır
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;

import java.lang.instrument.Instrumentation;

public class SimpleInstru {
  public static void premain(String argument,Instrumentation instrumentation) {

    System.out.println("Agent started at " + System.currentTimeMillis());

    Advice advice = Advice.to(TimeFooter.class);
    new AgentBuilder.Default()
      .type(ElementMatchers.nameContains("ApplicationBeingTransformed"))
      .transform((DynamicType.Builder<?> builder,
                TypeDescription type,
                  ClassLoader loader,
                  JavaModule module) -> {
                    return builder.visit(advice.on(ElementMatchers.isMethod()));
      }).installOn(instrumentation);
    }

    public static class TimeFooter {

      @Advice.OnMethodExit(onThrowable = Throwable.class)
      public static void exit(@Advice.Origin String methodName) {
        System.out.println("Now exiting: \n"+methodName);
      }
  }
}
Bu kodu agent olarak kullanılabilecek bir jar haline getirelim
javac -cp byte-buddy-1.10.22.jar;. SimpleInstru.java
jar cfm simpleInstrumentation.jar instru.mf
SimpleInstru.class SimpleInstru\$TimeFooter.class
Agent olması için instru.mf dosyası şöyle olmalı
Premain-Class: SimpleInstru
Class-Path: byte-buddy-1.10.22.jar
Daha sonra kendi kodumuzu çalıştırmak için şöyle yaparız
java -javaagent:simpleInstrumentation.jar -jar simpleApplication.jar

7 Nisan 2021 Çarşamba

Byte Buddy - Java Compiler Kullanmadan Java Kodu Üretir

Giriş
Açıklaması şöyle
Byte Buddy is a lightweight and user-friendly bytecode manipulation library. It simplifies bytecode generation and modification by providing a high-level API. Byte Buddy allows for the creation of new classes, modification of existing classes, and dynamic creation of methods and fields.

Maven
Şu satırı dahil ederiz
<properties>
  <byte-buddy.version>1.12.11</byte-buddy.version>
</properties>

<dependencies>
  <dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy</artifactId>
    <version>${byte-buddy.version}</version>
  </dependency>

  <dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy-agent</artifactId>
    <version>${byte-buddy.version}</version>
  </dependency>
</dependencies>
ByteBuddy Sınıfı

redefine metodu
Belirtilen sınıfı değiştirebilmek mümkün

Örnek
Şöyle yaparız. Bu örnekte privateMethod() exception fırlatıyor. Onu başka bir metod ile değiştiriyoruz.
public class MainService {
  public void publicMethod() {
    privateMethod();
  }

  private void privateMethod() {
    throw new IllegalStateException();
  }
}

public class StubService {
  public static void privateMethod() {
    // empty
  }
}

public class MainWithOverridedMethod {
  public static void main(String[] args) throws Throwable {
    ByteBuddyAgent.install();

    Class<? extends MainService> cls = new ByteBuddy()
	  .redefine(MainService.class)
	  .method(named("privateMethod"))
	  .intercept(MethodDelegation.to(StubService.class))
	  .make()
	  .load(StubService.class.getClassLoader(), fromInstalledAgent())
	  .getLoaded();
    MainService mainService = cls.newInstance();
    mainService.publicMethod();
  }
}
subclass metodu
Belirtilen sınıftan kalıtan yeni bir sınıf döner
Örnek
Şöyle yaparız. Bu örnekte Object nesnesinden kalıtılıyor ve toString() metodunu başka bir metod ile değiştiriyoruz.
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.FixedValue;

public class ByteBuddyExample {
  public static void main(String[] args) throws Exception {
    Class<?> dynamicType = new ByteBuddy()
      .subclass(Object.class)
      .method(ElementMatchers.named("toString"))
      .intercept(FixedValue.value("Hello Byte Buddy!"))
      .make()
      .load(ByteBuddyExample.class.getClassLoader())
      .getLoaded();
        
      Object instance = dynamicType.getDeclaredConstructor().newInstance();
      System.out.println(instance.toString());
  }
}
1. Traditional Advice Dispatching Approaches
Açıklaması şöyle
By default, the enter and exit advice is copied into the target methods, as if the original author of the class had added the agent’s code into the method.
Advice kopyalamanın bazı iyi ve kötü tarafları var. Açıklaması şöyle
The advantage is that the advice has access to any value or type that is normally reachable from the instrumented method. In the above example, this allows accessing javax.servlet.http.HttpServletRequest, even though the agent does not itself ship with that interface. As the agent’s code is run within the targeted method, it simply picks up the type definition that is already available to the method itself.

On the downside, the advice code is no longer executed in the context that it is defined within. As a result, you can, for example, not set a breakpoint in an advice method, because it is never actually called. Remember: the methods are merely used as a template.
Örnek - inlined advice
Şöyle yaparız
@Advice.OnMethodEnter
public static long enter() {
  return System.nanoTime();
}

@Advice.OnMethodExit
public static void exit(@Advice.Argument(0) HttpServletRequest request,
                        @Advice.Enter long startTime) {
  System.out.printf("Request to %s took %d ns%n",
    request.getRequestURI(),
    System.nanoTime() - startTime);
}
Advice gerçek metodun içine kopyalandığı için sanki şöyle kodlanmış gibi olur
protected void service(HttpServletRequest req, HttpServletResponse resp) {
  long startTime = System.nanoTime();
  // original method body
  System.out.printf("Request to %s took %d ns%n",
    request.getRequestURI(),
    System.nanoTime() - startTime);
}
Örnek - inlined advice
Byte Buddy AgentBuilder Sınıfı yazısına taşıdım

Örnek - Delegated Advice
Açıklaması şöyle
... it is possible to instruct Byte Buddy to delegate to the advice methods instead. This can be controlled via the advice annotation attribute @Advice.OnMethodEnter(inline = false). By default, Byte Buddy will delegate to an advice method via a static method call.
Bu durumda kod şöyle olur
protected void service(HttpServletRequest req, HttpServletResponse resp) {
  long startTime = AdviceClass.enter();
  // original method body
  AdviceClass.exit(req, startTime);
}
2. invokedynamic-Based Advice Dispatching Approach
Bu yeni bir yaklaşım ve JVM'e eklenen invokedynamic sayesinde mümkün