Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java8 dynamic proxy and default methods

Tags:

java

java-8

Having a dynamic proxy for an interface with default methods, how do I invoke a default method? By using something like defaultmethod.invoke(this, ...) you just get your proxy invocation handler called (Which is somehow correct, cause you have no implementing class for this interface).

I have a workaround using ASM to create a class implementing the interface and delegating such calls to an instance of this class. But this is not a good solution, especially if the default method calls other interface methods (you get a delegator ping-pong). The JLS is surprisingly silent about this question...

Here a small code example:

public class Java8Proxy implements InvocationHandler {
    public interface WithDefaultMethod {
        void someMethod();

        default void someDefaultMethod() {
            System.out.println("default method invoked!");
        }
    }

    @Test
    public void invokeTest() {
        WithDefaultMethod proxy = (WithDefaultMethod) Proxy.newProxyInstance(
            WithDefaultMethod.class.getClassLoader(),
            new Class<?>[] { WithDefaultMethod.class }, this);
        proxy.someDefaultMethod();

    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // assuming not knowing the interface before runtime (I wouldn't use a
        // proxy, would I?)
        // what to do here to get the line printed out?

        // This is just a loop
        // method.invoke(this, args);

        return null;
    }
}
like image 662
Cfx Avatar asked Oct 05 '14 19:10

Cfx


People also ask

How does JDK dynamic proxy work?

JDK Dynamic Proxies allow one to create implementations of Java interfaces at runtime by the means of Reflection. A proxy may be seen as a subject that will forward method calls to target instances and eventually return any result produced by the target instance to the caller.

When should I use dynamic proxy?

Thus, a dynamic proxy class can be used to create a type-safe proxy object for a list of interfaces without requiring pre-generation of the proxy class, such as with compile-time tools.

How many types of dynamic proxy is available in Spring?

Spring used two types of proxy strategy one is JDK dynamic proxy and other one is CGLIB proxy. JDK dynamic proxy is available with the JDK.

What is dynamic proxy?

Dynamic proxies allow one single class with one single method to service multiple method calls to arbitrary classes with an arbitrary number of methods. A dynamic proxy can be thought of as a kind of Facade, but one that can pretend to be an implementation of any interface.


2 Answers

The accepted answer uses setAccessible(true) to break into MethodHandles.Lookup, something that is restricted in Java 9 and beyond. This mail describes a JDK change that works for Java 9 or later.

It is possible to get this to work on Java 8 (and later) if you can get the writer of the interface to call your utility with an instance of MethodHandles.Lookup created in the interface (so it gets the permission to access the default methods of the interface):

interface HelloGenerator {
  public static HelloGenerator  createProxy() {
    // create MethodHandles.Lookup here to get access to the default methods
    return Utils.createProxy(MethodHandles.lookup(), HelloGenerator.class);
  }
  abstract String name();
  default void sayHello() {
    System.out.println("Hello " + name());
  }
}

public class Utils {
  static <P> P createProxy(MethodHandles.Lookup lookup, Class<P> type) {
    InvocationHandler handler = (proxy, method, args) -> {
        if (method.isDefault()) {
          // can use unreflectSpecial here, but only because MethodHandles.Lookup
          // instance was created in the interface and passed through
          return lookup
              .unreflectSpecial(method, method.getDeclaringClass())
              .bindTo(proxy)
              .invokeWithArguments(args);
        }
        return ...; // your desired proxy behaviour
    };

    Object proxy = Proxy.newProxyInstance(
        type.getClassLoader(), new Class<?>[] {type}, handler);
    return type.cast(proxy);
  }
}

This approach won't handle all Java 8 use cases, but it did handle mine.

like image 28
JodaStephen Avatar answered Sep 18 '22 21:09

JodaStephen


You can use the MethodHandles type in your InvocationHandler. This code is copied from Zero Turnaround.

Constructor<MethodHandles.Lookup> constructor;
Class<?> declaringClass;
Object result;

if (method.isDefault()) {
   declaringClass = method.getDeclaringClass();
   constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);

   constructor.setAccessible(true);

   result = constructor.
      newInstance(declaringClass, MethodHandles.Lookup.PRIVATE).
      unreflectSpecial(method, declaringClass).
      bindTo(proxy).
      invokeWithArguments(args);

   return(result);
}
like image 198
McDowell Avatar answered Sep 22 '22 21:09

McDowell