Writing a java annotation for timing method call

后端 未结 9 1384
逝去的感伤
逝去的感伤 2020-12-24 12:17

I want to write a java annotation which times the method call. something like this:

@TimeIt
public int someMethod() { ... }

and when this m

相关标签:
9条回答
  • 2020-12-24 12:54

    As already stated you can't and AOP or hprof should cover most of your needs, but if you insist there's a workaround using JSR269. FYI, apt is obsolete and an annotation processing API and tool has been incorporated into 1.6 (and it is called with the evocative name JSR269).

    The workaround would be to create an annotation processor that generates a class that extends the class that contains the method with the @TimeIt annotation. This generated class must override the timed method, it will look like the Python time_it but the line func(*args, **kwargs) would be replaced by super.methodName(arg1, arg2, ...).

    There are however two caveats:

    1. Elsewhere in your code you must be sure that you create instances of the generated class instead of the original class. That is a problem because you reference a class that does not exist yet: it will be created at the end of the first processing round.
    2. You will need to get familiar with the javax.annotation.processing and javax.lang.model packages, they are a bit awkward IMHO.
    0 讨论(0)
  • 2020-12-24 12:56

    I wondered the same thing several time and ended by writting the following start:

    The annotation:

    package main;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Clocking {
    
    }
    

    An object's interface:

    package main;
    
    public interface Examples {
        @Clocking
        void thisIsAMethod();
    
        void thisIsAnotherMethod(String something);
    
        @Clocking
        void thisIsALongRunningMethod();
    }
    

    An invocation handler:

    package main;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.time.Duration;
    import java.time.Instant;
    
    public class ExamplesInvocationHandler implements InvocationHandler {
        // ******************************
        // Fields
        // ******************************
        private Examples examples = new ExamplesImpl();
    
        // ******************************
        // Public methods
        // ******************************
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // If the annotation is not present, just redirect the method call to its origin...
            if(!method.isAnnotationPresent(Clocking.class)) {
                return method.invoke(examples, args);
            }
    
            // ... otherwise log the execution time of it.
            Instant start = Instant.now();
            Object returnObj = method.invoke(examples, args);
            Instant end = Instant.now();
    
            // TODO: This is for demonstration purpose only and should use the application's logging system.
            System.out.println("Method " + method.getName() + " executed in " + Duration.between(end, start) + ".");
    
            return returnObj;
        }
    
        // ******************************
        // Inner classes
        // ******************************
        private static class ExamplesImpl implements Examples {
            @Override
            public void thisIsAMethod() {
                System.out.println("thisIsAMethod called!");
            }
    
            @Override
            public void thisIsAnotherMethod(String something) {
                System.out.println("thisIsAnotherMethod called!");
            }
    
            @Override
            public void thisIsALongRunningMethod() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                System.out.println("thisIsALongRunningMethod called!");
            }
        }
    }
    

    An finally an entry point to test this:

    package main;
    import java.lang.reflect.Proxy;
    
    public class Main {
        public static void main(String[] args) {
            Examples examples = (Examples) Proxy.newProxyInstance(Examples.class.getClassLoader(), new Class[]{Examples.class}, new ExamplesInvocationHandler());
    
            examples.thisIsAMethod();
            examples.thisIsAnotherMethod("");
            examples.thisIsALongRunningMethod();
        }
    }
    

    This needs an improvement, since it requires Proxy to instantiate our object and so you can't really use it for "generic already written" code. But it might leads you to something more complete.

    0 讨论(0)
  • 2020-12-24 12:59

    AFAIK, Tomasz is right in saying that this can't be done using annotations. I think the confusion stems from the fact that Python decorators and Java annotations share the same syntax but are completely different in terms of the behavior they offer!

    Annotations are metadata attached to your class/methods/fields. This blog post addresses the point of timing methods using AOP. Though it uses Spring, the basic premise remains the same. If you are good to go with an AOP compiler, it shouldn't be too difficult to translate the code. Another reference (spring specific) here.

    EDIT: If your aim is to have a overall method timing for your application without using full blown profilers, you can use hprof for collecting total execution statistics.

    0 讨论(0)
提交回复
热议问题