How to explicitly invoke default method from a dynamic Proxy?

前端 未结 6 820
一向
一向 2020-12-09 15:33

Since Java 8 interfaces could have default methods. I know how to invoke the method explicitly from the implementing method, i.e. (see Explicitly calling a default method i

6条回答
  •  小蘑菇
    小蘑菇 (楼主)
    2020-12-09 16:10

    I've been troubled by similar issues as well when using MethodHandle.Lookup in JDK 8 - 10, which behave differently. I've blogged about the correct solution here in detail.

    This approach works in Java 8

    In Java 8, the ideal approach uses a hack that accesses a package-private constructor from Lookup:

    import java.lang.invoke.MethodHandles.Lookup;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Proxy;
    
    interface Duck {
        default void quack() {
            System.out.println("Quack");
        }
    }
    
    public class ProxyDemo {
        public static void main(String[] a) {
            Duck duck = (Duck) Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[] { Duck.class },
                (proxy, method, args) -> {
                    Constructor constructor = Lookup.class
                        .getDeclaredConstructor(Class.class);
                    constructor.setAccessible(true);
                    constructor.newInstance(Duck.class)
                        .in(Duck.class)
                        .unreflectSpecial(method, Duck.class)
                        .bindTo(proxy)
                        .invokeWithArguments(args);
                    return null;
                }
            );
    
            duck.quack();
        }
    }
    

    This is the only approach that works with both private-accessible and private-inaccessible interfaces. However, the above approach does illegal reflective access to JDK internals, which will no longer work in a future JDK version, or if --illegal-access=deny is specified on the JVM.

    This approach works on Java 9 and 10, but not 8

    import java.lang.invoke.MethodHandles;
    import java.lang.invoke.MethodType;
    import java.lang.reflect.Proxy;
    
    interface Duck {
        default void quack() {
            System.out.println("Quack");
        }
    }
    
    public class ProxyDemo {
        public static void main(String[] a) {
            Duck duck = (Duck) Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[] { Duck.class },
                (proxy, method, args) -> {
                    MethodHandles.lookup()
                        .findSpecial( 
                             Duck.class, 
                             "quack",  
                             MethodType.methodType(void.class, new Class[0]),  
                             Duck.class)
                        .bindTo(proxy)
                        .invokeWithArguments(args);
                    return null;
                }
            );
    
            duck.quack();
        }
    }
    

    Solution

    Simply implement both of the above solutions and check if your code is running on JDK 8 or on a later JDK and you'll be fine. Until you're not :)

提交回复
热议问题