I wondered the same thing several time and ended by writting the following start:
The 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 {
}
An object's interface:
package main;
public interface Examples {
@Clocking
void thisIsAMethod();
void thisIsAnotherMethod(String something);
@Clocking
void thisIsALongRunningMethod();
}
An invocation handler:
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!");
}
}
}
An finally an entry point to test this:
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();
}
}
This needs an improvement, since it requires Proxy to instantiate our object and so you can't really use it for "generic already written" code.
But it might leads you to something more complete.