|
@@ -21,6 +21,7 @@ import org.apache.ibatis.session.SqlSession;
|
|
|
|
|
|
import java.io.Serializable;
|
|
|
import java.lang.invoke.MethodHandles;
|
|
|
+import java.lang.invoke.MethodType;
|
|
|
import java.lang.reflect.Constructor;
|
|
|
import java.lang.reflect.InvocationHandler;
|
|
|
import java.lang.reflect.Method;
|
|
@@ -36,6 +37,10 @@ import java.util.Map;
|
|
|
public class MybatisMapperProxy<T> implements InvocationHandler, Serializable {
|
|
|
|
|
|
private static final long serialVersionUID = -6424540398559729838L;
|
|
|
+ private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
|
|
|
+ | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
|
|
|
+ private static final Constructor<MethodHandles.Lookup> lookupConstructor;
|
|
|
+ private static final Method privateLookupInMethod;
|
|
|
private final SqlSession sqlSession;
|
|
|
private final Class<T> mapperInterface;
|
|
|
private final Map<Method, MybatisMapperMethod> methodCache;
|
|
@@ -46,13 +51,43 @@ public class MybatisMapperProxy<T> implements InvocationHandler, Serializable {
|
|
|
this.methodCache = methodCache;
|
|
|
}
|
|
|
|
|
|
+ static {
|
|
|
+ Method privateLookupIn;
|
|
|
+ try {
|
|
|
+ privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
|
|
|
+ } catch (NoSuchMethodException e) {
|
|
|
+ privateLookupIn = null;
|
|
|
+ }
|
|
|
+ privateLookupInMethod = privateLookupIn;
|
|
|
+
|
|
|
+ Constructor<MethodHandles.Lookup> lookup = null;
|
|
|
+ if (privateLookupInMethod == null) {
|
|
|
+ // JDK 1.8
|
|
|
+ try {
|
|
|
+ lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
|
|
|
+ lookup.setAccessible(true);
|
|
|
+ } catch (NoSuchMethodException e) {
|
|
|
+ throw new IllegalStateException(
|
|
|
+ "There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",
|
|
|
+ e);
|
|
|
+ } catch (Throwable t) {
|
|
|
+ lookup = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ lookupConstructor = lookup;
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
|
|
try {
|
|
|
if (Object.class.equals(method.getDeclaringClass())) {
|
|
|
return method.invoke(this, args);
|
|
|
} else if (method.isDefault()) {
|
|
|
- return invokeDefaultMethod(proxy, method, args);
|
|
|
+ if (privateLookupInMethod == null) {
|
|
|
+ return invokeDefaultMethodJava8(proxy, method, args);
|
|
|
+ } else {
|
|
|
+ return invokeDefaultMethodJava9(proxy, method, args);
|
|
|
+ }
|
|
|
}
|
|
|
} catch (Throwable t) {
|
|
|
throw ExceptionUtil.unwrapThrowable(t);
|
|
@@ -62,21 +97,23 @@ public class MybatisMapperProxy<T> implements InvocationHandler, Serializable {
|
|
|
}
|
|
|
|
|
|
private MybatisMapperMethod cachedMapperMethod(Method method) {
|
|
|
- return methodCache.computeIfAbsent(method, k -> new MybatisMapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
|
|
|
+ return methodCache.computeIfAbsent(method,
|
|
|
+ k -> new MybatisMapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
|
|
|
}
|
|
|
|
|
|
- private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
|
|
|
+ private Object invokeDefaultMethodJava9(Object proxy, Method method, Object[] args)
|
|
|
+ throws Throwable {
|
|
|
+ final Class<?> declaringClass = method.getDeclaringClass();
|
|
|
+ return ((MethodHandles.Lookup) privateLookupInMethod.invoke(null, declaringClass, MethodHandles.lookup()))
|
|
|
+ .findSpecial(declaringClass, method.getName(),
|
|
|
+ MethodType.methodType(method.getReturnType(), method.getParameterTypes()), declaringClass)
|
|
|
+ .bindTo(proxy).invokeWithArguments(args);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object invokeDefaultMethodJava8(Object proxy, Method method, Object[] args)
|
|
|
throws Throwable {
|
|
|
- final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
|
|
|
- .getDeclaredConstructor(Class.class, int.class);
|
|
|
- if (!constructor.isAccessible()) {
|
|
|
- constructor.setAccessible(true);
|
|
|
- }
|
|
|
final Class<?> declaringClass = method.getDeclaringClass();
|
|
|
- return constructor
|
|
|
- .newInstance(declaringClass,
|
|
|
- MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
|
|
|
- | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
|
|
|
- .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
|
|
|
+ return lookupConstructor.newInstance(declaringClass, ALLOWED_MODES).unreflectSpecial(method, declaringClass)
|
|
|
+ .bindTo(proxy).invokeWithArguments(args);
|
|
|
}
|
|
|
}
|