【问题标题】:Inheriting annotation for AOP in GUICE在 GUICE 中继承 AOP 的注解
【发布时间】:2020-08-23 12:57:21
【问题描述】:

我正在使用 Guice 和 AspectJ,我正在尝试做一些 AOP 来测量某些方法的执行时间。

我有这个注释,它将用于注释我需要测量的所有方法:

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

我有这个方法拦截器:

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;
  }
}

我有这个界面

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

然后我有这个继承自 MySuperClass 的子类:

public class MySubClass implements MySuperClass {
}

最后,我有了这个绑定:

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

我像这样初始化我的 Guice 模块:

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

我的问题是没有记录任何内容,好像子类对 test() 方法的执行没有注释。

有什么办法可以解决这个问题?

【问题讨论】:

    标签: java aop guice aopalliance


    【解决方案1】:

    这是known and old issue,不太可能很快修复。

    有几种方法可以避免这种情况,但没有一种方法是标准的。最大功劳来自这里: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());
    }
    

    还有HasOrInheritsAnnotation 类(我试图解决任何缺少的通用问题,如果仍然存在,请编辑此答案以修复它):

    /**
     * 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();
        }
      }
    }
    

    【讨论】:

    • HasOrInheritsAnnotation中的matches方法是否应该接收一个Object而不是Method?我认为需要进行检查以验证传递的 Object 是否是 Method 的实例,如果是,则对其进行强制转换并执行流。类似@Override public boolean matches(Object o) { if (o instanceof Method) { Method method = (Method) o; return Stream.concat( Stream.of(method), getSuperMethods(method)) .anyMatch(this::hasARequiredAnnotation); } return false; }
    • @Nacho321 这基本上取决于你。但它应该是反映最多的东西。 Guice 显然足够聪明,可以做出区分,因为他们甚至有一个 Matcher.returns() 匹配器,它只匹配 Methods 并且像这个一样工作。我的猜测是保持现状,让 Guice 发挥它的魔力。
    猜你喜欢
    • 1970-01-01
    • 2019-03-04
    • 1970-01-01
    • 2014-09-03
    • 1970-01-01
    • 2011-11-02
    • 2015-08-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多