Назначение Advice.withCustomMapping (). Bind (...) - PullRequest
1 голос
/ 31 марта 2020

Я пытаюсь понять цель и использование Advice.withCustomMapping().bind(...), чтобы посмотреть, может ли это помочь в моем случае использования.

Чтение javado c для Advice.withCustomMapping():

Позволяет настраивать пользовательские аннотации, которые затем привязываются к динамически вычисляемому постоянному значению.

Вот пример использования, к которому я пытаюсь применить этот шаблон:

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 в stati c context из JettyAdvice и ServletContextAdvice. Так как методы совета должны быть статичны c, я не могу найти способ получить состояние экземпляра в этих методах (без эффективного построения какого-либо внешнего механизма ретрансляции, включающего карты экземпляров класса <->, что приводит к копированию + вставке кода в все подклассы AgentRule). Этот вариант использования применяется к проекту, который включает в себя множество правил , поэтому я пытаюсь найти наиболее эффективный и краткий способ сделать это.

Когда я пробую этот подход с Advice.withCustomMapping().bind(...), я получаю исключение от ByteBuddy, говорящее:

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

Является ли целью Advice.withCustomMapping().bind(...) только переопределение определенных c аргументов, которые существуют в сигнатуре метода? Я не смог найти упоминания об этом в javadocs, и, глядя на другие примеры в Интернете, мне кажется, что мой сценарий использования должен работать.

1 Ответ

1 голос
/ 31 марта 2020

Вам нужно

@Retention(RUNTIME)
public @interface Name { }

, иначе Byte Buddy не сможет увидеть вашу аннотацию и вернется к значению по умолчанию, которое является аргументом с тем же индексом.

...