DATACMNS-991 - Support Java 9 compatible MethodHandle lookup.

DefaultMethodInvokingMethodInterceptor now contains two alternative implementations, one compatible to Java 8, one for Java 9.

Original pull request: #223.
This commit is contained in:
Jens Schauder
2017-05-31 11:11:32 +02:00
committed by Mark Paluch
parent 77be0ba859
commit 6a39937d51

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,10 @@
*/
package org.springframework.data.projection;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
@@ -27,6 +30,7 @@ import org.springframework.aop.ProxyMethodInvocation;
* Method interceptor to invoke default methods on the repository proxy.
*
* @author Oliver Gierke
* @author Jens Schauder
*/
public class DefaultMethodInvokingMethodInterceptor implements MethodInterceptor {
@@ -36,15 +40,27 @@ public class DefaultMethodInvokingMethodInterceptor implements MethodInterceptor
* Creates a new {@link DefaultMethodInvokingMethodInterceptor}.
*/
public DefaultMethodInvokingMethodInterceptor() {
constructor = tryToGetLookupConstructor();
}
private static Constructor<Lookup> tryToGetLookupConstructor() {
try {
this.constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
Constructor<Lookup> accessibleConstructor = Lookup.class.getDeclaredConstructor(Class.class);
if (!accessibleConstructor.isAccessible()) {
accessibleConstructor.setAccessible(true);
}
return accessibleConstructor;
} catch (Exception ex) {
// this is the signal that we are on Java 9 and can't use the accessible constructor approach.
if (ex.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) {
return null;
} else {
throw new IllegalStateException(ex);
}
} catch (Exception o_O) {
throw new IllegalStateException(o_O);
}
}
@@ -65,7 +81,24 @@ public class DefaultMethodInvokingMethodInterceptor implements MethodInterceptor
Class<?> declaringClass = method.getDeclaringClass();
Object proxy = ((ProxyMethodInvocation) invocation).getProxy();
return constructor.newInstance(declaringClass).unreflectSpecial(method, declaringClass).bindTo(proxy)
.invokeWithArguments(arguments);
return getMethodHandle(method, arguments, declaringClass, proxy).bindTo(proxy).invokeWithArguments(arguments);
}
private MethodHandle getMethodHandle(Method method, Object[] arguments, Class<?> declaringClass, Object proxy)
throws Throwable {
if (constructor != null) {
// java 8 variant
return constructor.newInstance(declaringClass).unreflectSpecial(method, declaringClass);
}
// java 9 variant
return MethodHandles.lookup() //
.findSpecial( //
method.getDeclaringClass(), //
method.getName(), //
MethodType.methodType(method.getReturnType(), method.getParameterTypes()), //
method.getDeclaringClass() //
);
}
}