byte-buddy icon indicating copy to clipboard operation
byte-buddy copied to clipboard

Question:`java.lang.NoClassDefFoundError` when use ClassInjector

Open JamazRuan opened this issue 3 years ago • 1 comments

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..");
}

}


JamazRuan avatar Jul 18 '22 10:07 JamazRuan

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.

raphw avatar Jul 26 '22 19:07 raphw