Advice.withCustomMapping().bind(...) 的目的

Purpose of Advice.withCustomMapping().bind(...)

我正在尝试了解 Advice.withCustomMapping().bind(...) 的目的和用途,看看它是否可以帮助我的用例。

正在阅读 Advice.withCustomMapping() 的 javadoc:

Allows for the configuration of custom annotations that are then bound to a dynamically computed, constant value.

这是我尝试应用此模式的用例:

public @interface Name {
}
public abstract class AgentRule {
  private final String className = getClass().getName();

  public final Advice.WithCustomMapping advice() {
    return Advice.withCustomMapping().bind(Name.class, className);
  }

  public static boolean isEnabled(final String className, final String origin) {
    ...
  }

  public abstract Iterable<? extends AgentBuilder> buildAgent(AgentBuilder builder) throws Exception;
}
public class ServletContextAgentRule extends AgentRule {
  public static boolean filterAdded = false;

  @Override
  public Iterable<? extends AgentBuilder> buildAgent(final AgentBuilder builder) throws Exception {
    return Arrays.asList(builder
      .type(named("org.eclipse.jetty.servlet.ServletContextHandler"))
      .transform(new Transformer() {
        @Override
        public Builder<?> transform(final Builder<?> builder, final TypeDescription typeDescription, final ClassLoader classLoader, final JavaModule module) {
          return builder.visit(advice().to(JettyAdvice.class).on(isConstructor()));
        }})
.type(not(isInterface()).and(hasSuperType(named("javax.servlet.ServletContext"))
        // Jetty is handled separately due to the (otherwise) need for tracking state of the ServletContext
      .transform(new Transformer() {
        @Override
        public Builder<?> transform(final Builder<?> builder, final TypeDescription typeDescription, final ClassLoader classLoader, final JavaModule module) {
          return builder.visit(advice().to(ServletContextAdvice.class).on(isConstructor()));
        }}));
  }

  public static class JettyAdvice {
    @Advice.OnMethodExit
    public static void exit(final @Name String className, final @Advice.Origin String origin, final @Advice.This Object thiz) {
      if (isEnabled(className, origin))
        filterAdded = JettyAgentIntercept.addFilter(thiz);
    }
  }

  public static class ServletContextAdvice {
    @Advice.OnMethodExit
    public static void exit(final @Name String className, final @Advice.Origin String origin, final @Advice.This Object thiz) {
      if (isEnabled(className, origin))
        filterAdded = ServletContextAgentIntercept.addFilter(thiz);
    }
  }
}

实际上,我试图做的是将信息从 ServletContextAgentRule 实例上下文 中继到 静态上下文 JettyAdviceServletContextAdvice。由于建议方法应该是静态的,我无法找到一种方法将实例状态获取到这些方法中(没有有效地构建某种涉及 class<-> 实例映射的外部中继机制,导致复制+粘贴代码在 AgentRule 的所有子 class 中)。此用例正在应用于涉及 a lot of rules 的项目,这就是为什么我试图找出执行此操作的最高效和最简洁的方法。

当我用 Advice.withCustomMapping().bind(...) 尝试这种方法时,我从 ByteBuddy 得到一个异常说:

java.lang.IllegalStateException: org.eclipse.jetty.servlet.ServletContextHandler() does not define an index 0

Advice.withCustomMapping().bind(...) 的目的只是为了覆盖方法签名中存在的特定参数吗?我在 javadocs 中找不到对此的提及,并且在网上查看其他示例,我似乎认为我的用例应该有效。

你需要

@Retention(RUNTIME)
public @interface Name { }

否则 Byte Buddy 无法看到您的注释并回退到默认值,即具有相同索引的参数。