Écriture d'une annotation java pour l'appel de méthode de chronométrage

je veux écrire une annotation java qui chronomètre l'appel de méthode. quelque chose comme ceci:

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

et quand cette méthode est invoquée, il devrait sortir sur la console combien de temps cette méthode a pris

je sais comment le faire en python, c'est ce que je veux faire:

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

Donc mes questions sont? Où puis-je trouver de la documentation pour écrire quelque chose comme ça, j'ai essayé apt la documentation, n'avait aucune chance avec elle. quelqu'un peut aider avec écrit quelque chose comme cela?

19
demandé sur roopesh 2011-04-21 09:51:16

9 réponses

AFAIK, Tomasz a raison de dire que cela ne peut pas être fait en utilisant des annotations. Je pense que la confusion vient du fait que les décorateurs Python et les annotations Java partagent la même syntaxe mais sont complètement différents en termes de comportement qu'ils offrent!

les Annotations sont des métadonnées attachées à votre classe/méthodes/champs. ce billet de blog aborde le point de synchronisation des méthodes en utilisant AOP. Bien qu'il utilise le ressort, le principe de base reste le même. Si vous êtes bon à aller avec un compilateur AOP, il ne devrait pas être trop difficile de traduire le code. Une autre référence (spécifique au ressort) ici.

EDIT: si votre but est d'avoir un timing de méthode global pour votre application sans utiliser de profileurs complets, vous pouvez utiliser hprof pour la collecte totale des statistiques d'exécution.

7
répondu Sanjay T. Sharma 2016-08-22 15:44:16

simplement dit: vous ne pouvez pas!

les Annotations ne sont pas des morceaux de code qui sont automatiquement lancés avec votre code, ce sont juste des annotations, des morceaux d'information qui peuvent être utilisés par d'autres programmes travaillant sur votre code comme le charger ou l'exécuter.

ce dont vous avez besoin, c'est D'une programmation orientée vers l'aspect.

7
répondu Tomasz Stanczak 2016-07-10 10:25:28

à partir de 2016, il y a une bibliothèque d'annotations d'aspects astucieux jcabi-aspects.

Dans la doc:

annotez vos méthodes avec @Loggable annotation et à chaque fois qu'elles sont appelées, votre installation de journalisation SLF4J recevra un message avec les détails de l'exécution et le temps total d'exécution:

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

quelque Chose comme cela apparaît dans le journal:

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

en savoir plus sur @Loggable ici.

4
répondu headsvk 2016-11-01 22:43:46

malgré tous les nay-sayers, vous pouvez le faire. Les annotations Java ne peuvent pas changer les fichiers source ou classe sur lesquels elles fonctionnent, donc vos options sont:

1)Utilisez une classe super. Le processeur d'annotation peut générer une super-classe qui fois une méthode abstraite. Votre classe applique cette méthode. L'inconvénient est que la méthode que vous voulez chronométrer doit être renommée pour que la super-classe puisse fournir une implémentation. Le résultat pourrait ressembler à ceci

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

et le processus d'annotation générerait:

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

en utilisant une convention de nommage pour indiquer quelles méthodes doivent être chronométrées car le préfixe "bench_" a été utilisé dans mon exemple.

sauf si vous aimez travailler avec le code octet, utiliser AOP est le meilleur pari, mais il possible.

2
répondu Simon G. 2011-04-21 13:30:34

découvrez le Coda Hale Metrics bibliothèque. Il fournit une annotation @Timed pour les méthodes qui fournissent cette capacité. Pendant que vous y êtes, découvrez Code Hale Dropwizard qui présente des exemples de la façon dont les STI ont été intégrées dans leur cadre de service.

@GET
@Timed
public Saying sayHello(@QueryParam("name") Optional<String> name) {
    return new Saying(counter.incrementAndGet(),
                      String.format(template, name.or(defaultName)));
}
2
répondu Jeff 2013-02-07 20:32:31

je suis surpris de voir que personne n'a souligné java.lang.refléter.Proxy. Son un vieux thread, mais je pense que cette information serait utile à quelqu'un.

Proxy a une propriété intéressante qui donne

  1. mandataire instanceofoo as true.
  2. vous pouvez avoir une méthode dans votre invocation handler, qui imprime d'abord l'heure puis renvoie la méthode actuelle à partir de l'objet.

vous pouvez avoir ce mandataire pour tous objets en les faisant implémenter une interface ou vous pouvez utiliser Comparable.

cherchez section Dynamic proxies comme décorateur.

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

2
répondu Ganesh Bhat 2016-04-07 14:57:10

ce n'est pas aussi facile en Java. L'idée de base est celle-ci:

  1. Créer une annotation qui dit "temps cette méthode"
  2. créer un agent java qui utilise la transformation de code octet pour: un. Trouver des méthodes avec l'annotation b. Ajouter le code de timing pour eux
  3. définissez l'option javaagent lorsque vous exécutez java pour utiliser votre nouvel agent

Cet article devrait vous aider à démarrer: http://today.java.net/pub/a/today/2008/04/24/add-logging-at-class-load-time-with-instrumentation.html .

vous pourriez également être en mesure d'utiliser BTrace pour rendre cela encore plus facile:http://kenai.com/projects/btrace/pages/Home

1
répondu Dave 2012-08-03 20:48:41

je me demandais la même chose plusieurs fois et a fini par écrire le début suivantes:

L'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 {

}

interface d'un objet:

package main;

public interface Examples {
    @Clocking
    void thisIsAMethod();

    void thisIsAnotherMethod(String something);

    @Clocking
    void thisIsALongRunningMethod();
}

Une invocation gestionnaire:

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

enfin, un point d'entrée pour tester ceci:

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

ceci nécessite une amélioration, car il faut un Proxy pour instancier notre objet et donc vous ne pouvez pas vraiment l'utiliser pour du code "generic already written". Mais il pourrait vous mène à quelque chose de plus complet.

1
répondu Romano 2017-01-19 14:11:07

comme déjà indiqué, vous ne pouvez pas et AOP ou hprof devraient couvrir la plupart de vos besoins, mais si vous insistez Il ya une solution de contournement en utilisant JSR269. FYI, apt est obsolète et une API et un outil de traitement des annotations ont été incorporés dans 1.6 (et il est appelé avec le nom évocateur JSR269).

La solution serait de créer une annotation processeur génère une classe qui étend la classe qui contient la méthode avec le @TimeIt annotation. Cette classe générée doit remplacer la temporisation de la méthode, il se présente comme le Python time_it mais la ligne func(*args, **kwargs) sera remplacé par super.methodName(arg1, arg2, ...).

il y a cependant deux mises en garde:

  1. ailleurs dans votre code, vous devez être sûr de créer des instances de la classe générée au lieu de la classe d'origine. C'est un problème parce que vous faites référence à une classe qui n'existe pas encore: il sera créé à la fin de la première ronde.
  2. vous devez vous familiariser avec le javax.annotation.traitement et javax.lang.packages modèles, ils sont un peu maladroits IMHO.
0
répondu Robert Bossy 2011-04-21 07:42:57