编写java注解用于计时方法调用

发布于 2024-11-02 11:55:28 字数 902 浏览 1 评论 0原文

我想写一个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 文档,没有运气。 有人可以帮忙写这样的东西吗?

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

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

and when this method is invoked, it should output on console how long this method took

I know how to do it in python, this is what I want it to do:

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?")

So my questions are?
Where do I find some documentation to write something like this, I tried apt documentation, had no luck with it.
can someone help with writing something like this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

调妓 2024-11-09 11:55:28

AFAIK,Tomasz 说得对,这不能使用注释来完成。我认为这种混乱源于这样一个事实:Python 装饰器和 Java 注释共享相同的语法,但它们提供的行为完全不同!

注释是附加到您的类/方法/字段的元数据。 这篇博文地址使用AOP的点计时方法。尽管它使用 Spring,但基本前提保持不变。如果您擅长使用 AOP 编译器,那么翻译代码应该不会太困难。另一个参考(特定于 spring)此处

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

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.

初见 2024-11-09 11:55:28

简而言之:你不能!

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

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

Simply put: you can't!

Annotations are not pieces of code that get automatically started together with your code, they are just annotation, pieces of information that can be used by other programs working on your code like loading or running it.

What you need is AOP: aspect oriented programming.

情何以堪。 2024-11-09 11:55:28

截至 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 的更多信息。

As of 2016, there's a nifty aspect annotation library jcabi-aspects.

From the docs:

Annotate your methods with @Loggable annotation and every time they are called, your SLF4J logging facility will receive a message with the details of execution and the total execution time:

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

Something like this will appear in the log:

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

Read more about @Loggable here.

流云如水 2024-11-09 11:55:28

我多次想知道同样的事情,并通过编写以下开始结束:

注释:

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 来实例化我们的对象,所以你不能真正将它用于“通用已编写”代码。
但它可能会让你得到更完整的东西。

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.

东京女 2024-11-09 11:55:28

查看 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)));
}

Check out the Coda Hale Metrics library. It provides a @Timed annotation for methods that provides this capability. While you're at it check out Code Hale Dropwizard which has examples for how its been integrated into their service framework.

@GET
@Timed
public Saying sayHello(@QueryParam("name") Optional<String> name) {
    return new Saying(counter.incrementAndGet(),
                      String.format(template, name.or(defaultName)));
}
瑕疵 2024-11-09 11:55:28

尽管有很多反对者,但你可以做到这一点。 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 是更好的选择,但它是可能的。

Despite all the nay-sayers, you can do this. Java annotations cannot change the source or class files they operate on, so your options are:

1) Use a super class. The annotation processor can generate a super-class that times an abstract method. Your actual class implements this method. The downsides is that the method you want to time has to be renamed so that the super-class can provide an implementation. The result might look like this

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

and the annotation process would generate:

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

By using a naming convention to indicate which methods should be timed as the prefix "bench_" was used in my example.

2) Use a ClassFileTranformer as well as an Annotation
The approach would be to create a runtime annotation that can be used to mark the methods you are interested in timing. At runtime a ClassFileTransformer is specified on the command line and it transforms the byte code to insert the timing code.

Unless you like working with byte code, using AOP is the better bet, but it IS possible.

岁月苍老的讽刺 2024-11-09 11:55:28

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

Proxy 有一个有趣的属性,它使

  1. proxy instanceof Foo 为 true。
  2. 您可以在调用处理程序中使用一个方法,该方法首先打印时间,然后触发对象中的实际方法。

您可以通过使所有对象实现某个接口来为所有对象拥有此代理,或者您可以使用 Comparable。

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

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

I am surprised to see that no one pointed out java.lang.reflect.Proxy. Its an old thread, but I think this information would be helpful to someone.

Proxy has an interesting property which gives

  1. proxy instanceof Foo as true.
  2. You can have a method in your invocation handler, which prints the time first and then fires the actual method from the object.

You can have this proxy for all objects by making them implement some interface or you can use Comparable.

Look for section Dynamic proxies as decorator.

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

捎一片雪花 2024-11-09 11:55:28

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

  1. 创建一个注释,表示“time this method”
  2. 创建一个使用字节码转换的 java 代理来:
    一个。查找带注解的方法
    b.向它们添加计时代码
  3. 在运行 java 时设置 javaagent 选项以使用新代理

本文将帮助您入门: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

It isn't nearly as easy in Java. The basic idea would be this:

  1. Create annotation that says "time this method"
  2. Create a java agent that uses byte code transformation to:
    a. Find methods with the annotation
    b. Add timing code to them
  3. Set the javaagent option when you run java to use your new agent

This article would get you started: http://today.java.net/pub/a/today/2008/04/24/add-logging-at-class-load-time-with-instrumentation.html .

You might also be able to use BTrace to make this even easier: http://kenai.com/projects/btrace/pages/Home

嘿咻 2024-11-09 11:55:28

如前所述,您不能,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 包,恕我直言,它们有点尴尬。

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.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文