As others have correctly said already, use a DynamicProxy. Here's an example.
This class uses a DynamicProxy to intercept invocations of methods declared in the "HammerListener" interface. It does some logging and then delegates to the "real" HammerListener implementation (yes, the same thing can be done with AOP).
See the newInstance method for proxy instantiation (note that you need to pass in the interface(s) the proxy should implement - a proxy can implement multiple interface).
All method invocations on interfaces that the proxy implements will end up as calls to the "invoke" method, which is declared in the "InvocationHandler" interface. All proxy handlers must implement this interface.
import java.lang.reflect.*;
/**
* Decorates a HammerListener instance, adding BEFORE/AFTER
* log messages around all methods exposed in the HammerListener interface.
*/
public class HammerListenerDecorator implements InvocationHandler {
private final HammerListener delegate;
static HammerListener newInstance(HammerListener delegate) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return (HammerListener)Proxy.newProxyInstance(cl, new Class[]{HammerListener.class},
new HammerListenerDecorator(delegate));
}
private HammerListenerDecorator(HammerListener delegate) {
this.delegate = delegate;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
logger.info("BEFORE " + method.getName() + " {{{" + argsToString(args) + "}}}");
Object rtn = method.invoke(delegate, args);
logger.info("AFTER " + method.getName());
return rtn;
}
private String argsToString(Object[] args) {
StringBuilder sb = new StringBuilder();
for (Object o : args) {
sb.append(String.valueOf(o)).append(" ");
}
return sb.toString();
}
}