How can I create dynamic proxy for final Class?

alexsmail picture alexsmail · May 8, 2013 · Viewed 7k times · Source

In short: 1. I have some final class that I want to create dynamic proxy for it. How can I do it? 2. Can I convert MethodHandle to Method?

Details First of all, does exists any API to convert MethodHandle to Method? Something like in java.lang.invoke.MethodHandles

public MethodHandle unreflect(Method m) throws IllegalAccessException;

but the opposite way arrond?

Let say I want to create dynamic java.lang.reflect.Method. It is defiend as

public final
   class Method extends AccessibleObject implements GenericDeclaration,
                                                 Member ;

So, if I want to use JDK Dynamic proxy I must use some interface (Member for example). There 2 main drawabacks though. First, method such as

public Class<?>[] getParameterTypes();

and such as

public Class<?> getReturnType();

are not part of any interface, while they are extensively used.

The second drawback is that it fails to provide drop-in replacement. That is, I can't pass my dynamic proxy to the code that expects java.lang.reflect.Method.

Another approach is to use CGLIB or Javaassist. AFAIK, CGLIB can't proxy final class, does he? Can Javaassist proxy final class? How can I "remove" final identifier from the class? AFAIL, Javvassist can somehow do it...

Answer

Rafael Winterhalter picture Rafael Winterhalter · Dec 1, 2013

It depends on what kind of proxy you need. There are basically three aproaches of how you can achieve this, of which two are feasible in production code. As @probrekely stated, the problem of cglib or javassist ist that they dynamically create a subclass what is not possible for final classes. You can avoid this by:

  • Disabling byte code verification. The Java run time verifies byte code in order to secure that no malicious byte code is loaded. This is important when for example receiving classes over the network or the internet, for example an applet. This way, you could create a subclass of a final class since the byte code verifier would not stop you. Hypothetically, you can disable this verification if you run trusted code only. This can be done by running:

    java -Xverify:none ApplicationName
    

    This is however the solution I would recommend you the least. I would not use this aproach for production code but it is most certainly the easiest solution to implement.

  • Remove the final modifier from loaded classes, either before or after the classes are loaded. This can be achieve by using a Java agent. A Java agent can be installed at application startup via the command line or at runtime via the Attach API. With a byte code tool like ASM, you could parse the original byte array and remove the final modifier from all classes of interest. It is also possible to redefine classes that were already loaded. Remove a final modifier does not introduce conflicts with old class versions such that such a redefinition is always possible.

  • Do the same as I described with removing the final modifier but redefine the loaded class to actually contain all your instrumentation logic within the original class. This aporach will most certanly require the biggest effort but this will make your instrumentation transperent to all other code. This would be the cleanest solution of all solutions.