调用带有ByteBuddy的拦截方法中的lambda会引发java.lang.NoClassDefFoundError错误:

huangapple go评论116阅读模式
英文:

calling lambda from an intercept method with bytebuddy raises java.lang.NoClassDefFoundError:

问题

以下是您要翻译的内容:

我正在尝试使用ByteBuddy和Java代理进行一些仪器化工作。在其中的一个步骤中,我想捕获堆栈跟踪并使用我关心的调用进行过滤。让我们想象一个premain函数如下所示:

public class SeleniumReporter {
    public static void premain(final String agentArgs, final Instrumentation inst) {

        new AgentBuilder.Default()
                .with(new AgentBuilder.InitializationStrategy.SelfInjection.Eager())
                .type(named("org.openqa.selenium.remote.RemoteWebDriver"))
                .transform((builder, type, classLoader, module) -> builder
                        .method(nameStartsWith("findElement")
                                .and(takesArguments(By.class).or(takesArguments(String.class, String.class)))
                                .and(isPublic())
                        )
                        .intercept(Advice.to(FindElementInterceptor.class))
                )
                .installOn(inst);
    }
}

拦截器的形式如下:

public class FindElementInterceptor {
    @Advice.OnMethodExit
    public static void log(@Advice.This RemoteWebDriver driver, @Advice.Origin String method, @Advice.AllArguments Object[] args) {
        /*
        ... 一些额外的代码
        */
        
        final String stackTrace = Arrays.stream(Thread.currentThread().getStackTrace())
                .map(t -> String.format("%s:%s", t.getClassName(), t.getMethodName()))
                .filter(s -> !s.startsWith("org.codehaus.plexus."))
                .filter(s -> !s.startsWith("org.apache.maven."))
                .collect(Collectors.joining(";"));

        System.out.println(stackTrace);
    }
}

运行该代码会抛出java.lang.NoClassDefFoundError,因为Lambda表达式没有被加载。因此,我的问题是:我该如何确保它们被加载?我考虑过创建一个包含所有实用程序的类,并通过ByteBuddy加载它。是否有更加优雅的方法?

英文:

I am trying to do some instrumentation using ByteBuddy and a java agent. In one of the steps, I would like to capture the stacktrace and filter it with the calls that I care about. Let's imagine a premain function looks like this:

public class SeleniumReporter {
    public static void premain(final String agentArgs, final Instrumentation inst) {

        new AgentBuilder.Default()
                .with(new AgentBuilder.InitializationStrategy.SelfInjection.Eager())
                .type(named("org.openqa.selenium.remote.RemoteWebDriver"))
                .transform((builder, type, classLoader, module) -> builder
                        .method(nameStartsWith("findElement")
                                .and(takesArguments(By.class).or(takesArguments(String.class, String.class)))
                                .and(isPublic())
                        )
                        .intercept(Advice.to(FindElementInterceptor.class))
                )
                .installOn(inst);
    }
}

and an interceptor takes this form:

public class FindElementInterceptor {
    @Advice.OnMethodExit
    public static void log(@Advice.This RemoteWebDriver driver, @Advice.Origin String method, @Advice.AllArguments Object[] args) {
        /*
        ... Some extra code
        */
        
        final String stackTrace = Arrays.stream(Thread.currentThread().getStackTrace())
                .map(t -> String.format("%s:%s", t.getClassName(), t.getMethodName()))
                .filter(s -> !s.startsWith("org.codehaus.plexus."))
                .filter(s -> !s.startsWith("org.apache.maven."))
                .collect(Collectors.joining(";"));

        System.out.println(stackTrace);
    }
}

Running that code will throw a java.lang.NoClassDefFoundError because the lambda expression are not loaded. Hence, my question is: What can I do to ensure there are loaded? I was thinking of creating a class and load it through ByteBuddy with all the utils. Is there a more elegant way?

答案1

得分: 1

Byte Buddy无法将lambda表达式从advice方法复制到目标方法。从技术上讲,Lambda只是在定义它的类中的私有方法,这些方法对于目标类是不可用的。

改为使用常规迭代来避免这个问题。或者,您需要在单独的类中实现这些表达式,并使用ClassInjector将这些类注入到目标类加载器中。

英文:

Byte Buddy cannot copy the lambda expressions from the advice method to the target method. Lambdas are technically speaking only private methods in the class that defines it which will not be available to the target class.

Use regular iteration instead to avoid this problem. Alternatively, you need to implement the expressions in individual classes and inject these classes into the target class loader using a ClassInjector.

huangapple
  • 本文由 发表于 2020年8月14日 23:02:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/63415289.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定