mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-08 17:48:25 +08:00
refactor: performance optimization
This commit is contained in:
parent
adb43c6c47
commit
eb663b3518
@ -279,7 +279,7 @@ public class GeneratorTest {
|
|||||||
generator.generate();
|
generator.generate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
// @Test
|
||||||
public void testCodeGen5() {
|
public void testCodeGen5() {
|
||||||
// 配置数据源
|
// 配置数据源
|
||||||
HikariDataSource dataSource = new HikariDataSource();
|
HikariDataSource dataSource = new HikariDataSource();
|
||||||
|
|||||||
@ -22,12 +22,15 @@ import com.mybatisflex.core.keygen.MultiEntityKeyGenerator;
|
|||||||
import com.mybatisflex.core.keygen.MultiRowKeyGenerator;
|
import com.mybatisflex.core.keygen.MultiRowKeyGenerator;
|
||||||
import com.mybatisflex.core.keygen.MybatisKeyGeneratorUtil;
|
import com.mybatisflex.core.keygen.MybatisKeyGeneratorUtil;
|
||||||
import com.mybatisflex.core.keygen.RowKeyGenerator;
|
import com.mybatisflex.core.keygen.RowKeyGenerator;
|
||||||
|
import com.mybatisflex.core.mybatis.binding.FlexMapperRegistry;
|
||||||
import com.mybatisflex.core.mybatis.executor.FlexBatchExecutor;
|
import com.mybatisflex.core.mybatis.executor.FlexBatchExecutor;
|
||||||
import com.mybatisflex.core.mybatis.executor.FlexReuseExecutor;
|
import com.mybatisflex.core.mybatis.executor.FlexReuseExecutor;
|
||||||
import com.mybatisflex.core.mybatis.executor.FlexSimpleExecutor;
|
import com.mybatisflex.core.mybatis.executor.FlexSimpleExecutor;
|
||||||
import com.mybatisflex.core.table.TableInfo;
|
import com.mybatisflex.core.table.TableInfo;
|
||||||
import com.mybatisflex.core.table.TableInfoFactory;
|
import com.mybatisflex.core.table.TableInfoFactory;
|
||||||
|
import com.mybatisflex.core.util.MapUtil;
|
||||||
import com.mybatisflex.core.util.StringUtil;
|
import com.mybatisflex.core.util.StringUtil;
|
||||||
|
import org.apache.ibatis.binding.MapperRegistry;
|
||||||
import org.apache.ibatis.executor.CachingExecutor;
|
import org.apache.ibatis.executor.CachingExecutor;
|
||||||
import org.apache.ibatis.executor.Executor;
|
import org.apache.ibatis.executor.Executor;
|
||||||
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
import org.apache.ibatis.executor.keygen.KeyGenerator;
|
||||||
@ -42,10 +45,8 @@ import org.apache.ibatis.mapping.MappedStatement;
|
|||||||
import org.apache.ibatis.mapping.ResultMap;
|
import org.apache.ibatis.mapping.ResultMap;
|
||||||
import org.apache.ibatis.session.*;
|
import org.apache.ibatis.session.*;
|
||||||
import org.apache.ibatis.transaction.Transaction;
|
import org.apache.ibatis.transaction.Transaction;
|
||||||
import com.mybatisflex.core.util.MapUtil;
|
|
||||||
|
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -59,6 +60,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
public class FlexConfiguration extends Configuration {
|
public class FlexConfiguration extends Configuration {
|
||||||
|
|
||||||
private static final Map<String, MappedStatement> dynamicMappedStatementCache = new ConcurrentHashMap<>();
|
private static final Map<String, MappedStatement> dynamicMappedStatementCache = new ConcurrentHashMap<>();
|
||||||
|
private final MapperRegistry mapperRegistry = new FlexMapperRegistry(this);
|
||||||
|
|
||||||
public FlexConfiguration() {
|
public FlexConfiguration() {
|
||||||
setObjectWrapperFactory(new FlexWrapperFactory());
|
setObjectWrapperFactory(new FlexWrapperFactory());
|
||||||
@ -345,17 +347,35 @@ public class FlexConfiguration extends Configuration {
|
|||||||
|
|
||||||
//不支持泛型类添加
|
//不支持泛型类添加
|
||||||
if (!isGenericInterface) {
|
if (!isGenericInterface) {
|
||||||
super.addMapper(type);
|
mapperRegistry.addMapper(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
// @SuppressWarnings("unchecked")
|
||||||
@Override
|
// @Override
|
||||||
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
|
// public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
|
||||||
T mapper = super.getMapper(type, sqlSession);
|
// T mapper = super.getMapper(type, sqlSession);
|
||||||
return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}
|
// return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}
|
||||||
, new MapperInvocationHandler(mapper, environment.getDataSource()));
|
// , new MapperInvocationHandler(mapper, environment.getDataSource()));
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
public void addMappers(String packageName, Class<?> superType) {
|
||||||
|
mapperRegistry.addMappers(packageName, superType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addMappers(String packageName) {
|
||||||
|
mapperRegistry.addMappers(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
|
||||||
|
return mapperRegistry.getMapper(type, sqlSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasMapper(Class<?> type) {
|
||||||
|
return mapperRegistry.hasMapper(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ public class MappedStatementTypes {
|
|||||||
private MappedStatementTypes() {
|
private MappedStatementTypes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ThreadLocal<Class<?>> currentTypeTL = new ThreadLocal<>();
|
private static final ThreadLocal<Class<?>> currentTypeTL = new ThreadLocal<>();
|
||||||
|
|
||||||
public static void setCurrentType(Class<?> type) {
|
public static void setCurrentType(Class<?> type) {
|
||||||
currentTypeTL.set(type);
|
currentTypeTL.set(type);
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.mybatisflex.core.mybatis;
|
package com.mybatisflex.core.mybatis.binding;
|
||||||
|
|
||||||
import com.mybatisflex.annotation.UseDataSource;
|
import com.mybatisflex.annotation.UseDataSource;
|
||||||
import com.mybatisflex.core.FlexGlobalConfig;
|
import com.mybatisflex.core.FlexGlobalConfig;
|
||||||
@ -21,38 +21,38 @@ import com.mybatisflex.core.datasource.DataSourceKey;
|
|||||||
import com.mybatisflex.core.datasource.FlexDataSource;
|
import com.mybatisflex.core.datasource.FlexDataSource;
|
||||||
import com.mybatisflex.core.dialect.DbType;
|
import com.mybatisflex.core.dialect.DbType;
|
||||||
import com.mybatisflex.core.dialect.DialectFactory;
|
import com.mybatisflex.core.dialect.DialectFactory;
|
||||||
|
import com.mybatisflex.core.mybatis.FlexConfiguration;
|
||||||
import com.mybatisflex.core.row.RowMapper;
|
import com.mybatisflex.core.row.RowMapper;
|
||||||
import com.mybatisflex.core.table.TableInfo;
|
import com.mybatisflex.core.table.TableInfo;
|
||||||
import com.mybatisflex.core.table.TableInfoFactory;
|
import com.mybatisflex.core.table.TableInfoFactory;
|
||||||
|
import com.mybatisflex.core.util.MapUtil;
|
||||||
import com.mybatisflex.core.util.StringUtil;
|
import com.mybatisflex.core.util.StringUtil;
|
||||||
import org.apache.ibatis.reflection.ExceptionUtil;
|
import org.apache.ibatis.reflection.ExceptionUtil;
|
||||||
|
import org.apache.ibatis.session.SqlSession;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
public class FlexMapperProxy<T> extends MybatisMapperProxy<T> {
|
||||||
* @author michael
|
private static final String NULL_KEY = "@NK";
|
||||||
* @author norkts
|
private static final Map<Method, String> methodDsKeyCache = new ConcurrentHashMap<>();
|
||||||
*/
|
|
||||||
public class MapperInvocationHandler implements InvocationHandler {
|
|
||||||
|
|
||||||
private final Object mapper;
|
|
||||||
private final FlexDataSource dataSource;
|
private final FlexDataSource dataSource;
|
||||||
|
|
||||||
public MapperInvocationHandler(Object mapper, DataSource dataSource) {
|
public FlexMapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache,
|
||||||
this.mapper = mapper;
|
FlexConfiguration configuration) {
|
||||||
if (dataSource instanceof FlexDataSource) {
|
super(sqlSession, mapperInterface, methodCache);
|
||||||
this.dataSource = (FlexDataSource) dataSource;
|
this.dataSource = (FlexDataSource) configuration.getEnvironment().getDataSource();
|
||||||
} else {
|
|
||||||
this.dataSource = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
if (Object.class.equals(method.getDeclaringClass())) {
|
||||||
|
return method.invoke(this, args);
|
||||||
|
}
|
||||||
|
|
||||||
boolean needClearDsKey = false;
|
boolean needClearDsKey = false;
|
||||||
boolean needClearDbType = false;
|
boolean needClearDbType = false;
|
||||||
try {
|
try {
|
||||||
@ -100,7 +100,8 @@ public class MapperInvocationHandler implements InvocationHandler {
|
|||||||
DialectFactory.setHintDbType(dbType);
|
DialectFactory.setHintDbType(dbType);
|
||||||
needClearDbType = true;
|
needClearDbType = true;
|
||||||
}
|
}
|
||||||
return method.invoke(mapper, args);
|
// return method.invoke(mapper, args);
|
||||||
|
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw ExceptionUtil.unwrapThrowable(e);
|
throw ExceptionUtil.unwrapThrowable(e);
|
||||||
} finally {
|
} finally {
|
||||||
@ -115,30 +116,33 @@ public class MapperInvocationHandler implements InvocationHandler {
|
|||||||
|
|
||||||
|
|
||||||
private static String getConfigDataSourceKey(Method method, Object proxy) {
|
private static String getConfigDataSourceKey(Method method, Object proxy) {
|
||||||
UseDataSource useDataSource = method.getAnnotation(UseDataSource.class);
|
String result = MapUtil.computeIfAbsent(methodDsKeyCache, method, method1 -> {
|
||||||
if (useDataSource != null && StringUtil.isNotBlank(useDataSource.value())) {
|
UseDataSource useDataSource = method1.getAnnotation(UseDataSource.class);
|
||||||
return useDataSource.value();
|
if (useDataSource != null && StringUtil.isNotBlank(useDataSource.value())) {
|
||||||
}
|
return useDataSource.value();
|
||||||
|
|
||||||
Class<?>[] interfaces = proxy.getClass().getInterfaces();
|
|
||||||
for (Class<?> anInterface : interfaces) {
|
|
||||||
UseDataSource annotation = anInterface.getAnnotation(UseDataSource.class);
|
|
||||||
if (annotation != null) {
|
|
||||||
return annotation.value();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (interfaces[0] != RowMapper.class) {
|
Class<?>[] interfaces = proxy.getClass().getInterfaces();
|
||||||
TableInfo tableInfo = TableInfoFactory.ofMapperClass(interfaces[0]);
|
for (Class<?> anInterface : interfaces) {
|
||||||
if (tableInfo != null) {
|
UseDataSource annotation = anInterface.getAnnotation(UseDataSource.class);
|
||||||
String dataSourceKey = tableInfo.getDataSource();
|
if (annotation != null) {
|
||||||
if (StringUtil.isNotBlank(dataSourceKey)) {
|
return annotation.value();
|
||||||
return dataSourceKey;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return null;
|
if (interfaces[0] != RowMapper.class) {
|
||||||
|
TableInfo tableInfo = TableInfoFactory.ofMapperClass(interfaces[0]);
|
||||||
|
if (tableInfo != null) {
|
||||||
|
String dataSourceKey = tableInfo.getDataSource();
|
||||||
|
if (StringUtil.isNotBlank(dataSourceKey)) {
|
||||||
|
return dataSourceKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL_KEY;
|
||||||
|
});
|
||||||
|
|
||||||
|
return NULL_KEY.equals(result) ? null : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009-2022 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.mybatisflex.core.mybatis.binding;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.mybatis.FlexConfiguration;
|
||||||
|
import org.apache.ibatis.session.SqlSession;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lasse Voss
|
||||||
|
* @author Michael Yang
|
||||||
|
*/
|
||||||
|
public class FlexMapperProxyFactory<T> {
|
||||||
|
|
||||||
|
private final Class<T> mapperInterface;
|
||||||
|
private final Map<Method, MybatisMapperProxy.MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public FlexMapperProxyFactory(Class<T> mapperInterface) {
|
||||||
|
this.mapperInterface = mapperInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<T> getMapperInterface() {
|
||||||
|
return mapperInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Method, MybatisMapperProxy.MapperMethodInvoker> getMethodCache() {
|
||||||
|
return methodCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected T newInstance(FlexMapperProxy<T> mapperProxy) {
|
||||||
|
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T newInstance(SqlSession sqlSession, FlexConfiguration configuration) {
|
||||||
|
final FlexMapperProxy<T> mapperProxy = new FlexMapperProxy<>(sqlSession, mapperInterface, methodCache, configuration);
|
||||||
|
return newInstance(mapperProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009-2023 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.mybatisflex.core.mybatis.binding;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.mybatis.FlexConfiguration;
|
||||||
|
import org.apache.ibatis.binding.BindingException;
|
||||||
|
import org.apache.ibatis.binding.MapperRegistry;
|
||||||
|
import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder;
|
||||||
|
import org.apache.ibatis.io.ResolverUtil;
|
||||||
|
import org.apache.ibatis.session.SqlSession;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class FlexMapperRegistry extends MapperRegistry {
|
||||||
|
private final FlexConfiguration config;
|
||||||
|
private final Map<Class<?>, FlexMapperProxyFactory<?>> knownMappers = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public FlexMapperRegistry(FlexConfiguration config) {
|
||||||
|
super(config);
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
|
||||||
|
final FlexMapperProxyFactory<T> mapperProxyFactory = (FlexMapperProxyFactory<T>) knownMappers.get(type);
|
||||||
|
if (mapperProxyFactory == null) {
|
||||||
|
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return mapperProxyFactory.newInstance(sqlSession, config);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> boolean hasMapper(Class<T> type) {
|
||||||
|
return knownMappers.containsKey(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void addMapper(Class<T> type) {
|
||||||
|
if (type.isInterface()) {
|
||||||
|
if (hasMapper(type)) {
|
||||||
|
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
|
||||||
|
}
|
||||||
|
boolean loadCompleted = false;
|
||||||
|
try {
|
||||||
|
knownMappers.put(type, new FlexMapperProxyFactory<>(type));
|
||||||
|
// It's important that the type is added before the parser is run
|
||||||
|
// otherwise the binding may automatically be attempted by the
|
||||||
|
// mapper parser. If the type is already known, it won't try.
|
||||||
|
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
|
||||||
|
parser.parse();
|
||||||
|
loadCompleted = true;
|
||||||
|
} finally {
|
||||||
|
if (!loadCompleted) {
|
||||||
|
knownMappers.remove(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the mappers.
|
||||||
|
*
|
||||||
|
* @return the mappers
|
||||||
|
* @since 3.2.2
|
||||||
|
*/
|
||||||
|
public Collection<Class<?>> getMappers() {
|
||||||
|
return Collections.unmodifiableCollection(knownMappers.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the mappers.
|
||||||
|
*
|
||||||
|
* @param packageName the package name
|
||||||
|
* @param superType the super type
|
||||||
|
* @since 3.2.2
|
||||||
|
*/
|
||||||
|
public void addMappers(String packageName, Class<?> superType) {
|
||||||
|
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
|
||||||
|
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
|
||||||
|
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
|
||||||
|
for (Class<?> mapperClass : mapperSet) {
|
||||||
|
addMapper(mapperClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the mappers.
|
||||||
|
*
|
||||||
|
* @param packageName the package name
|
||||||
|
* @since 3.2.2
|
||||||
|
*/
|
||||||
|
public void addMappers(String packageName) {
|
||||||
|
addMappers(packageName, Object.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009-2023 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.mybatisflex.core.mybatis.binding;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.util.MapUtil;
|
||||||
|
import org.apache.ibatis.binding.MapperMethod;
|
||||||
|
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.MethodHandles.Lookup;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Clinton Begin
|
||||||
|
* @author Eduardo Macarron
|
||||||
|
* @author Michael Yang
|
||||||
|
* <p>
|
||||||
|
* 参考 MapperProxy<T> 并开放 MapperMethodInvoker,方便子类代理
|
||||||
|
*/
|
||||||
|
public class MybatisMapperProxy<T> implements InvocationHandler, Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -4724728412955527868L;
|
||||||
|
private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
|
||||||
|
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
|
||||||
|
private static final Constructor<Lookup> lookupConstructor;
|
||||||
|
private static final Method privateLookupInMethod;
|
||||||
|
protected final SqlSession sqlSession;
|
||||||
|
private final Class<T> mapperInterface;
|
||||||
|
private final Map<Method, MapperMethodInvoker> methodCache;
|
||||||
|
|
||||||
|
public MybatisMapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
|
||||||
|
this.sqlSession = sqlSession;
|
||||||
|
this.mapperInterface = mapperInterface;
|
||||||
|
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<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 (Exception e) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
throw ExceptionUtil.unwrapThrowable(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
|
||||||
|
try {
|
||||||
|
return MapUtil.computeIfAbsent(methodCache, method, m -> {
|
||||||
|
if (!m.isDefault()) {
|
||||||
|
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (privateLookupInMethod == null) {
|
||||||
|
return new DefaultMethodInvoker(getMethodHandleJava8(method));
|
||||||
|
}
|
||||||
|
return new DefaultMethodInvoker(getMethodHandleJava9(method));
|
||||||
|
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
|
||||||
|
| NoSuchMethodException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (RuntimeException re) {
|
||||||
|
Throwable cause = re.getCause();
|
||||||
|
throw cause == null ? re : cause;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodHandle getMethodHandleJava9(Method method)
|
||||||
|
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
final Class<?> declaringClass = method.getDeclaringClass();
|
||||||
|
return ((Lookup) privateLookupInMethod.invoke(null, declaringClass, MethodHandles.lookup())).findSpecial(
|
||||||
|
declaringClass, method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()),
|
||||||
|
declaringClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodHandle getMethodHandleJava8(Method method)
|
||||||
|
throws IllegalAccessException, InstantiationException, InvocationTargetException {
|
||||||
|
final Class<?> declaringClass = method.getDeclaringClass();
|
||||||
|
return lookupConstructor.newInstance(declaringClass, ALLOWED_MODES).unreflectSpecial(method, declaringClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MapperMethodInvoker {
|
||||||
|
Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PlainMethodInvoker implements MapperMethodInvoker {
|
||||||
|
private final MapperMethod mapperMethod;
|
||||||
|
|
||||||
|
public PlainMethodInvoker(MapperMethod mapperMethod) {
|
||||||
|
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) {
|
||||||
|
this.methodHandle = methodHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
|
||||||
|
return methodHandle.bindTo(proxy).invokeWithArguments(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user