Search code examples
javaspringreflectionannotationsaop

How to get annotations from parent method?


I have an interface:

public interface JustToCheckInterface {

    @MyCheck(feature = "occurrence-logging")
    void log();

    void log2();

    @MyCheck(feature = "check-logging")
    void log3();
}

and implementation:

@Component
public class JustToCheck implements JustToCheckInterface {

    public void log() {
        System.out.println("hello");
    }

    @MyCheck(feature = "check-no-logging")
    public void log2() {
        System.out.println("hello2");
    }

    public void log3() {
        System.out.println("hello3");
    }
}

I've created annotation (the one is used in my interface and in spring component):

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Documented
public @interface MyCheck {

    @Required
    String feature();
}

and advisor:

@Component
public class MyAdvisor extends AbstractPointcutAdvisor {
    @Autowired
    private MyMethodInterceptor myMethodInterceptor;
    private final StaticMethodMatcherPointcut pointcut = new
            StaticMethodMatcherPointcut() {
                @Override
                public boolean matches(Method method, Class<?> targetClass) {
                    return method.isAnnotationPresent(MyCheck.class);
                }
            };

    @Override
    public Pointcut getPointcut() {
        return pointcut;
    }

    @Override
    public Advice getAdvice() {
        return myMethodInterceptor;
    }
}

method interceptor

@Component
public class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        MyCheck annotation = methodInvocation.getMethod().getAnnotation(MyCheck.class);
        if (mySpecialCheck(annotation.feature())) {
            return methodInvocation.proceed();
        }
        return null;
    }
}

It seems almost work. It works for object if the called method (that overrides parent interface) has correspond annotation. But it does not work for methods which overrides interface without annotation in case the interface methods has the annotation. See methods log() and log3().

As for me there are two potential candidates into solution:

  • reflection solution (get superMethods and annotations from super);
  • interceptor solution (improve one).

unfortunately i'm not strong in both of them.


Solution

  • You can traverse all the super classes and interfaces' super methods to find the annotation. But you may find more than one annotation because the method may declared in several classes or interfaces.

    Here is my sample code:

    public class Q46553516 {
      public static void main(String[] args) throws Exception {
        // the input method
        Method method = ClassB.class.getMethod("func");
        // get the annotation value
        Class<?> clz = method.getDeclaringClass();
        List<Anno> collect = Stream.concat(
            Stream.of(clz),
            Stream.concat(
                Stream.of(ReflectUtil.getAllSuperClasses(clz)),
                Stream.of(ReflectUtil.getAllInterfaces(clz))))
            .map(c -> {
              try {
                return c.getMethod(method.getName(), method.getParameterTypes());
              } catch (Exception e) {
                return null;
              }
            })
            .filter(m -> m != null)
            .map(m -> m.getAnnotation(Anno.class))
            .filter(a -> a != null)
            .collect(Collectors.toList());
        collect.forEach(System.out::println);
      }
    
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.METHOD)
      @Documented
      @Inherited
      public @interface Anno {
        String value();
      }
    
      static interface Inter {
        @Anno("Inter")
        void func();
      }
    
      static class ClassA implements Inter {
        @Override
        @Anno("ClassA")
        public void func() {
    
        }
      }
    
      static class ClassB extends ClassA {
        @Override
        public void func() {
    
        }
      }
    }
    

    and output:

    @xdean.stackoverflow.java.reflection.Q46553516$MyCheck(feature=ClassA)
    @xdean.stackoverflow.java.reflection.Q46553516$MyCheck(feature=Inter)
    

    I used my utility ReflectUtil, you can find it here