为定时方法调用编写java注释

时间:2011-04-21 05:51:16

标签: java annotations timing

我想写一个java注释,它是方法调用的时间。像这样的东西:

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

并且在调用此方法时,它应该在控制台上输出此方法花了多长时间

我知道如何在python中执行此操作,这就是我想要它做的事情:

from time import time, sleep

def time_it(func):
    def wrapper(*args, **kwargs):
        start = time()
        func(*args, **kwargs)
        stop = time()
        print "The function", func.__name__, " took %.3f" % (stop - start)
    wrapper.__name__ = func.__name__
    return wrapper

@time_it
def print_something(*args, **kwargs):
    print "before sleeping"
    print args, kwargs
    sleep(3) # wait 3 seconds
    print "after sleeping"

print_something(1, 2, 3, a="what is this?")

所以我的问题是? 我在哪里找到一些类似这样的文档,我尝试了apt文档,没有运气。 有人可以帮忙写这样的东西吗?

9 个答案:

答案 0 :(得分:7)

简单地说:你不能!

注释不是与代码一起自动启动的代码片段,它们只是注释,可以由处理代码的其他程序使用的信息片段,如加载或运行它。

你需要的是AOP:面向方面的编程。

答案 1 :(得分:7)

AFAIK,Tomasz说这是不能使用注释完成的。我认为混淆源于这样一个事实,即Python装饰器和Java注释共享相同的语法,但在它们提供的行为方面完全不同!

注释是附加到您的类/方法/字段的元数据。 This blog post解决了使用AOP的计时方法问题。虽然它使用Spring,但基本前提仍然相同。如果你喜欢使用AOP编译器,那么翻译代码应该不会太难。另一个参考(特定于春季)here

编辑:如果您的目标是在不使用完整的分析器的情况下为应用程序提供整体方法时序,则可以使用hprof来收集总执行统计信息。

答案 2 :(得分:5)

截至2016年,有一个漂亮的方面注释库jcabi-aspects

来自文档:

使用@Loggable注释注释您的方法,每次调用它们时,您的SLF4J日志记录工具都会收到一条消息,其中包含执行细节和总执行时间:

public class Resource {
  @Loggable(Loggable.DEBUG)
  public String load(URL url) {
    return url.openConnection().getContent();
  }
}

这样的东西会出现在日志中:

[DEBUG] #load('http://www.google.com'): returned "<html ..." in 23ms

详细了解@Loggable here

答案 3 :(得分:3)

查看Coda Hale Metrics库。它为提供此功能的方法提供@Timed注释。在您访问它时,请查看Code Hale Dropwizard,其中包含有关如何将其集成到其服务框架中的示例。

@GET
@Timed
public Saying sayHello(@QueryParam("name") Optional<String> name) {
    return new Saying(counter.incrementAndGet(),
                      String.format(template, name.or(defaultName)));
}

答案 4 :(得分:2)

尽管有所有的说法,你可以做到这一点。 Java注释不能更改它们操作的源文件或类文件,因此您的选项是:

1)使用超级课程。注释处理器可以生成一个超抽象方法的超类。您的实际类实现此方法。缺点是必须重命名您想要计时的方法,以便超类可以提供实现。结果可能如下所示

@BenchmarkMe( extend="MySuperClass" )
public class MyClass extends BenchmarkMyClass {
    public void normalMethod() { ... }
    public void bench_myMethod() { ... }
}  

并且注释过程将生成:

public class BenchmarkMyClass extends MySuperClass {
    public abstract void bench_myMethod();
    public void myMethod() {
       benchmarkStart();
       try {
          bench_myMethod();
       } finally { benchmarkStop(); }
    }
}

通过使用命名约定来指示应该定时哪些方法,因为在我的示例中使用了前缀“bench_”。

2)使用ClassFileTranformer和Annotation 方法是创建一个运行时注释,可用于标记您对计时感兴趣的方法。在运行时,在命令行上指定了ClassFileTransformer,它会转换字节代码以插入时序代码。

除非您喜欢使用字节代码,否则使用AOP是更好的选择,但 IS 可能。

答案 5 :(得分:2)

我很惊讶地看到没有人指出java.lang.reflect.Proxy。它是一个旧线程,但我认为这些信息对某人有帮助。

代理有一个有趣的属性,它给出了

  1. 代理实例Foo为true。
  2. 您可以在调用处理程序中使用一个方法,该方法首先打印时间,然后从对象触发实际方法。
  3. 您可以通过使所有对象实现某个接口来使用此代理,也可以使用Comparable。

    查找动态代理部分作为装饰器。

    http://www.ibm.com/developerworks/library/j-jtp08305/

答案 6 :(得分:2)

我好几次想到了同样的事情,最后写了下面的开头:

注释:

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 {

}

对象的界面:

package main;

public interface Examples {
    @Clocking
    void thisIsAMethod();

    void thisIsAnotherMethod(String something);

    @Clocking
    void thisIsALongRunningMethod();
}

调用处理程序:

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!");
        }
    }
}

最后一个测试它的入口点:

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

这需要改进,因为它需要Proxy来实例化我们的对象,因此你不能真正将它用于“通用已编写”代码。 但它可能会让你更完整。

答案 7 :(得分:1)

在Java中并不那么容易。基本的想法是:

  1. 创建“time this method”
  2. 的注释
  3. 创建一个使用字节码转换的java代理: 一个。使用注释查找方法 湾向他们添加时间码
  4. 运行java以使用新代理时设置javaagent选项
  5. 本文将帮助您入门:http://today.java.net/pub/a/today/2008/04/24/add-logging-at-class-load-time-with-instrumentation.html

    您也可以使用BTrace使这更容易:http://kenai.com/projects/btrace/pages/Home

答案 8 :(得分:0)

如前所述,您不能和AOP或hprof满足您的大部分需求,但如果您坚持使用JSR269就可以解决这个问题。仅供参考,apt已过时,并且注释处理API和工具已合并到1.6中(并且使用令人回味的名称JSR269调用它)。

解决方法是创建一个注释处理器,该处理器生成一个类,该类扩展包含带@TimeIt注释的方法的类。生成的类必须覆盖定时方法,它看起来像Python time_it,但行func(*args, **kwargs)将被super.methodName(arg1, arg2, ...)替换。

但有两点需要注意:

  1. 在代码的其他地方,您必须确保创建生成的类的实例而不是原始类。这是一个问题,因为你引用了一个尚不存在的类:它将在第一轮处理结束时创建。
  2. 你需要熟悉javax.annotation.processing和javax.lang.model包,它们有点尴尬恕我直言。