【问题标题】:Byte Buddy's method delegation leads to StackOverflowErrorByte Buddy 的方法委托导致 StackOverflowError
【发布时间】:2019-01-10 21:02:31
【问题描述】:

我正在尝试创建一个代理方法的代理,以便在方法之前和之后执行一些逻辑。为了做到这一点,我使用带有方法委托的 Byte Buddy。

代理:

//Agent code before...
private static void instrument(String agentOps, Instrumentation inst) {
    new AgentBuilder.Default().with(new Eager())
            .ignore(ElementMatchers.nameContains("com.dvelopp.agenttest"))
            .or(ElementMatchers.hasAnnotation(ElementMatchers.annotationType(ElementMatchers.nameContains("SpringBootApplication"))))
            .type((ElementMatchers.any()))
            .transform((builder, typeDescription, classLoader, module) -> builder.method(ElementMatchers.any())
                    .intercept(MethodDelegation.to(Interceptor.class)))
            .installOn(inst);
}
//Agent code after...

拦截器:

public static class Interceptor {

    @RuntimeType
    public static Object intercept(@SuperCall Callable<?> superCall, @SuperMethod Method superMethod, @Origin Method currentMethod,
                                   @AllArguments Object[] args, @This(optional = true) Object me) throws Exception {
        //... logic
        Object call = superCall.call();
        //... logic
        return call;
    }
}

它在一个简单的控制台应用程序上运行完美。但是,当我的类路径中有一些通用库(例如简单的 Spring Boot 项目)时,它不能按预期工作。它会产生不同的错误,但大多数都与这样的意外反射有关:

Exception in thread "main" java.lang.NoClassDefFoundError: sun/reflect/GeneratedMethodAccessor24
at sun.reflect.GeneratedMethodAccessor24.<clinit>(Unknown Source)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:403)
at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:394)
at java.security.AccessController.doPrivileged(Native Method)
at sun.reflect.MethodAccessorGenerator.generate(MethodAccessorGenerator.java:393)
at sun.reflect.MethodAccessorGenerator.generateMethod(MethodAccessorGenerator.java:75)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:53)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.logging.log4j.status.StatusLogger.<clinit>(StatusLogger.java:85)
at org.apache.logging.log4j.LogManager.<clinit>(LogManager.java:60)
at org.apache.commons.logging.LogAdapter$Log4jLog.<clinit>(LogAdapter.java:135)
at org.apache.commons.logging.LogAdapter$Log4jAdapter.createLog$original$snrPR67N(LogAdapter.java:102)
at org.apache.commons.logging.LogAdapter$Log4jAdapter.createLog$original$snrPR67N$accessor$5P1QZaof(LogAdapter.java)
at org.apache.commons.logging.LogAdapter$Log4jAdapter$auxiliary$pVmPc64S.call(Unknown Source)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:126)
at org.apache.commons.logging.LogAdapter$Log4jAdapter.createLog(LogAdapter.java)
at org.apache.commons.logging.LogAdapter.createLog$original$4Ty3vM8s(LogAdapter.java:79)
at org.apache.commons.logging.LogAdapter.createLog$original$4Ty3vM8s$accessor$z3gv7aJK(LogAdapter.java)
at org.apache.commons.logging.LogAdapter$auxiliary$6cBoraQE.call(Unknown Source)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:126)
at org.apache.commons.logging.LogAdapter.createLog(LogAdapter.java)
at org.apache.commons.logging.LogFactory.getLog$original$MoDHp2B7(LogFactory.java:67)
at org.apache.commons.logging.LogFactory.getLog$original$MoDHp2B7$accessor$VdtqA6Wx(LogFactory.java)
at org.apache.commons.logging.LogFactory$auxiliary$ak9XEBl0.call(Unknown Source)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:126)
at org.apache.commons.logging.LogFactory.getLog(LogFactory.java)
at org.apache.commons.logging.LogFactory.getLog$original$MoDHp2B7(LogFactory.java:59)
at org.apache.commons.logging.LogFactory.getLog$original$MoDHp2B7$accessor$VdtqA6Wx(LogFactory.java)
at org.apache.commons.logging.LogFactory$auxiliary$8JhBdK8k.call(Unknown Source)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:126)
at org.apache.commons.logging.LogFactory.getLog(LogFactory.java)
at org.springframework.boot.SpringApplication.<clinit>(SpringApplication.java:194)
at hello.Application.main(Application.java:15)
Caused by: java.lang.ClassNotFoundException: sun.reflect.GeneratedMethodAccessor24
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 39 more

如我所见,这些错误发生在代理对象上:

e.g. org.apache.commons.logging.LogFactory.getLog$original$MoDHp2B7

我尝试更深入地调试它,我发现超级方法和起源有不同的类。 “超级”有一个代理类。

@SuperMethod Method superMethod, @Origin Method currentMethod

有没有办法让委托与这些对象一起工作?

添加:

在这个 Spring Boot 2 示例中(https://spring.io/guides/gs/spring-boot/)有一个明确的 StackOverflowError:

java.lang.StackOverflowError: null
at com.dvelopp.agenttest.MethodCallContext.getCaller(MethodCallContext.java:18)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:108)
at org.springframework.core.env.SystemEnvironmentPropertySource.getSource(SystemEnvironmentPropertySource.java)
at org.springframework.core.env.MapPropertySource.getSource(MapPropertySource.java)
at org.springframework.core.env.SystemEnvironmentPropertySource.getSource$accessor$d92Pg8OK(SystemEnvironmentPropertySource.java)
at org.springframework.core.env.SystemEnvironmentPropertySource$auxiliary$ATPz5tSr.call(Unknown Source)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:122)
at org.springframework.core.env.SystemEnvironmentPropertySource.getSource(SystemEnvironmentPropertySource.java)
at org.springframework.core.env.MapPropertySource.getSource(MapPropertySource.java)
at org.springframework.core.env.SystemEnvironmentPropertySource.getSource$accessor$d92Pg8OK(SystemEnvironmentPropertySource.java)
at org.springframework.core.env.SystemEnvironmentPropertySource$auxiliary$ATPz5tSr.call(Unknown Source)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:122)
at org.springframework.core.env.SystemEnvironmentPropertySource.getSource(SystemEnvironmentPropertySource.java)
at org.springframework.core.env.MapPropertySource.getSource(MapPropertySource.java)
at org.springframework.core.env.SystemEnvironmentPropertySource.getSource$accessor$d92Pg8OK(SystemEnvironmentPropertySource.java)
at org.springframework.core.env.SystemEnvironmentPropertySource$auxiliary$ATPz5tSr.call(Unknown Source)
at com.dvelopp.agenttest.Main$Interceptor.intercept(Main.java:122)

【问题讨论】:

  • 这与 StackOverflowError 有关系吗?
  • 是的,错误是不同的。试图用谷歌搜索它们的错误会导致 StackOverflowError 问题。我试着不检测这个包,然后我在 Spring 包中得到了类似的东西。
  • 添加到我用 Spring 项目测试时发生的 StackOverflowError 问题。
  • 对我来说,这看起来像是 Byte Buddy 正确调用了 MapPropertySource 中 SystemEnvironmentPropertySource 的超级方法。但是,此方法确实会调用自身,这会导致递归。
  • 如果我从检测中排除类。例如。将 any() 更改为 .type((ElementMatchers.is(Main.class))) 然后一切正常。这就是为什么我认为 byte buddy 与 spring boot 冲突

标签: java stack-overflow byte-buddy javaagents


【解决方案1】:

您似乎使用了错误的注释或功能。

我建议检查更简单的情况。例如替换你的实现:

    @RuntimeType
    public static Object intercept(@SuperCall Callable<?> superCall, @SuperMethod Method superMethod, @Origin Method currentMethod,
                                   @AllArguments Object[] args, @This(optional = true) Object me) throws Exception {
        return superCall.call();
    }

下一个:

    @RuntimeType
    public static Object intercept(@RuntimeType Object value) throws Exception {
        return value;
    }

在这种情况下,您可以避免递归调用,但可能会有机会检测和分析不同的问题,这很清楚并且与您的情况有关。

可能你也应该注意概念问题。你知道java 开发人员使用Callable 对象不应该直接使用call()

遵循@RuntimeType 的 API 描述,它用于从一个对象映射到另一个对象。出现新问题:为什么需要转换callable 对象?它将转换为哪种类型(您确定没有 Future 包装器,您将在那里有一个对象。可能有 null 可能导致您的异常,不是吗?)?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-11
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多