我有一个项目,允许人们记录JVM(https://github.com/jdeppe-pivotal/classload-tracer)加载的类。这种限制是必须成功加载一个类才能被记录。
现在我想尝试扩展它并设置各种ClassLoader
方法,以便在加载时尝试记录类(无论成功与否)。我希望将ByteBuddy用于此。不幸的是我运气不好。
以下是代理的代码:
public class ScratchAgent {
private static final PrintWriter out;
public static final ByteArrayOutputStream baos;
static {
baos = new ByteArrayOutputStream(10);
out = new PrintWriter(baos);
}
public static void premain(String arg, Instrumentation inst) throws Exception {
File temp = Files.createTempDirectory("tmp").toFile();
ClassInjector.UsingInstrumentation
.of(temp, ClassInjector.UsingInstrumentation.Target.BOOTSTRAP, inst)
.inject(Collections.singletonMap(
new TypeDescription.ForLoadedType(MyInterceptor.class),
ClassFileLocator.ForClassLoader.read(MyInterceptor.class)));
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(AgentBuilder.Listener.StreamWriting.toSystemOut().withTransformationsOnly())
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(AgentBuilder.TypeStrategy.Default.REDEFINE)
.with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
.ignore(ElementMatchers.nameStartsWith("net.bytebuddy."))
.enableBootstrapInjection(inst, temp)
.type(is(TestClass.class))
.or(ElementMatchers.isSubTypeOf(ClassLoader.class)
.or(ElementMatchers.nameContainsIgnoreCase("classloader"))
)
.transform((builder, type, classLoader, module) -> builder
.visit(Advice.to(MyInterceptor.class)
.on(hasMethodName("fooMethod")))
.visit(Advice.to(MyInterceptor.class)
.on(hasMethodName("loadClass"))))
.installOn(inst);
}
public static class MyInterceptor {
@Advice.OnMethodEnter
public static void decorate(
@Advice.Argument(0) String arg,
@Advice.This Object thisThis,
@Advice.Origin Method method,
@Advice.Origin Class<?> clazz) {
System.out.println("--->>> OK " + method.getName()
+ "(" + arg + ") " + thisThis);
out.println("--->>> OK " + arg);
out.flush();
}
}
}
这是一个测试:
@Test
public void sanity() throws Exception {
ScratchAgent.premain(null, ByteBuddyAgent.install());
String result = new TestClass().fooMethod("world");
assertThat(result).isEqualTo("Hello world");
assertThat(ScratchAgent.baos.toString()).isEqualTo("--->>> OK world\n");
}
但是这会产生以下错误:
[Byte Buddy] TRANSFORM io.pivotal.test.TestClass [sun.misc.Launcher$AppClassLoader@18b4aac2, null, loaded=true]
[Byte Buddy] TRANSFORM java.net.URLClassLoader$2 [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.net.URLClassLoader$3$1 [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.net.URLClassLoader$3 [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.lang.ClassLoader$2 [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.net.URLClassLoader$1 [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.lang.SystemClassLoaderAction [null, null, loaded=true]
[Byte Buddy] TRANSFORM sun.misc.Launcher$AppClassLoader$1 [null, null, loaded=true]
[Byte Buddy] TRANSFORM sun.misc.Launcher$ExtClassLoader$1 [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.net.URLClassLoader$7 [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.lang.ClassLoader$ParallelLoaders [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.lang.ClassLoader$NativeLibrary [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.lang.ClassLoader$3 [null, null, loaded=true]
[Byte Buddy] TRANSFORM sun.misc.Launcher$ExtClassLoader [null, null, loaded=true]
[Byte Buddy] TRANSFORM sun.misc.Launcher$AppClassLoader [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.net.URLClassLoader [null, null, loaded=true]
[Byte Buddy] TRANSFORM sun.reflect.DelegatingClassLoader [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.security.SecureClassLoader [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.lang.ClassLoader [null, null, loaded=true]
--->>> OK fooMethod(world) io.pivotal.test.TestClass@2ca26d77
--->>> OK loadClass(org.assertj.core.api.Assertions) sun.misc.Launcher$AppClassLoader@18b4aac2
--->>> OK loadClass(org.junit.runners.model.MultipleFailureException) sun.misc.Launcher$AppClassLoader@18b4aac2
--->>> OK loadClass(org.junit.runner.notification.RunNotifier$7) sun.misc.Launcher$AppClassLoader@18b4aac2
--->>> OK loadClass(net.bytebuddy.pool.TypePool$Default$WithLazyResolution$LazyResolution) sun.misc.Launcher$AppClassLoader@18b4aac2
[Byte Buddy] ERROR java.lang.Throwable$WrappedPrintStream [null, null, loaded=false]
[Byte Buddy] ERROR java.lang.Throwable$PrintStreamOrWriter [null, null, loaded=false]
[Byte Buddy] ERROR java.util.IdentityHashMap$KeySet [null, null, loaded=false]
Exception in thread "main" java.lang.NoClassDefFoundError: org/junit/runners/model/MultipleFailureException
at org.junit.internal.runners.model.EachTestNotifier.addFailure(EachTestNotifier.java:20)
at org.junit.runners.ParentRunner.run(ParentRunner.java:369)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
[Byte Buddy] ERROR java.lang.Shutdown [null, null, loaded=false]
java.lang.NoClassDefFoundError: net/bytebuddy/pool/TypePool$Default$WithLazyResolution$LazyResolution
at net.bytebuddy.pool.TypePool$Default$WithLazyResolution.doDescribe(TypePool.java:1319)
at net.bytebuddy.pool.TypePool$AbstractBase.describe(TypePool.java:408)
at net.bytebuddy.pool.TypePool$AbstractBase$Hierarchical.describe(TypePool.java:471)
at net.bytebuddy.agent.builder.AgentBuilder$DescriptionStrategy$Default$1.apply(AgentBuilder.java:3373)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.resolve(AgentBuilder.java:10499)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:10469)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10432)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access$1500(AgentBuilder.java:10198)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:10807)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:10754)
at java.security.AccessController.doPrivileged(Native Method)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10355)
at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
[Byte Buddy] ERROR java.lang.Shutdown$Lock [null, null, loaded=false]
java.lang.NoClassDefFoundError: net/bytebuddy/pool/TypePool$Default$WithLazyResolution$LazyResolution
at net.bytebuddy.pool.TypePool$Default$WithLazyResolution.doDescribe(TypePool.java:1319)
at net.bytebuddy.pool.TypePool$AbstractBase.describe(TypePool.java:408)
at net.bytebuddy.pool.TypePool$AbstractBase$Hierarchical.describe(TypePool.java:471)
at net.bytebuddy.agent.builder.AgentBuilder$DescriptionStrategy$Default$1.apply(AgentBuilder.java:3373)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.resolve(AgentBuilder.java:10499)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:10469)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10432)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access$1500(AgentBuilder.java:10198)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:10807)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:10754)
at java.security.AccessController.doPrivileged(Native Method)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10355)
at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
at java.lang.Shutdown.<clinit>(Shutdown.java:61)
Process finished with exit code 1
我意识到我的测试没有做任何测试ClassLoader装饰的事情,但我只是首先尝试使用'普通'类,然后慢慢添加必要的位来测试ClassLoader
类。
我究竟做错了什么?仪器ClassLoader
s甚至可行吗?谢谢!
在检测类时,可能会在检测期间加载其他类。如果这会触发您依赖的类的类加载,例如您自己的代理的类不在Byte Buddy命名空间或引导加载程序的类中,这可能会触发一个检测循环,因此没有类def def发现您的错误经验。
尝试使用最大限度地避免类加载的POOL_ONLY
TypeStrategy
,您可能需要减少要检测的类的数量,以避免检测建立日志记录所需的类。