Inheriting annotation for AOP in GUICE

痴心易碎 提交于 2021-01-28 13:36:35

问题


I'm using Guice and AspectJ, and I am trying to do some AOP to measure the execution time of certain methods.

I have this annotation which will be used to annotate all the methods I need to measure:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface MyStopWatch {
}

I have this method interceptor:

public class MyInterceptor implements org.aopalliance.intercept.MethodInterceptor {
  private final Logger logger = LoggerFactory.getLogger(MyInterceptor.class);

  @Override
  public Object invoke(MethodInvocation invocation) throws Throwable {
    final org.apache.commons.lang3.time.StopWatch stopWatch = org.apache.commons.lang3.time.StopWatch.createStarted();
    final Object returnedObject = invocation.proceed();
    stopWatch.stop();

    logger.info("[STOPWATCH] Method: {} - Time: {}.", invocation.getMethod().getName(), stopWatch.getTime());

    return returnedObject;
  }
}

I have this interface

public interface MySuperClass {
  @MyStopWatch
  default void test() {
    System.out.println("Hello, world");
  }
}

Then I have this subclass which inherits from MySuperClass:

public class MySubClass implements MySuperClass {
}

And finally, I have this bind:

public class MyAOPModule extends AbstractModule {
  @Override
  protected void configure() {
    bindInterceptor(
            Matchers.any(),
            Matchers.annotatedWith(MyStopWatch.class),
            new MyInterceptor()
    );
  }
}

I initialize my Guice module like this:

public static void main(String[] args) throws Throwable {
  Injector injector = createInjector(new MyAOPModule());
  injector.getInstance(MySubClass.class).test(); 
}

My problem is that there is nothing being logged, as if the subclass's execution of the test() method was not annotated.

Any way I can go around this?


回答1:


This is a known and old issue and is unlikely to be fixed soon.

There exist several ways to avoid this, but none are standard. The one with the most merit comes from here: https://codingcraftsman.wordpress.com/2018/11/11/google-guice-aop-with-interface-annotations/

// the configure override of a Guice Abstract Module
@Override
protected void configure() {
  bindInterceptor(any(),
      // an instance of the matcher looking for HTTP methods
      new HasOrInheritsAnnotation(GET.class, PATCH.class, PUT.class, POST.class, DELETE.class),
      // the method interceptor to bind with
      new MyInterceptor());
}

And the HasOrInheritsAnnotation class (I tried to resolve any missing generic issue, if any persists, please edit this answer to fix it):

/**
 * Matcher for methods which either have, or override a method which has a given
 * annotation type from the set.
 */
public class HasOrInheritsAnnotation extends AbstractMatcher<Method> {
  private final Set<Class<? extends Annotation>> annotationTypes;

  /**
   * Construct with the types of annotations required
   * @param annotationTypes which types of annotation make this matcher match
   */
  @SafeVarArgs
  public HasOrInheritsAnnotation(Class<? extends Annotation>... annotationTypes) {
    this.annotationTypes = ImmutableSet.copyOf(annotationTypes);
  }

  @Override
  public boolean matches(Method method) {
    // join this method in a stream with the super methods to fine
    // one which has the required annotation
    return Stream.concat(
          Stream.of(method),
          getSuperMethods(method))
        .anyMatch(this::hasARequiredAnnotation);
  }

  /**
   * Do any of the annotations on the method appear in our search list?
   * @param method the method to inspect
   * @return true if any of the annotations are found
   */
  private boolean hasARequiredAnnotation(Method method) {
    return Arrays.stream(method.getDeclaredAnnotations())
        .map(Annotation::annotationType)
        .anyMatch(annotationTypes::contains);
  }

  /**
   * Provide a stream of methods from all super types of this
   * @param method the method to search with
   * @return all methods that are overridden by <code>method</code>
   */
  private Stream<Method> getSuperMethods(Method method) {
    return streamOfParentTypes(method.getDeclaringClass())
        .map(clazz -> getSuperMethod(method, clazz))
        .filter(Optional::isPresent)
        .map(Optional::get);
  }

  /**
   * A stream of every parent type in the class hierarchy
   * @param declaringClass the class to read
   * @return a stream of the immediate parent types and their parents
   */
  private static Stream<Class<?>> streamOfParentTypes(Class<?> declaringClass) {
    return Stream.of(
        // all interfaces to the declaring class
        Arrays.stream(declaringClass.getInterfaces()),

        // all the parent types of those interfaces
        Arrays.stream(declaringClass.getInterfaces())
          .flatMap(HasOrInheritsAnnotation::streamOfParentTypes),

        // the super class of the declaring class
        Optional.ofNullable(declaringClass.getSuperclass())
          .map(Stream::of)
          .orElse(Stream.empty()),

        // any parents of the super class
        Optional.ofNullable(declaringClass.getSuperclass())
          .map(HasOrInheritsAnnotation::streamOfParentTypes)
          .orElse(Stream.empty()))

        // turn from a stream of streams into a single stream of types
        .flatMap(Function.identity());
  }

  /**
   * Search for the given method in a super class. In other words, which
   * is the super class method that the given method overrides?
   * @param method the method to match
   * @param superType the type to scan
   * @return an optional with the super type's method in, or empty if not found
   */
  private static Optional<Method> getSuperMethod(Method method, Class superType) {
    try {
      return Optional.of(superType.getMethod(method.getName(), method.getParameterTypes()));
    } catch (NoSuchMethodException e) {
      // this exception means the method doesn't exist in the superclass
      return Optional.empty();
    }
  }
}


来源:https://stackoverflow.com/questions/61671183/inheriting-annotation-for-aop-in-guice

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!