如何避免使用 byte buddy 进行递归调用 - java.lang.StackOverflowError

how to avoid recursive calls with byte buddy - java.lang.StackOverflowError

我有一个建议在建议中调用类似的方法。我们如何确保通知被调用一次且仅调用一次。现在,由于我在建议中调用的方法与正在检测的方法相同,它进入递归调用并导致 java.lang.WhosebugError.

 transform(
              new AgentBuilder.Transformer.ForAdvice()
.include(JettyHandlerAdvice.class.getClassLoader())
.advice(named("addFilterWithMapping").and(ElementMatchers.takesArgument(0,named("org.eclipse.jetty.servlet.FilterHolder"))),JettyHandlerAdvice.class.getName())
                        )

建议

@Advice.OnMethodEnter
    private static void before(@Advice.AllArguments Object[] args,  @Advice.Origin("#m") String methodName, @Advice.This Object thiz) {          
        FilterHolder filterHolder = ((org.eclipse.jetty.servlet.ServletHandler)thiz).addFilterWithMapping(XYZFilter.class, "/*", EnumSet.of(javax.servlet.DispatcherType.REQUEST));
    }

Byte Buddy 是一个代码生成框架,不是面向方面的。想想代码被复制粘贴到目标位置;如果您将检测硬编码到目标方法中,您看到的堆栈溢出错误将是相同的。

这可以通过添加一个标志来避免,例如,您可以定义一个 ThreadLocal<Boolean> 并在进行递归调用之前将其设置为 true,例如:

if (!threadLocal.get()) {
  threadLocal.set(true);
  try {
    // your code here.
  } finally {
    threadLocal.set(false);
  }
}

这样,您可以跟踪递归调用。但是,您确实需要在某个地方管理您的状态。一种选择是使用 Instrumentation 接口将 属性 的 holder class 注入 bootstrap class 加载程序。

或者,您可以检查递归调用的堆栈。这不如显式状态管理有效,但从 Java 9 开始,您可以使用堆栈 walker API,它更便宜并且更容易实现。