java—有没有办法在bytebuddy代理之前对加载的类的方法调用advice()?

hl0ma9xz  于 2021-07-06  发布在  Java
关注(0)|答案(2)|浏览(402)

我实现了一个简单的代理,如下所示。这是我的习惯 Foo.class ,但我无法分配 advice 对于 java.net.URL 班级。
测试代码示例;

public class AgentTest {

    @Test
    public void advice() throws IOException {
        Foo foo = new Foo();
        File temp = Files.createTempDirectory("tmp").toFile();
        Map<TypeDescription, byte[]> map = new HashMap<>();
        map.put(new TypeDescription.ForLoadedType(URL.class), ClassFileLocator.ForClassLoader.read(URL.class));
        ClassInjector.UsingInstrumentation.of(temp, ClassInjector.UsingInstrumentation.Target.BOOTSTRAP, ByteBuddyAgent.install()).inject(map);

        new AgentBuilder.Default()
                .disableClassFormatChanges()
                .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
                .with(AgentBuilder.TypeStrategy.Default.REBASE)
                .type(is(URL.class).or(is(Foo.class)))
                .transform(
                        new AgentBuilder.Transformer.ForAdvice()
                                .advice(
                                        isMethod().and(isPublic()).and(named("openConnection")).or(named("myMethod")),
                                        FooAdvice.class.getName()
                                )
                )
                .installOnByteBuddyAgent();

        foo.myMethod();
    }

    public static class FooAdvice {

        @Advice.OnMethodEnter
        public static void enter() {
            System.out.println("1- method entered !");
        }

        @Advice.OnMethodExit
        public static void exit() {
            System.out.println("2- method exited");
        }
    }
}

有没有什么具体的方法来约束 advice 的java.net.url类方法,以便在 ByteBuddy 探员?

kq0g1dla

kq0g1dla1#

拉斐尔是对的,和往常一样。不过,我想对您的问题进行更详细的阐述:您在这里遇到了一个自举问题:
建议需要在引导类路径上,否则不能与引导类一起使用 URL .
由于注解的原因,建议需要bytebuddy。所以bb也必须在引导类路径上。
如果您将示例代码放在一个类中,并且从那里使用bb代理库,那么这个类也需要位于引导类路径上。i、 如果你想运行这个代码

import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;

import static net.bytebuddy.matcher.ElementMatchers.*;

class AgentTest {
  public static void main(String[] args) throws IOException {
    ByteBuddyAgent.install();
    new AgentBuilder.Default()
      .disableClassFormatChanges()
      .ignore(none())
      .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
      .with(AgentBuilder.RedefinitionStrategy.Listener.StreamWriting.toSystemError())
      .with(AgentBuilder.Listener.StreamWriting.toSystemError().withTransformationsOnly())
      .with(AgentBuilder.InstallationListener.StreamWriting.toSystemError())
      .type(is(URL.class).or(is(Foo.class)))
      .transform(
        new AgentBuilder.Transformer.ForAdvice()
          .advice(
            isMethod().and(isPublic()).and(named("openConnection")).or(named("myMethod")),
            FooAdvice.class.getName()
          )
      )
      .installOnByteBuddyAgent();

    new Foo().myMethod();
  }

  public static class Foo {
    public void myMethod() throws IOException {
      new URL("https://google.de").openConnection();
    }
  }

  public static class FooAdvice {
    @Advice.OnMethodEnter
    public static void enter(@Advice.Origin Method method) {
      System.out.println("Entering " + method);
    }

    @Advice.OnMethodExit
    public static void exit(@Advice.Origin Method method) {
      System.out.println("Exiting " + method);
    }
  }

}

您需要在java命令行中添加如下内容:

java -Xbootclasspath/a:/path/to/my-classes;/path/to/byte-buddy-1.10.13.jar;/path/to/byte-buddy-agent-1.10.13.jar ... AgentTest

然后得到这个输出:

[Byte Buddy] BEFORE_INSTALL net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer@27bc2616 on sun.instrument.InstrumentationImpl@3941a79c
[Byte Buddy] REDEFINE BATCH #0 [2 of 2 type(s)]
[Byte Buddy] TRANSFORM AgentTest$Foo [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.net.URL [null, null, loaded=true]
[Byte Buddy] REDEFINE COMPLETE 1 batch(es) containing 2 types [0 failed batch(es)]
[Byte Buddy] INSTALL net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer@27bc2616 on sun.instrument.InstrumentationImpl@3941a79c
Entering public void AgentTest$Foo.myMethod() throws java.io.IOException
Entering public java.net.URLConnection java.net.URL.openConnection() throws java.io.IOException
Entering public java.net.URLConnection java.net.URL.openConnection() throws java.io.IOException
Exiting public java.net.URLConnection java.net.URL.openConnection() throws java.io.IOException
Entering public java.net.URLConnection java.net.URL.openConnection() throws java.io.IOException
Entering public java.net.URLConnection java.net.URL.openConnection() throws java.io.IOException
Exiting public java.net.URLConnection java.net.URL.openConnection() throws java.io.IOException
Exiting public java.net.URLConnection java.net.URL.openConnection() throws java.io.IOException
Exiting public java.net.URLConnection java.net.URL.openConnection() throws java.io.IOException
Exiting public void AgentTest$Foo.myMethod() throws java.io.IOException

另一种方法是,您可以使用springboardjava代理,它可以动态地将bb和您的转换器放到引导类路径上,而不直接引用它们的任何类。随后可以通过反射启动转换。

j13ufse2

j13ufse22#

默认情况下,byte-buddy忽略引导和扩展/平台加载程序。您需要将一个自定义的忽略匹配器设置为instrumenturl。
注意,如果您插入byte-buddy自己需要的类,尤其是byte-buddy在另一个类的插入过程中自己加载这些类时,这可能会导致麻烦。

相关问题