Написание пользовательских аннотаций в java — пример аннотации TimeCounter

В основном у меня есть требование отслеживать время, затраченное на метод, с использованием пользовательской аннотации. (Я знаю, что для этого можно использовать Spring AOP, но мы не можем использовать его в нашем продукте).

public class TimeCounterDemo {
    
    @TimeCounter
    public void trackMyTimeSpentUsingAnnotation()
    {
        //some heavy processing stuff
    }   
}

public @interface TimeCounter {
  //need help with this implementation.
}

Итак, мое требование — заполнить аннотацию TimeCounter. Требование простое -

  1. Лог времени запуска метода.
  2. Время завершения метода в журнале.
  3. Зарегистрируйте общее время, проведенное в методе.
  4. Имя выполняемого метода

Может ли кто-нибудь помочь в реализации этой аннотации к вышеуказанным требованиям.

Заранее спасибо.


person joven    schedule 17.05.2021    source источник


Ответы (2)


Уже есть много библиотек, в которых есть такие аннотации. Если вам нужна собственная реализация, одним из подходов будет использование динамические прокси:

Вот как может выглядеть ваш TimeCounterDemo:

Счетчик времени (аннотация)

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 TimeCounter {

}

ITimerCounterDemo (интерфейс)

public interface ITimerCounterDemo {
    @TimeCounter
    public void trackMyTimeSpentUsingAnnotation();

    public void someOtherMethod(int a);
}

TimerCounterDemo (реализация вышеуказанного интерфейса)


public class TimerCounterDemo implements ITimerCounterDemo {
    
    public void trackMyTimeSpentUsingAnnotation() {
        System.out.println("TimerCounterDemo:: Going to sleep");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
        }
        System.out.println("TimerCounterDemo:: Completed.");
    }

    public void someOtherMethod(int a) {
        System.out.println("In someothermethod with value:: " + a);
    }
}

ТаймерПрокси

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Objects;

public class TimerProxy implements InvocationHandler {

    private Object targetObj;

    public static Object newInstance(Object targetObj) {
        Objects.requireNonNull(targetObj);
        return Proxy.newProxyInstance(
                      targetObj.getClass().getClassLoader(), 
                      targetObj.getClass().getInterfaces(),
                      new TimerProxy(targetObj)
               );
    }

    private TimerProxy(Object targetObj) {
        this.targetObj = targetObj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.isAnnotationPresent(TimeCounter.class)) {

            LocalDateTime start = LocalDateTime.now();
            Object returnObj = method.invoke(targetObj, args);

            System.out.println(method.getName() + " executed in "
                    + Duration.between(start, LocalDateTime.now()).getSeconds() + " seconds");
            return returnObj;
        }

        return method.invoke(targetObj, args);
    }

}

Проверка таймера:

public class TimerTest {
    public static void main(String[] args) throws InterruptedException {
        ITimerCounterDemo t = (ITimerCounterDemo) TimerProxy.newInstance(new TimerCounterDemo());
        t.someOtherMethod(10);
        t.trackMyTimeSpentUsingAnnotation();
    }
}

Вывод:

In someothermethod with value:: 10
TimerCounterDemo:: Going to sleep
TimerCounterDemo:: Completed.
trackMyTimeSpentUsingAnnotation executed in 2 seconds

Подробнее об этом можно прочитать здесь и здесь

person javac    schedule 19.05.2021
comment
замечательная реализация +1. Итак, ваша реализация использует динамические прокси, которые вы связали. Верно? И являются аннотациями к методам интерфейса, унаследованным реализующими классами. Этот пост говорит об обратном - Почему классы Java не наследуют аннотации из реализованных интерфейсов - person joven; 19.05.2021
comment
@joven, правильно, мы используем прокси. Мы не ставили @TimerCounter в интерфейсе для наследования подклассами; чтобы мы могли проверить аннотацию в методе invoke. Прокси работает с интерфейсами (отсюда и название динамические прокси). Для простоты я поставил его на интерфейс. Вы также можете поместить аннотацию в методы, объявленные внутри класса. У вас есть targetObj, и вы можете проверять наличие аннотаций в его методах (например: targetObj.getClass().getMethods()) и действовать соответствующим образом. - person javac; 19.05.2021
comment
@joven Обратите внимание, что нам по-прежнему (когда мы помещаем @TimeCounter в определение метода внутри TimerCounterDemo) нужно ITimerCounterDemo. Это потому, что прокси работают с интерфейсами. У нас не может быть TimerCounterDemo t = (TimerCounterDemo) TimerProxy.newInstance(new TimerCounterDemo());. Тип приведения и назначения должен быть одним из интерфейсов, реализованных TimerCounterDemo. - person javac; 19.05.2021

Вместо написания аннотации вы реализуете отслеживание времени непосредственно в своем методе. Первые 2 строки в вашем методе должны быть

long startTime = System.currentTimeMillis();
logger.info("Method started at = " + new Date(startTime);

Теперь вы должны зарегистрировать общее время, затраченное методом. Это можно сделать, написав приведенный ниже оператор в тот же метод

logger.info("Total time taken by the method is = " + (System.currentTimeMillis() - startTime));
person Vipul Verma    schedule 17.05.2021
comment
я знаю это .. не могли бы вы удалить свой ответ. Я думаю об удалении этого Q - person joven; 17.05.2021