Подтвердить что ты не робот

Запись аннотации java для вызова метода синхронизации

Я хочу написать аннотацию 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 документацию, которой не повезло. может кто-нибудь помочь с написанием чего-то подобного?

4b9b3361

Ответ 1

AFAIK, Томаш прав, говоря, что это невозможно сделать с помощью аннотаций. Я думаю, что путаница проистекает из того факта, что декодеры Python и аннотации Java имеют один и тот же синтаксис, но совершенно разные с точки зрения поведения, которое они предлагают!

Аннотации - это метаданные, связанные с вашим классом/методами/полями. В этом сообщении в блоге рассматривается метод определения времени, использующий АОП. Хотя он использует Spring, основная посылка остается прежней. Если вы хорошо согласны с компилятором AOP, это не должно быть слишком сложным для перевода кода. Другая ссылка (spring specific) здесь.

РЕДАКТИРОВАТЬ. Если ваша цель состоит в том, чтобы иметь общий метод для вашего приложения без использования полномасштабных профилографов, вы можете использовать hprof для сбора общей статистики выполнения.

Ответ 2

Проще говоря: вы не можете!

Аннотации - это не фрагменты кода, которые автоматически запускаются вместе с вашим кодом, это просто аннотация, фрагменты информации, которые могут быть использованы другими программами, работающими над вашим кодом, например, загрузка или запуск.

Что вам нужно, это AOP: аспектно-ориентированное программирование.

Ответ 3

По состоянию на 2016 год существует библиотека замечательных аннотаций jcabi-аспекты.

Из документов:

Аннотируйте свои методы с помощью аннотации @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 здесь.

Ответ 4

Несмотря на все nay-sayers, вы можете это сделать. Аннотации 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, а также аннотацию Этот подход будет заключаться в создании аннотации времени выполнения, которые могут использоваться для обозначения методов, которые вас интересуют. Во время выполнения ClassFileTransformer указывается в командной строке и преобразует код байта, чтобы вставить код синхронизации.

Если вам не нравится работать с байтовым кодом, использование AOP - лучшая ставка, но возможно IS.

Ответ 5

Ознакомьтесь с библиотекой 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)));
}

Ответ 6

Я удивлен, увидев, что никто не указал java.lang.reflect.Proxy. Это старая нить, но я думаю, что эта информация была бы полезной для кого-то.

Прокси имеет интересное свойство, которое дает

  • proxy instanceof Foo как true.
  • У вас может быть метод в обработчике вызовов, который сначала печатает время, а затем запускает реальный метод из объекта.

У вас может быть этот прокси для всех объектов, заставив их реализовать какой-либо интерфейс или вы можете использовать Comparable.

Посмотрите раздел Динамические прокси в качестве декоратора.

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

Ответ 7

В Java это не так просто. Основная идея была бы такой:

  • Создать аннотацию, в которой указано "время этого метода"
  • Создайте java-агент, который использует преобразование байтового кода: а. Найти методы с помощью аннотации б. Добавьте к ним код синхронизации
  • Задайте параметр javaagent при запуске java для использования вашего нового агента.

В этой статье вы начнете: 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

Я задал себе одно и то же несколько раз и закончил, написав следующее:

Аннотация:

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

Это нуждается в улучшении, поскольку для этого требуется прокси-сервер, чтобы создать экземпляр нашего объекта, и поэтому вы не можете использовать его для "генерированного уже написанного" кода. Но это может привести к чему-то более полному.

Ответ 9

Как уже было сказано, вы не можете и AOP или hprof должны покрывать большинство ваших потребностей, но если вы настаиваете на обходном пути с использованием JSR269. FYI, apt является устаревшим, а API обработки обработки аннотации и инструмент включены в 1.6 (и он вызывается с вызывающим именем JSR269).

Обходным решением будет создание обработчика аннотаций, который генерирует класс, который расширяет класс, содержащий метод с аннотацией @TimeIt. Этот сгенерированный класс должен переопределить метод timed, он будет выглядеть как Python time_it, но строка func(*args, **kwargs) будет заменена на super.methodName(arg1, arg2, ...).

Однако существуют два оговорки:

  • В другом месте вашего кода вы должны быть уверены, что создаете экземпляры сгенерированного класса вместо исходного класса. Это проблема, потому что вы ссылаетесь на класс, который еще не существует: он будет создан в конце первого раунда обработки.
  • Вам нужно будет ознакомиться с пакетами javax.annotation.processing и javax.lang.model, они немного неудобны IMHO.