-1

Basically I have a requirement to track time spent in a method using custom annotation. (I know spring AOP can be used for this, but we can not use it in our product).

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

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

So, my requirement is to complete the TimeCounter annotation. The requirement are simple -

  1. Log time of method start.
  2. Log time of method end.
  3. Log total time spent in method.
  4. Name of method executed

Could someone help on how to implement this annotation to above requirements.

Thanks in advance.

joven
  • 371
  • 1
  • 6
  • 17

2 Answers2

2

There are already many libraries that have such annotations available. If you want your own implementation, one of the appraoches would be to use dynamic proxies:

Here's how your TimeCounterDemo may look like:

TimeCounter (Annotation)

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 (Interface)

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

    public void someOtherMethod(int a);
}

TimerCounterDemo (Implementation of above interface)


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

TimerProxy

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

}

Testing the timer:

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

Output:

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

You can read more about it here and here

adarsh
  • 1,393
  • 1
  • 8
  • 16
  • wonderful implementation +1. So, your implementation uses dynamic proxies which you linked. Right? And are annotations on interface methods inherited by implementing classes. This post says otherway - [Why java classes do not inherit annotations from implemented interfaces](https://stackoverflow.com/questions/4745798/why-java-classes-do-not-inherit-annotations-from-implemented-interfaces) – joven May 19 '21 at 09:22
  • @joven, Correct, we are using proxies. We didn't put `@TimerCounter` at interface for subclasses to inherit; it's so we can check for the annotation at `invoke` method. Proxy works with interfaces (hence the name dynamic proxies.) I put it on interface for simplicity. You can put the annotation at methods declared inside class too. You have `targetObj` and can check for annotations at its methods (like: `targetObj.getClass().getMethods()`) and act accordingly. – adarsh May 19 '21 at 13:12
  • @joven Note that we still (when we put `@TimeCounter` on method def inside `TimerCounterDemo`) need `ITimerCounterDemo`. This is because proxies work with interfaces. We cannot have `TimerCounterDemo t = (TimerCounterDemo) TimerProxy.newInstance(new TimerCounterDemo());`. The casting and assignment type has to be one of the interfaces implemented by `TimerCounterDemo`. – adarsh May 19 '21 at 13:17
-1

Instead of writing an annotation, you implement time tracking directly in your method. The first 2 lines in your method must be

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

Now you have to log the total time taken by the method. This can be done by writing the below statement into the same method

logger.info("Total time taken by the method is = " + (System.currentTimeMillis() - startTime));
Vipul Verma
  • 115
  • 1
  • 7