001/* 002 * Copyright 2009-2023 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.core.mybatis.binding; 017 018import com.mybatisflex.core.util.MapUtil; 019import org.apache.ibatis.binding.MapperMethod; 020import org.apache.ibatis.reflection.ExceptionUtil; 021import org.apache.ibatis.session.SqlSession; 022 023import java.io.Serializable; 024import java.lang.invoke.MethodHandle; 025import java.lang.invoke.MethodHandles; 026import java.lang.invoke.MethodHandles.Lookup; 027import java.lang.invoke.MethodType; 028import java.lang.reflect.Constructor; 029import java.lang.reflect.InvocationHandler; 030import java.lang.reflect.InvocationTargetException; 031import java.lang.reflect.Method; 032import java.util.Map; 033 034/** 035 * @author Clinton Begin 036 * @author Eduardo Macarron 037 * @author Michael Yang 038 * <p> 039 * 参考 MapperProxy<T> 并开放 MapperMethodInvoker,方便子类代理 040 */ 041public class MybatisMapperProxy<T> implements InvocationHandler, Serializable { 042 043 private static final long serialVersionUID = -4724728412955527868L; 044 private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED 045 | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC; 046 private static final Constructor<Lookup> lookupConstructor; 047 private static final Method privateLookupInMethod; 048 protected final SqlSession sqlSession; 049 private final Class<T> mapperInterface; 050 private final Map<Method, MapperMethodInvoker> methodCache; 051 052 public MybatisMapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) { 053 this.sqlSession = sqlSession; 054 this.mapperInterface = mapperInterface; 055 this.methodCache = methodCache; 056 } 057 058 static { 059 Method privateLookupIn; 060 try { 061 privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class); 062 } catch (NoSuchMethodException e) { 063 privateLookupIn = null; 064 } 065 privateLookupInMethod = privateLookupIn; 066 067 Constructor<Lookup> lookup = null; 068 if (privateLookupInMethod == null) { 069 // JDK 1.8 070 try { 071 lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class); 072 lookup.setAccessible(true); 073 } catch (NoSuchMethodException e) { 074 throw new IllegalStateException( 075 "There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.", 076 e); 077 } catch (Exception e) { 078 lookup = null; 079 } 080 } 081 lookupConstructor = lookup; 082 } 083 084 @Override 085 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 086 try { 087 if (Object.class.equals(method.getDeclaringClass())) { 088 return method.invoke(this, args); 089 } 090 return cachedInvoker(method).invoke(proxy, method, args, sqlSession); 091 } catch (Throwable t) { 092 throw ExceptionUtil.unwrapThrowable(t); 093 } 094 } 095 096 097 protected MapperMethodInvoker cachedInvoker(Method method) throws Throwable { 098 try { 099 return MapUtil.computeIfAbsent(methodCache, method, m -> { 100 if (!m.isDefault()) { 101 return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); 102 } 103 try { 104 if (privateLookupInMethod == null) { 105 return new DefaultMethodInvoker(getMethodHandleJava8(method)); 106 } 107 return new DefaultMethodInvoker(getMethodHandleJava9(method)); 108 } catch (IllegalAccessException | InstantiationException | InvocationTargetException 109 | NoSuchMethodException e) { 110 throw new RuntimeException(e); 111 } 112 }); 113 } catch (RuntimeException re) { 114 Throwable cause = re.getCause(); 115 throw cause == null ? re : cause; 116 } 117 } 118 119 private MethodHandle getMethodHandleJava9(Method method) 120 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 121 final Class<?> declaringClass = method.getDeclaringClass(); 122 return ((Lookup) privateLookupInMethod.invoke(null, declaringClass, MethodHandles.lookup())).findSpecial( 123 declaringClass, method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()), 124 declaringClass); 125 } 126 127 private MethodHandle getMethodHandleJava8(Method method) 128 throws IllegalAccessException, InstantiationException, InvocationTargetException { 129 final Class<?> declaringClass = method.getDeclaringClass(); 130 return lookupConstructor.newInstance(declaringClass, ALLOWED_MODES).unreflectSpecial(method, declaringClass); 131 } 132 133 public interface MapperMethodInvoker { 134 Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable; 135 } 136 137 private static class PlainMethodInvoker implements MapperMethodInvoker { 138 private final MapperMethod mapperMethod; 139 140 public PlainMethodInvoker(MapperMethod mapperMethod) { 141 this.mapperMethod = mapperMethod; 142 } 143 144 @Override 145 public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable { 146 return mapperMethod.execute(sqlSession, args); 147 } 148 } 149 150 private static class DefaultMethodInvoker implements MapperMethodInvoker { 151 private final MethodHandle methodHandle; 152 153 public DefaultMethodInvoker(MethodHandle methodHandle) { 154 this.methodHandle = methodHandle; 155 } 156 157 @Override 158 public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable { 159 return methodHandle.bindTo(proxy).invokeWithArguments(args); 160 } 161 } 162}