mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-07 00:58:24 +08:00
!507 动态数据源 @UseDataSource 的value值扩展支持表达式解析处理
Merge pull request !507 from Alay/main
This commit is contained in:
commit
a7cf8f2713
@ -105,6 +105,89 @@ interface AccountMapper extends BaseMapper{
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`@UseDataSource(value = "#my_expression")`扩展数据源切换,表达式动态读取 key 值
|
||||||
|
|
||||||
|
- 自定义表达式扩展,对接口 DataSourceProcessor 进行实现,推荐约定动态表达式以 `#` 作为起始标识(当然并不强制做要求)
|
||||||
|
```java
|
||||||
|
// 自定义基于Session的表达式取值示例
|
||||||
|
public class InSessionDataSourceProcessor implements DataSourceProcessor {
|
||||||
|
private static final String SESSION_PREFIX = "#session";
|
||||||
|
@Override
|
||||||
|
public String process(String dataSourceKey, Object mapper, Method method, Object[] arguments) {
|
||||||
|
if (StrUtil.isBlank(dataSourceKey)) return null;
|
||||||
|
if (!dataSourceKey.startsWith(SESSION_PREFIX)) return null;
|
||||||
|
|
||||||
|
HttpServletRequest request = RequestContextHolder.get.....;
|
||||||
|
if (null==request) return null;
|
||||||
|
return request.getSession().getAttribute(dataSourceKey.substring(9)).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 内置取值表达式取值解析处理器,作为示例参考
|
||||||
|
- `DelegatingDataSourceProcessor`,对`DataSourceProcessor`结构进行扩大和增强的委托类,多处理器推荐使用该委托类进行注入,注意委托类内使用`List`委托管理多个解析处理器,执行时有先后顺序。
|
||||||
|
- `ParamIndexDataSourceProcessor`,针对简单参数快速取值。
|
||||||
|
- `SpelExpressionDataSourceProcessor`,`Spring` 模式下 `SPEL` 表达式取值。
|
||||||
|
- 注入动态数据源取值处理器
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 注入顺序既为执行时的先后顺序(前面的处理器一旦正确处理,将不会再往下传递处理)
|
||||||
|
DelegatingDataSourceProcessor dataSourceProcessor = DelegatingDataSourceProcessor.with(
|
||||||
|
// 参数索引快速取值的
|
||||||
|
new ParamIndexDataSourceProcessor(),
|
||||||
|
// Spel 表达式的
|
||||||
|
new SpelExpressionDataSourceProcessor(),
|
||||||
|
new .....等多个自行扩展表达式()
|
||||||
|
);
|
||||||
|
// 实例 setter 赋值
|
||||||
|
DataSourceManager.setDataSourceProcessor(dataSourceProcessor);
|
||||||
|
```
|
||||||
|
|
||||||
|
- 应用示例:
|
||||||
|
```java
|
||||||
|
@Mapper
|
||||||
|
public interface MyMapper extends BaseMapper<MyEntity >{
|
||||||
|
// 取值第一个参数的值(arg0的值)
|
||||||
|
@UseDataSource(value = "#first")
|
||||||
|
MyEntity testFirst(String arg0, String arg1, String arg2);
|
||||||
|
|
||||||
|
// 取值索引1(第二个参数 arg1 )的值
|
||||||
|
@UseDataSource(value = "#index1")
|
||||||
|
MyEntity testIndex(String arg0, String arg1, String arg2);
|
||||||
|
|
||||||
|
// 取值最后一个参数的值(arg3的值)
|
||||||
|
@UseDataSource(value = "#last")
|
||||||
|
MyEntity testLast(String arg0, String arg1, String arg2, String arg3);
|
||||||
|
|
||||||
|
// Spel 表达式取值数据源
|
||||||
|
@UseDataSource(value = "#condition.getDsId()")
|
||||||
|
MyEntity testSpel(MyDatasourceCondition condition);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- 调用
|
||||||
|
```java
|
||||||
|
public void test(){
|
||||||
|
// mapper中的注解 @UseDataSource(value = "#first")
|
||||||
|
myMapper.testFirst("my-dskey1", "arg1", "arg2");
|
||||||
|
|
||||||
|
// 取值索引1(第二个参数)的值;mapper中的注解 @UseDataSource(value = "#index1")
|
||||||
|
myMapper.testIndex("arg0", "my-dskey2", "arg2");
|
||||||
|
|
||||||
|
// mapper中的注解 @UseDataSource(value = "#last")
|
||||||
|
myMapper.testLast("arg0", "arg1", "arg2","my-dskey4");
|
||||||
|
|
||||||
|
MyDatasourceCondition condition = new MyDatasourceCondition();
|
||||||
|
condition.setDsId("my-dskey5");
|
||||||
|
// mapper中的注解 @UseDataSource(value = "#condition.getDsId()")
|
||||||
|
myMapper.testSpel(condition);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **注意:**
|
||||||
|
- 1:使用区分`Spring模式`和`非Spring`模式,`Spring`模式下,处理逻辑`DataSourceInterceptor`优先级高于 `FlexMapperProxy`,
|
||||||
|
所以`Spring模式下仅 DataSourceInterceptor 生效`(切面生效的前提下)。`非Spring` 模式下,`仅支持注解使用到 Mapper(Dao层)`,
|
||||||
|
使用到其他层(如Service层)不支持注解解析。
|
||||||
|
- 2:`Spring模式`下,不区分使用到程序的层(Controller、Service、Dao层都支持),下层控制粒度细上层控制粒粗,使用时根据需要进行灵活应用。
|
||||||
|
|
||||||
|
|
||||||
`@Table(dataSource="dataSourceName")` 示例:
|
`@Table(dataSource="dataSourceName")` 示例:
|
||||||
|
|||||||
@ -84,6 +84,12 @@ public class DataSourceKey {
|
|||||||
lookup = threadLocal;
|
lookup = threadLocal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String processDataSourceKey(String dataSourceKey, Object targetOrProxy, Method method, Object[] arguments) {
|
||||||
|
String dsKey = DataSourceManager.processDataSourceKey(dataSourceKey, targetOrProxy, method, arguments);
|
||||||
|
return dsKey != null ? dsKey : dataSourceKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String getShardingDsKey(String dataSource, Object mapper, Method method, Object[] args) {
|
public static String getShardingDsKey(String dataSource, Object mapper, Method method, Object[] args) {
|
||||||
String shardingDsKey = DataSourceManager.getShardingDsKey(dataSource, mapper, method, args);
|
String shardingDsKey = DataSourceManager.getShardingDsKey(dataSource, mapper, method, args);
|
||||||
return shardingDsKey != null ? shardingDsKey : dataSource;
|
return shardingDsKey != null ? shardingDsKey : dataSource;
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.mybatisflex.core.datasource;
|
package com.mybatisflex.core.datasource;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.datasource.processor.DataSourceProcessor;
|
||||||
import com.mybatisflex.core.exception.FlexExceptions;
|
import com.mybatisflex.core.exception.FlexExceptions;
|
||||||
import com.mybatisflex.core.util.ClassUtil;
|
import com.mybatisflex.core.util.ClassUtil;
|
||||||
import org.apache.ibatis.logging.LogFactory;
|
import org.apache.ibatis.logging.LogFactory;
|
||||||
@ -37,6 +38,19 @@ public class DataSourceManager {
|
|||||||
DataSourceManager.decipher = decipher;
|
DataSourceManager.decipher = decipher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态数据源key取值处理
|
||||||
|
*/
|
||||||
|
private static DataSourceProcessor dataSourceProcessor;
|
||||||
|
|
||||||
|
public static DataSourceProcessor getDataSourceProcessor() {
|
||||||
|
return dataSourceProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setDataSourceProcessor(DataSourceProcessor dataSourceProcessor) {
|
||||||
|
DataSourceManager.dataSourceProcessor = dataSourceProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
private static DataSourceShardingStrategy dataSourceShardingStrategy;
|
private static DataSourceShardingStrategy dataSourceShardingStrategy;
|
||||||
|
|
||||||
public static DataSourceShardingStrategy getDataSourceShardingStrategy() {
|
public static DataSourceShardingStrategy getDataSourceShardingStrategy() {
|
||||||
@ -95,6 +109,11 @@ public class DataSourceManager {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String processDataSourceKey(String dataSourceKey, Object targetOrProxy, Method method, Object[] arguments) {
|
||||||
|
// 如果没有配置 DataSourceProcessor 实例,则不做处理,返回原始值
|
||||||
|
return dataSourceProcessor == null ? dataSourceKey : dataSourceProcessor.process(dataSourceKey, targetOrProxy, method, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static String getShardingDsKey(String dataSource, Object mapper, Method method, Object[] args) {
|
static String getShardingDsKey(String dataSource, Object mapper, Method method, Object[] args) {
|
||||||
return dataSourceShardingStrategy != null ? dataSourceShardingStrategy.doSharding(dataSource, mapper, method, args) : null;
|
return dataSourceShardingStrategy != null ? dataSourceShardingStrategy.doSharding(dataSource, mapper, method, args) : null;
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
package com.mybatisflex.core.datasource.processor;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.mybatis.binding.FlexMapperProxy;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态数据源 @UseDataSource的value值解析处理器(如表达式解析取值等),使用时推荐使用 DelegatingDataSourceProcessor{@link DelegatingDataSourceProcessor} 实例化
|
||||||
|
* 对动态数据源注解@UseDataSource 增强处理{@link com.mybatisflex.annotation.UseDataSource}
|
||||||
|
* <p>
|
||||||
|
* 使用区分Spring模式 和 非Spring模式,Spring模式下,代理处理逻辑 DataSourceInterceptor{@link com.mybatisflex.spring.datasource.DataSourceInterceptor} 优先级高于 FlexMapperProxy{@link com.mybatisflex.core.mybatis.binding.FlexMapperProxy} ;
|
||||||
|
* 所以Spring模式下仅 DataSourceInterceptor 生效(切面生效的前提下)。非Spring 模式下,仅支持注解使用到 Mapper(Dao层),使用到其他层(如Service层)不支持注解解析。
|
||||||
|
* <p>
|
||||||
|
* Spring模式下,不区分使用到程序的层(Controller、Service、Dao层都支持),下层控制粒度细上层控制粒粗,使用时根据需要进行灵活应用。
|
||||||
|
*
|
||||||
|
* @author Alay
|
||||||
|
* @since 2024-12-07 15:34
|
||||||
|
*/
|
||||||
|
public interface DataSourceProcessor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据源key解析扩展
|
||||||
|
*
|
||||||
|
* @param dataSourceKey 注解UseDataSource的value 值,调用process时不会为null,可能为空字符{@link FlexMapperProxy#invoke(Object, Method, Object[])}And{@link com.mybatisflex.spring.datasource.DataSourceInterceptor#getDataSourceKey(Object, Method, Object[])}
|
||||||
|
* @param targetOrProxy AOP对象this或Mapper代理对象(当注解@UseDataSource使用到Mapper上时为proxy)
|
||||||
|
* @param method Mapper当前执行的方法函数
|
||||||
|
* @param arguments Mapper当前执行的函数参数
|
||||||
|
* @return 数据源名称(可能为null 为 null 时表示不符合当前处理器的处理)
|
||||||
|
*/
|
||||||
|
String process(String dataSourceKey, Object targetOrProxy, Method method, Object[] arguments);
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
package com.mybatisflex.core.datasource.processor;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.exception.FlexAssert;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DataSourceProcessor 委托扩展类,对 DataSourceProcessor 结构进行扩大和增强
|
||||||
|
* 如果多个实例化,建议通过 DelegatingDataSourceProcessor.with(多个解析处理器实例) 方式进行实例化。
|
||||||
|
* 需要注意的是委托解析处理器之间有先后顺序,一旦排列前面的解析处理器正常处理后,将直接返回处理值,不再往下传递处理
|
||||||
|
*
|
||||||
|
* @author Alay
|
||||||
|
* @since 2024-12-07 15:38
|
||||||
|
*/
|
||||||
|
public class DelegatingDataSourceProcessor implements DataSourceProcessor {
|
||||||
|
/**
|
||||||
|
* 多个处理器委托集合(使用时请注意 DataSourceProcessor 的顺序)
|
||||||
|
*/
|
||||||
|
private final List<DataSourceProcessor> delegates;
|
||||||
|
|
||||||
|
|
||||||
|
private DelegatingDataSourceProcessor(List<DataSourceProcessor> delegates) {
|
||||||
|
this.delegates = delegates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param processors 使用时请注意 DataSourceProcessor 的顺序
|
||||||
|
*/
|
||||||
|
public static DelegatingDataSourceProcessor with(DataSourceProcessor... processors) {
|
||||||
|
FlexAssert.notEmpty(processors, "datasource processors");
|
||||||
|
List<DataSourceProcessor> dataSourceProcessors = new ArrayList<>(Arrays.asList(processors));
|
||||||
|
return new DelegatingDataSourceProcessor(dataSourceProcessors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param processors 使用时请注意 DataSourceProcessor 的顺序
|
||||||
|
*/
|
||||||
|
public static DelegatingDataSourceProcessor with(List<DataSourceProcessor> processors) {
|
||||||
|
FlexAssert.notEmpty(processors, "datasource processors");
|
||||||
|
return new DelegatingDataSourceProcessor(processors);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String process(String dataSourceKey, Object mapper, Method method, Object[] arguments) {
|
||||||
|
for (DataSourceProcessor delegate : delegates) {
|
||||||
|
// 使用时请注意 DataSourceProcessor 的顺序,一旦匹配到处理器将进行终止处理链,并返回当前处理结果
|
||||||
|
String returnKey = delegate.process(dataSourceKey, mapper, method, arguments);
|
||||||
|
if (null != returnKey) return returnKey;
|
||||||
|
}
|
||||||
|
// 无可用的处理器策略,返回原始值
|
||||||
|
return dataSourceKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
package com.mybatisflex.core.datasource.processor;
|
||||||
|
|
||||||
|
import com.mybatisflex.processor.util.StrUtil;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数索引中取出数据源名称(针对简单类型参数快速解析读取)
|
||||||
|
*
|
||||||
|
* @author Alay
|
||||||
|
* @since 2024-12-07 15:43
|
||||||
|
*/
|
||||||
|
public class ParamIndexDataSourceProcessor implements DataSourceProcessor {
|
||||||
|
private static final String NULL_STR = "null";
|
||||||
|
private static final String DYNAMIC_PREFIX = "#";
|
||||||
|
private static final String INDEX_FIRST = "#first";
|
||||||
|
private static final String INDEX_LAST = "#last";
|
||||||
|
private static final String PARAM_INDEX = "#index";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若不符合处理逻辑将返回 null 值
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String process(String dataSourceKey, Object mapper, Method method, Object[] arguments) {
|
||||||
|
if (StrUtil.isBlank(dataSourceKey)) return null;
|
||||||
|
if (!dataSourceKey.startsWith(DYNAMIC_PREFIX)) return null;
|
||||||
|
// 无效的参数
|
||||||
|
if (arguments.length == 0) return null;
|
||||||
|
|
||||||
|
Integer index = null;
|
||||||
|
if (INDEX_FIRST.equals(dataSourceKey)) {
|
||||||
|
index = 0;
|
||||||
|
} else if (INDEX_LAST.equals(dataSourceKey)) {
|
||||||
|
index = arguments.length - 1;
|
||||||
|
} else if (dataSourceKey.startsWith(PARAM_INDEX)) {
|
||||||
|
index = parseIndex(dataSourceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有符合约定的格式输入,则会返回 null
|
||||||
|
if (null == index) return null;
|
||||||
|
// 参数输入不合法(索引参数大于参数索引数)
|
||||||
|
if (index >= arguments.length) return null;
|
||||||
|
|
||||||
|
// 参数中按照索引取出数值
|
||||||
|
String value = String.valueOf(arguments[index]);
|
||||||
|
if (StrUtil.isBlank(value) || NULL_STR.equals(value)) return null;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Integer parseIndex(String dsKey) {
|
||||||
|
// 参数索引
|
||||||
|
String indexStr = dsKey.substring(PARAM_INDEX.length());
|
||||||
|
if (indexStr.isEmpty()) return null;
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(indexStr);
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态数据源注解@UseDataSource 的扩展
|
||||||
|
*/
|
||||||
|
package com.mybatisflex.core.datasource.processor;
|
||||||
@ -26,6 +26,7 @@ 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.StringUtil;
|
import com.mybatisflex.core.util.StringUtil;
|
||||||
|
import com.mybatisflex.processor.util.StrUtil;
|
||||||
import org.apache.ibatis.reflection.ExceptionUtil;
|
import org.apache.ibatis.reflection.ExceptionUtil;
|
||||||
import org.apache.ibatis.session.SqlSession;
|
import org.apache.ibatis.session.SqlSession;
|
||||||
|
|
||||||
@ -58,10 +59,15 @@ public class FlexMapperProxy<T> extends MybatisMapperProxy<T> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (StringUtil.noText(finalDsKey)) {
|
if (StringUtil.noText(finalDsKey)) {
|
||||||
|
// Mapper 方法上获取 UseDataSource的value值
|
||||||
finalDsKey = getMethodDsKey(method, proxy);
|
finalDsKey = getMethodDsKey(method, proxy);
|
||||||
|
// 对数据源取值进行动态取值处理
|
||||||
|
if (!StrUtil.isBlank(finalDsKey)) {
|
||||||
|
finalDsKey = DataSourceKey.processDataSourceKey(finalDsKey, proxy, method, args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//通过自定义分配策略去获取最终的数据源
|
// 通过自定义分配策略去获取最终的数据源
|
||||||
finalDsKey = DataSourceKey.getShardingDsKey(finalDsKey, proxy, method, args);
|
finalDsKey = DataSourceKey.getShardingDsKey(finalDsKey, proxy, method, args);
|
||||||
|
|
||||||
if (StringUtil.hasText(finalDsKey) && !finalDsKey.equals(userDsKey)) {
|
if (StringUtil.hasText(finalDsKey) && !finalDsKey.equals(userDsKey)) {
|
||||||
|
|||||||
@ -19,6 +19,7 @@ package com.mybatisflex.spring.datasource;
|
|||||||
import com.mybatisflex.annotation.UseDataSource;
|
import com.mybatisflex.annotation.UseDataSource;
|
||||||
import com.mybatisflex.core.datasource.DataSourceKey;
|
import com.mybatisflex.core.datasource.DataSourceKey;
|
||||||
import com.mybatisflex.core.util.StringUtil;
|
import com.mybatisflex.core.util.StringUtil;
|
||||||
|
import com.mybatisflex.processor.util.StrUtil;
|
||||||
import org.aopalliance.intercept.MethodInterceptor;
|
import org.aopalliance.intercept.MethodInterceptor;
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
import org.springframework.core.MethodClassKey;
|
import org.springframework.core.MethodClassKey;
|
||||||
@ -44,7 +45,7 @@ public class DataSourceInterceptor implements MethodInterceptor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||||
String dsKey = getDataSourceKey(invocation.getMethod(), invocation.getThis().getClass());
|
String dsKey = getDataSourceKey(invocation.getThis(), invocation.getMethod(), invocation.getArguments());
|
||||||
if (StringUtil.noText(dsKey)) {
|
if (StringUtil.noText(dsKey)) {
|
||||||
return invocation.proceed();
|
return invocation.proceed();
|
||||||
}
|
}
|
||||||
@ -56,11 +57,15 @@ public class DataSourceInterceptor implements MethodInterceptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getDataSourceKey(Method method, Class<?> targetClass) {
|
private String getDataSourceKey(Object target, Method method, Object[] arguments) {
|
||||||
Object cacheKey = new MethodClassKey(method, targetClass);
|
Object cacheKey = new MethodClassKey(method, target.getClass());
|
||||||
String dsKey = this.dsCache.get(cacheKey);
|
String dsKey = this.dsCache.get(cacheKey);
|
||||||
if (dsKey == null) {
|
if (dsKey == null) {
|
||||||
dsKey = determineDataSourceKey(method, targetClass);
|
dsKey = determineDataSourceKey(method, target.getClass());
|
||||||
|
// 对数据源取值进行动态取值处理
|
||||||
|
if (!StrUtil.isBlank(dsKey)) {
|
||||||
|
dsKey = DataSourceKey.processDataSourceKey(dsKey, target, method, arguments);
|
||||||
|
}
|
||||||
this.dsCache.put(cacheKey, dsKey);
|
this.dsCache.put(cacheKey, dsKey);
|
||||||
}
|
}
|
||||||
return dsKey;
|
return dsKey;
|
||||||
|
|||||||
@ -0,0 +1,106 @@
|
|||||||
|
package com.mybatisflex.spring.datasource.processor;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.datasource.processor.DataSourceProcessor;
|
||||||
|
import com.mybatisflex.processor.util.StrUtil;
|
||||||
|
import org.springframework.context.expression.MethodBasedEvaluationContext;
|
||||||
|
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||||
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
|
import org.springframework.expression.BeanResolver;
|
||||||
|
import org.springframework.expression.ExpressionParser;
|
||||||
|
import org.springframework.expression.ParserContext;
|
||||||
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpEL表达式支持处理器
|
||||||
|
*
|
||||||
|
* @author Alay
|
||||||
|
* @since 2024-12-07 15:48
|
||||||
|
*/
|
||||||
|
public class SpelExpressionDataSourceProcessor implements DataSourceProcessor {
|
||||||
|
/**
|
||||||
|
* 动态表达式前缀
|
||||||
|
*/
|
||||||
|
private static final String DYNAMIC_PREFIX = "#";
|
||||||
|
/**
|
||||||
|
* 参数发现器
|
||||||
|
*/
|
||||||
|
private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
|
||||||
|
/**
|
||||||
|
* Express语法解析器
|
||||||
|
*/
|
||||||
|
private static final ExpressionParser PARSER = new SpelExpressionParser();
|
||||||
|
|
||||||
|
private BeanResolver beanResolver;
|
||||||
|
/**
|
||||||
|
* 解析上下文的模板 对于默认不设置的情况下,从参数中取值的方式 #param1
|
||||||
|
* 设置指定模板 ParserContext. TEMPLATE_EXPRESSION{@link ParserContext#TEMPLATE_EXPRESSION} 后的取值方式: #{#param1}
|
||||||
|
*/
|
||||||
|
private ParserContext parserContext = new ParserContext() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTemplate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getExpressionPrefix() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getExpressionSuffix() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String process(String dataSourceKey, Object mapper, Method method, Object[] arguments) {
|
||||||
|
if (StrUtil.isBlank(dataSourceKey)) return null;
|
||||||
|
if (!dataSourceKey.startsWith(DYNAMIC_PREFIX)) return null;
|
||||||
|
if (arguments.length == 0) return null;
|
||||||
|
|
||||||
|
RootObject rootObject = new RootObject(method, arguments, mapper);
|
||||||
|
StandardEvaluationContext context = new MethodBasedEvaluationContext(rootObject, method, arguments, NAME_DISCOVERER);
|
||||||
|
context.setBeanResolver(beanResolver);
|
||||||
|
final Object value = PARSER.parseExpression(dataSourceKey, parserContext).getValue(context);
|
||||||
|
return value == null ? null : value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setBeanResolver(BeanResolver beanResolver) {
|
||||||
|
this.beanResolver = beanResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParserContext(ParserContext parserContext) {
|
||||||
|
this.parserContext = parserContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RootObject {
|
||||||
|
private final Method method;
|
||||||
|
private final Object[] args;
|
||||||
|
private final Object target;
|
||||||
|
|
||||||
|
public RootObject(Method method, Object[] args, Object target) {
|
||||||
|
this.method = method;
|
||||||
|
this.args = args;
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Method getMethod() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object[] getArgs() {
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getTarget() {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态数据源注解@UseDataSource 的扩展
|
||||||
|
*/
|
||||||
|
package com.mybatisflex.spring.datasource.processor;
|
||||||
Loading…
x
Reference in New Issue
Block a user