Как избежать рекурсивных вызовов с помощью байта приятель - java.lang.StackruError
У меня есть совет, который вызывает аналогичный метод в совете. Как сделать так, чтобы совет вызывался один раз и только один раз? Прямо сейчас, так как метод, который я вызываю в рамках совета, тот же, что и инструментальный, он переходит в рекурсивный вызов и приводит к java.lang.StackruError.
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));
}
1 ответ
Byte Buddy - это структура генерации кода, которая не является аспектно-ориентированной. Подумайте о коде, вставляемом в целевое местоположение; ошибка переполнения стека, которую вы видите, будет такой же, если вы жестко закодируете инструментарий в свой целевой метод.
Этого можно избежать, добавив флаг, например, вы можете определить ThreadLocal<Boolean>
что вы установили в true перед выполнением рекурсивного вызова, например:
if (!threadLocal.get()) {
threadLocal.set(true);
try {
// your code here.
} finally {
threadLocal.set(false);
}
}
Таким образом, вы можете отслеживать рекурсивный вызов. Вам, однако, нужно управлять своим состоянием. Один из вариантов - вставить класс держателя для вашего свойства в загрузчик классов начальной загрузки, используя Instrumentation
интерфейс.
Кроме того, вы можете проверить стек для повторного вызова. Это не так эффективно, как явное управление состоянием, но начиная с Java 9, вы можете использовать API обхода стека, который намного дешевле и делает его доступным.