'Get children method's annotation in Java using reflection or something like this

OBS: My code is java 8.
For example, I have this stacktrace:

MainClass:MethodA() calls
--------ClassB:MethodB() calls
------------ClassC:MethodC() @Cacheable calls
----------------ClassFinal:MethodD()

MethodA() -> MethodB() -> MethodC() -> MethodD();


In my example, ClassC:methodC() it's noted with @Cacheable. I need get this annotation in ClassFinal:MethodD(), something like this:

    public void MethodD() {
        Cacheable cacheable = ...
    }

I already done this using reflection, but it isn't work with overload:

    public static <T extends Annotation> T getStacktraceAnnotation(Class<T> type) {
        int currStack = 0;
        T result = null;
        
        //
        //
        StackTraceElement[] stackTraceElementArray = Thread.currentThread().getStackTrace();
        
        //
        //
        for (java.lang.StackTraceElement curr : stackTraceElementArray) {
            try {
                Method[] methods = Class.forName(curr.getClassName()).getMethods();
                
                for (Method currMethod : methods) 
                    if (currMethod.getName().equals(curr.getMethodName())) {
                        result = currMethod.getAnnotation(type);
                        if (result != null) break;
                    }
                //
                //
                if (result != null || currStack++ > 6) break;
            } catch(Exception exc) {
                // Nothing!
            }
        }
        //
        //
        return result;
    }

Real stacktace of my program:

fff.commons.serverless.abstracts.v2.AbstractCommonIntegrationHttp.sendGet(AbstractCommonIntegrationHttp.java:320) 
fff.commons.serverless.abstracts.v2.Teste.a(Teste.java:14) 
fff.commons.serverless.abstracts.v2.Teste.main(Teste.java:18)

Teste.a(Teste.java:14) is noted with @Cachable
And I need get this anottation in sendGet(AbstractCommonIntegrationHttp.java:320)

My annotation:

@Retention(RUNTIME)
@Target({ TYPE, METHOD })
@Inherited
public @interface Cacheable {
    int secs() default 15;
}


Solution 1:[1]

I strongly suggest using a StackWalker instead:

private static final StackWalker SW = StackWalker
        .getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);

public static <T extends Annotation> T getStacktraceAnnotation(Class<T> type) {
    return SW.walk(s -> s.map(sf -> getMethodAnnotation(type, sf)).filter(Objects::nonNull)
            .findFirst()).orElseThrow();
}

private static <T extends Annotation> T getMethodAnnotation(Class<T> annotationClass,
        StackFrame sf) {
    try {
        return sf.getDeclaringClass()
                .getDeclaredMethod(sf.getMethodName(), sf.getMethodType().parameterArray())
                .getAnnotation(annotationClass);
    } catch (NoSuchMethodException e) {
        throw new RuntimeException(e);
    }
}

The StackWalker.StackFrame contains the information needed to distinguish between different overloads - namely the method type.
A StackTraceElement only contains the line number - which might be good enough if you parse the corresponding class file yourself - but this gets quickly out of hand.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Johannes Kuhn