Question:`java.lang.NoClassDefFoundError` when use ClassInjector
I try to attach and enhance by GeneralInterceptor class, and use ClassInjector to inject GeneralExecutor class because GeneralInterceptor will call GeneralExecutor's method, but is will trigger 'java.lang.NoClassDefFoundError: com/agent/interceptor/GeneralExecutor'. Here is my code.
public static void agentmain(String agentArgs, Instrumentation inst) {
String interceptor = "com.agent.interceptor.GeneralExecutor";
File temp = null;
try {
temp = Files.createTempDirectory("tmp").toFile();
} catch (IOException e) {
e.printStackTrace();
}
ClassInjector.UsingInstrumentation
.of(temp, ClassInjector.UsingInstrumentation.Target.BOOTSTRAP, inst)
.inject(Collections.singletonMap(new TypeDescription.ForLoadedType(GeneralExecutor.class),
ClassFileLocator.ForClassLoader.read(GeneralExecutor.class)));
AgentBuilder builder = new AgentBuilder.Default()
.ignore(ElementMatchers.nameStartsWith("net.bytebuddy."))
.disableClassFormatChanges()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(new AgentBuilder.InjectionStrategy.UsingInstrumentation(inst, temp))
.with(classFileTransformer -> {
System.out.println("ResettableClassFileTransformer decorate=" + classFileTransformer);
return classFileTransformer;
})
.with(new GeneralAgentListener())
.type(named("com.agent.test.HandlerTest"))
.transform((b, typeDescription, classLoader, module) ->
b.visit(Advice.to(GeneralInterceptor.class).on(ElementMatchers.named("call"))));
builder.installOn(inst);
}
public class GeneralInterceptor {
@Advice.OnMethodEnter
public static void enter(@Advice.This Object thisObject) {
System.out.println(thisObject.getClass() + " entry ..");
try {
GeneralExecutor.start();
} catch (Throwable t) {
t.printStackTrace();
}
}
@Advice.OnMethodExit
public static void exit(@Advice.This Object thisObject) {
System.out.println(thisObject.getClass() + " exist ..");
try {
GeneralExecutor.end();
} catch (Throwable t) {
t.printStackTrace();
}
}
}
public class GeneralExecutor {
public static void before() {
System.out.println("method before..");
}
public static void after() {
System.out.println("method after..");
}
}
Your advice code is inlined into the target class, independent of the targeted class's class loader. The instrumented class has a different class loader and is therefore not able to see those methods which are not copied.
If you need to invoke methods from your agent, you need to inject those classes into the boot loader. The Instrumentation API or ClassInjector offers APIs for that.