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

Я хочу написать аннотацию java, которая раз вызов метода. что-то вроде этого:--4-->

@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 ответов


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

аннотации-это метаданные, прикрепленные к вашему классу/методам/полям. этот блог обращается к точке синхронизации методов с помощью AOP. Хотя он использует весну, основная предпосылка остается той же. Если вы хорошо пойдите с компилятором AOP, не должно быть слишком сложно перевести код. Еще одна ссылка (весенняя специфика) здесь.

редактировать: если ваша цель состоит в том, чтобы иметь общий метод синхронизации для вашего приложения без использования полномасштабных профилировщиков, вы можете использовать hprof по для сбора общей статистики выполнения.


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

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

вам нужно AOP: ориентированное на аспект Программирование.


в 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 здесь.


несмотря на все 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 возможно.


Проверьте Показатели Кода Хейла библиотека. Он предоставляет аннотацию @Timed для методов, которые предоставляют эту возможность. Пока вы на нем проверить Код Hale Dropwizard который имеет примеры того, как он был интегрирован в их Service framework.

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

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

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

  1. прокси-экземпляр Foo как true.
  2. у вас может быть метод в обработчике вызова, который сначала печатает время, а затем запускает фактический метод из объекта.

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

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

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


Это не так просто в Java. Основная идея была бы такова:--1-->

  1. создать аннотацию с надписью "time this method"
  2. создайте агент java, который использует преобразование байтового кода для: a. Найти методы с аннотацией b. Добавьте к ним код синхронизации
  3. установите параметр 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


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

аннотация:

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

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


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

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

но есть два предостережения:

  1. в другом месте кода Вы должны быть уверены, что создаете экземпляры созданного класса вместо исходного класса. Это проблема, потому что вы ссылаетесь на класс, которого еще не существует: он будет создан в конце первого цикла обработки.
  2. вам нужно будет ознакомиться с класса javax.аннотация.обработка и javax.ленг.модельные пакеты, они немного неудобные IMHO.