有什么方法可以在 ByteBuddy 代理之前对加载的 class 方法调用 advice() 吗?
Is there any way to call advice() on a loaded class's methods before the ByteBuddy agent?
我已经实现了一个简单的代理,如下所示。它适用于我的自定义 Foo.class
,但我无法将 advice
分配给 java.net.URL
class。
示例测试代码;
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 class 方法,以便 class 在 ByteBuddy
代理之前加载?
默认情况下,Byte Buddy 会忽略引导和 extension/platform 加载程序。您需要设置自定义忽略匹配器以检测 URL.
请注意,如果您检测 Byte Buddy 自身需要的 classes,尤其是当 Byte Buddy 在检测另一个 class 期间自行加载这些 classes 时,这可能会造成麻烦。
Rafael 和往常一样是对的。不过,我想更详细地说明你的问题:你在这里遇到 bootstrapping 问题:
- advice需要在bootstrapclass路径,否则不能和bootstrapclass
URL
.[=26一起使用=]
- 由于注释,建议需要 ByteBuddy。所以 BB 也必须在 bootstrap class 路径上。
- 如果您将示例代码放入单个 class 并从那里使用 BB 代理库,则该库也需要位于 bootstrap class 路径中。 IE。如果你想运行这个代码
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
作为替代方案,您可以使用 spring-board Java 代理动态地将 BB 和您的转换器放到 bootstrap class 路径上,而不直接引用他们的任何 classes。您随后可以通过反射开始转换。
我已经实现了一个简单的代理,如下所示。它适用于我的自定义 Foo.class
,但我无法将 advice
分配给 java.net.URL
class。
示例测试代码;
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 class 方法,以便 class 在 ByteBuddy
代理之前加载?
默认情况下,Byte Buddy 会忽略引导和 extension/platform 加载程序。您需要设置自定义忽略匹配器以检测 URL.
请注意,如果您检测 Byte Buddy 自身需要的 classes,尤其是当 Byte Buddy 在检测另一个 class 期间自行加载这些 classes 时,这可能会造成麻烦。
Rafael 和往常一样是对的。不过,我想更详细地说明你的问题:你在这里遇到 bootstrapping 问题:
- advice需要在bootstrapclass路径,否则不能和bootstrapclass
URL
.[=26一起使用=] - 由于注释,建议需要 ByteBuddy。所以 BB 也必须在 bootstrap class 路径上。
- 如果您将示例代码放入单个 class 并从那里使用 BB 代理库,则该库也需要位于 bootstrap class 路径中。 IE。如果你想运行这个代码
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
作为替代方案,您可以使用 spring-board Java 代理动态地将 BB 和您的转换器放到 bootstrap class 路径上,而不直接引用他们的任何 classes。您随后可以通过反射开始转换。