public interface Action {
void doSomething();
void dontProxy();
}
For example with this interface, I just want the JVM to proxy the doSomething method.
class DynamicProxy implements InvocationHandler{
private Action work;
public DynamicProxy(Action action){
this.work = action;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName()+" start");
var tmp = method.invoke(work,args);
System.out.println(method.getName()+" end");
return tmp;
}
Action action = (Action) Proxy.newProxyInstance(handler.getClass().getClassLoader(),work.getClass().getInterfaces(),handler);
The work is an instance of the Action interface implementation class. If I use Proxy.newProxyInstance, it seems to make all the interface methods be handled by the handler.
Indeed, in a proxy invocation, all methods are handled by the specified
InvocationHandler. As folks already pointed out in the comments, you would otherwise end up with unimplemented methods, and the typical implementation of anInvocationHandlerusually involves some kind of logic to switch between different implementations for different methods. For methods that should retain the original behavior you would usually have some kind of pass-through handler that just calls the original method (but the methods would still be proxied).Alternatively, there are several libraries available that make this sort of instrumentation easier. For example, with the Projo library you could use a slightly different approach (DISCLAIMER: I'm the author of that library):
This is especially helpful if you have a large interface with dozens of methods, but you only wish to instrument just a few methods. To test this out, you could write something like this:
The resulting output should be something like:
As you see, only
doSomething()is instrumented with the start/end messages, whereas callingdontProxy()just prints the original message.For this to work you just need to add these two additional dependencies:
Another advantage of this approach is that it actually does not use proxies (which are very inefficient and slow), but generates some wrapper code at runtime. If you leave out the second dependency (
projo-runtime-code-generation) the code will revert to using proxies, which will be very close to what your original code is doing (but also not very efficient; you might also see some warnings about illegal accesses via reflection).Under the hood, Projo uses Byte Buddy for the runtime code generation, and you can also hand-roll a simple solution for your case just using Byte Buddy directly.
Spring and some other dependency injection frameworks also support efficient code instrumentation using annotation-based approaches.