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

Hiç yorum yok:

Yorum Gönder