package tc.oc.commons.core.proxy; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; import javax.annotation.Nullable; import com.google.common.cache.LoadingCache; import tc.oc.commons.core.reflect.MethodHandleUtils; import tc.oc.commons.core.util.CacheUtils; public abstract class MethodHandleDispatcherBase { private static final MethodHandle NULL_HANDLE = MethodHandles.constant(Object.class, new Object()); private static final LoadingCache<Method, MethodHandle> UNBOUND_CACHE = CacheUtils.newCache(MethodHandleUtils::privateUnreflect); protected abstract @Nullable Object targetFor(Method method); protected MethodHandle unboundMethodHandle(Method method) { return UNBOUND_CACHE.getUnchecked(method); } protected MethodHandle boundMethodHandle(Method method) { final Object target = targetFor(method); return target == null ? NULL_HANDLE : unboundMethodHandle(method).bindTo(target); } public final Object dispatch(Object proxy, Method method, Object[] args, DefaultDispatch defaultDispatch) throws Throwable { final MethodHandle handle = boundMethodHandle(method); return handle == NULL_HANDLE ? defaultDispatch.call() : handle.invokeWithArguments(args); } protected interface DefaultDispatch { Object call() throws Throwable; } }