Browse Source
Pass calls to default methods on proxy through to implementation on interface.pull/369/head
Dan Jasek
9 years ago
9 changed files with 179 additions and 10 deletions
@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
|
||||
package feign; |
||||
|
||||
import feign.InvocationHandlerFactory.MethodHandler; |
||||
import org.jvnet.animal_sniffer.IgnoreJRERequirement; |
||||
|
||||
import java.lang.invoke.MethodHandle; |
||||
import java.lang.invoke.MethodHandles.Lookup; |
||||
import java.lang.reflect.Field; |
||||
import java.lang.reflect.Method; |
||||
|
||||
/** |
||||
* Handles default methods by directly invoking the default method code on the interface. |
||||
* The bindTo method must be called on the result before invoke is called. |
||||
*/ |
||||
@IgnoreJRERequirement |
||||
final class DefaultMethodHandler implements MethodHandler { |
||||
// Uses Java 7 MethodHandle based reflection. As default methods will only exist when
|
||||
// run on a Java 8 JVM this will not affect use on legacy JVMs.
|
||||
// When Feign upgrades to Java 7, remove the @IgnoreJRERequirement annotation.
|
||||
private final MethodHandle unboundHandle; |
||||
|
||||
// handle is effectively final after bindTo has been called.
|
||||
private MethodHandle handle; |
||||
|
||||
public DefaultMethodHandler(Method defaultMethod) { |
||||
try { |
||||
Class<?> declaringClass = defaultMethod.getDeclaringClass(); |
||||
Field field = Lookup.class.getDeclaredField("IMPL_LOOKUP"); |
||||
field.setAccessible(true); |
||||
Lookup lookup = (Lookup) field.get(null); |
||||
|
||||
this.unboundHandle = lookup.unreflectSpecial(defaultMethod, declaringClass); |
||||
} catch (NoSuchFieldException ex) { |
||||
throw new IllegalStateException(ex); |
||||
} catch (IllegalAccessException ex) { |
||||
throw new IllegalStateException(ex); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Bind this handler to a proxy object. After bound, DefaultMethodHandler#invoke will act as if it was called |
||||
* on the proxy object. Must be called once and only once for a given instance of DefaultMethodHandler |
||||
*/ |
||||
public void bindTo(Object proxy) { |
||||
if(handle != null) { |
||||
throw new IllegalStateException("Attempted to rebind a default method handler that was already bound"); |
||||
} |
||||
handle = unboundHandle.bindTo(proxy); |
||||
} |
||||
|
||||
/** |
||||
* Invoke this method. DefaultMethodHandler#bindTo must be called before the first |
||||
* time invoke is called. |
||||
*/ |
||||
@Override |
||||
public Object invoke(Object[] argv) throws Throwable { |
||||
if(handle == null) { |
||||
throw new IllegalStateException("Default method handler invoked before proxy has been bound."); |
||||
} |
||||
return handle.invokeWithArguments(argv); |
||||
} |
||||
} |
Loading…
Reference in new issue