|
@@ -21,10 +21,12 @@ import org.apache.ibatis.reflection.ExceptionUtil;
|
|
|
import org.apache.ibatis.session.SqlSession;
|
|
|
|
|
|
import java.io.Serializable;
|
|
|
+import java.lang.invoke.MethodHandle;
|
|
|
import java.lang.invoke.MethodHandles;
|
|
|
import java.lang.invoke.MethodType;
|
|
|
import java.lang.reflect.Constructor;
|
|
|
import java.lang.reflect.InvocationHandler;
|
|
|
+import java.lang.reflect.InvocationTargetException;
|
|
|
import java.lang.reflect.Method;
|
|
|
import java.util.Map;
|
|
|
|
|
@@ -37,16 +39,16 @@ import java.util.Map;
|
|
|
*/
|
|
|
public class MybatisMapperProxy<T> implements InvocationHandler, Serializable {
|
|
|
|
|
|
- private static final long serialVersionUID = -6424540398559729838L;
|
|
|
+ private static final long serialVersionUID = -5154982058833204559L;
|
|
|
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;
|
|
|
+ private final Map<Method, MapperMethodInvoker> methodCache;
|
|
|
|
|
|
- public MybatisMapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MybatisMapperMethod> methodCache) {
|
|
|
+ public MybatisMapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
|
|
|
this.sqlSession = sqlSession;
|
|
|
this.mapperInterface = mapperInterface;
|
|
|
this.methodCache = methodCache;
|
|
@@ -83,38 +85,81 @@ public class MybatisMapperProxy<T> implements InvocationHandler, Serializable {
|
|
|
try {
|
|
|
if (Object.class.equals(method.getDeclaringClass())) {
|
|
|
return method.invoke(this, args);
|
|
|
- } else if (method.isDefault()) {
|
|
|
- if (privateLookupInMethod == null) {
|
|
|
- return invokeDefaultMethodJava8(proxy, method, args);
|
|
|
- } else {
|
|
|
- return invokeDefaultMethodJava9(proxy, method, args);
|
|
|
- }
|
|
|
+ } else {
|
|
|
+ return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
|
|
|
}
|
|
|
} catch (Throwable t) {
|
|
|
throw ExceptionUtil.unwrapThrowable(t);
|
|
|
}
|
|
|
- final MybatisMapperMethod mapperMethod = cachedMapperMethod(method);
|
|
|
- return mapperMethod.execute(sqlSession, args);
|
|
|
}
|
|
|
-
|
|
|
- private MybatisMapperMethod cachedMapperMethod(Method method) {
|
|
|
- return CollectionUtils.computeIfAbsent(methodCache, method,
|
|
|
- k -> new MybatisMapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
|
|
|
+
|
|
|
+ private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
|
|
|
+ try {
|
|
|
+ return CollectionUtils.computeIfAbsent(methodCache, method, m -> {
|
|
|
+ if (m.isDefault()) {
|
|
|
+ try {
|
|
|
+ if (privateLookupInMethod == null) {
|
|
|
+ return new DefaultMethodInvoker(getMethodHandleJava8(method));
|
|
|
+ } else {
|
|
|
+ return new DefaultMethodInvoker(getMethodHandleJava9(method));
|
|
|
+ }
|
|
|
+ } catch (IllegalAccessException | InstantiationException | InvocationTargetException
|
|
|
+ | NoSuchMethodException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return new PlainMethodInvoker(new MybatisMapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (RuntimeException re) {
|
|
|
+ Throwable cause = re.getCause();
|
|
|
+ throw cause == null ? re : cause;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- private Object invokeDefaultMethodJava9(Object proxy, Method method, Object[] args)
|
|
|
- throws Throwable {
|
|
|
+ private MethodHandle getMethodHandleJava9(Method method)
|
|
|
+ throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
|
|
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);
|
|
|
+ return ((MethodHandles.Lookup) privateLookupInMethod.invoke(null, declaringClass, MethodHandles.lookup())).findSpecial(
|
|
|
+ declaringClass, method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()),
|
|
|
+ declaringClass);
|
|
|
}
|
|
|
-
|
|
|
- private Object invokeDefaultMethodJava8(Object proxy, Method method, Object[] args)
|
|
|
- throws Throwable {
|
|
|
+
|
|
|
+ private MethodHandle getMethodHandleJava8(Method method)
|
|
|
+ throws IllegalAccessException, InstantiationException, InvocationTargetException {
|
|
|
final Class<?> declaringClass = method.getDeclaringClass();
|
|
|
- return lookupConstructor.newInstance(declaringClass, ALLOWED_MODES).unreflectSpecial(method, declaringClass)
|
|
|
- .bindTo(proxy).invokeWithArguments(args);
|
|
|
+ return lookupConstructor.newInstance(declaringClass, ALLOWED_MODES).unreflectSpecial(method, declaringClass);
|
|
|
+ }
|
|
|
+
|
|
|
+ interface MapperMethodInvoker {
|
|
|
+ Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class PlainMethodInvoker implements MapperMethodInvoker {
|
|
|
+ private final MybatisMapperMethod mapperMethod;
|
|
|
+
|
|
|
+ public PlainMethodInvoker(MybatisMapperMethod mapperMethod) {
|
|
|
+ super();
|
|
|
+ this.mapperMethod = mapperMethod;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
|
|
|
+ return mapperMethod.execute(sqlSession, args);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class DefaultMethodInvoker implements MapperMethodInvoker {
|
|
|
+ private final MethodHandle methodHandle;
|
|
|
+
|
|
|
+ public DefaultMethodInvoker(MethodHandle methodHandle) {
|
|
|
+ super();
|
|
|
+ this.methodHandle = methodHandle;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
|
|
|
+ return methodHandle.bindTo(proxy).invokeWithArguments(args);
|
|
|
+ }
|
|
|
}
|
|
|
}
|