Java 8 added the ability to support default methods in interfaces.
That’s great for a lot of reasons and it works fine but what about
dynamic proxies? Are the default method skipped when needed? Are they
forwarded to the invocation handler?
Well JVM is well done and it is forwarded to the InvocationHandler. It
means your invocation handler invoke(proxy, method, args) method is
invoked when calling an interface method with a default….which
sounds logical since after all that’s an interface method.
When you use proxies to fully decorate an existing instance no issue,
if you call the default method your proxy will call the same method on
the delegate instance and if it should use the default it will….but
if you hack the proxy to implement it on the
fly without any delegate how to do?
In this case you know that if a method is a default one you need to
call the default implementation of the interface. Here are two issues:
- how to know a method is a default one?
- How to call the default implementation?
First issue is easy to solve, Java 8 added to its reflection API
java.lang.reflect.Method.isDefault(). Side note is you can check it in
previous java versions too backporting its implementation to an
utility method:
public boolean isDefault() { return ((getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC) && getDeclaringClass().isInterface(); }
The second issue is a bit more complex. If you call
method.invoke(proxy, args) you’ll loop in the proxy since the proxy
will try to invoke the real method which is the proxy one actually.
To workaround it you need to use java 7 new invoke API (MethodHandle).
The methods to have a look are the *Special ones which basically skip
the overriding checks.
Here how to do:
MethodHandles.lookup()
.in(declaringClass)
.unreflectSpecial(method, declaringClass)
.bindTo(proxy)
.invokeWithArguments(args);
First step is to get a lookup (kind of method handle factory), then
force the declaring class (default is caller class), unwrap the
java.lang.reflect.Method to a “special” MethodHandler and finally
invoke it on the proxy. Since it is “special” it will skip the
overriding done by the proxying and invoke the default implementation
:).
Here what the InvocationHandler finally looks like:
final InvocationHandler handler = (proxy, method, args) -> { if (method.isDefault()) { final Class<?> declaringClass = method.getDeclaringClass(); return MethodHandles.lookup() .in(declaringClass) .unreflectSpecial(method, declaringClass) .bindTo(proxy) .invokeWithArguments(args); } // proxy impl of not defaults methods return null; };
This opens several interesting doors. For instance with EJBs you could – if the spec goes this way – decorate an interface default method with @Asynchronous and return an AsyncResult just wrapping the other method. This way you get for all implementations asychronous calls for free from the implementations point of view.
Where it can go weird is if you can inject in parameters CDI beans or any injectable EE resources…then you don’t need classes anymore 😉
Want more content ? Keep up-to-date on my new blog
Or stay in touch on twitter @rmannibucau
Unfortunately that doesn’t work for me.
unreflectSpecial throws an exception:
java.lang.IllegalAccessException: no private access for invokespecial
As described in the docs:
http://docs.oracle.com/javase/8/docs/api/java/lang/invoke/MethodHandles.Lookup.html#unreflectSpecial-java.lang.reflect.Method-java.lang.Class-
the lookup object must have private access which is not the case if it has not bee created by the interface class itself.
Did you do anything else?
You have several solution to force private access:
or
That worked. I used the second option since that one looks a litte less hacky. 😉
In my opinion java should provide an ‘official’ way to do that. For example:
Method.invokeDefault(Class c, Object obj, Object… args)
Anyway. Thank you very much for your article + reply! I couldn’t find anything on the topic elsewhere.
Indeed, thank you very much. Spent much of a day trying to track this down.
FYI: https://gist.github.com/jdigger/f4183e36c52a073b469d
Hey.
Is the message “no private access for invokespecial” some kind of BUG?
I really don’t found any official documentation about call about call default methods from Java proxy API.
When the interface is a inner type of class where I put main method to test, your first solution works, but if a place the interface in a separated file, the exception is thrown.
For me, use the reflection to change Java API is a design problem. May be default method methods were protected to be no called from proxies.
I dont think it is a bug.
About the design: yes and no. Such solutions are most of the time used in frameworks and in such a case it can be justified.