diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java index 6b41ee739..6803250dc 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java @@ -25,4 +25,10 @@ public @interface DataSource * 切换数据源名称 */ public DataSourceType value() default DataSourceType.MASTER; + + /** + * 支持通过el表达式从参数获取切换名称 + * example: elValue="#field" (从参数获取) 或 "#pojo.field" (从对象获取) + */ + String elValue() default ""; } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java index 4b5341d19..562bcb163 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java @@ -1,5 +1,9 @@ package com.ruoyi.common.enums; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + /** * 数据源 * @@ -15,5 +19,16 @@ public enum DataSourceType /** * 从库 */ - SLAVE + SLAVE; + + private static final Set DATA_SOURCE_TYPE_SET; + + static { + DATA_SOURCE_TYPE_SET = Arrays.stream(DataSourceType.values()) + .map(DataSourceType::name).collect(Collectors.toSet()); + } + + public static boolean contains(String sourceType){ + return DATA_SOURCE_TYPE_SET.contains(sourceType); + } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/el/ExpressionEvaluator.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/el/ExpressionEvaluator.java new file mode 100644 index 000000000..6cbec7847 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/el/ExpressionEvaluator.java @@ -0,0 +1,51 @@ +package com.ruoyi.common.utils.el; + +import org.springframework.aop.support.AopUtils; +import org.springframework.context.expression.AnnotatedElementKey; +import org.springframework.context.expression.CachedExpressionEvaluator; +import org.springframework.context.expression.MethodBasedEvaluationContext; +import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author : mudi + * @date : Created in 2021/12/3 21:02 + * @description : + * @modified By : + */ +public class ExpressionEvaluator extends CachedExpressionEvaluator { + private final ParameterNameDiscoverer paramNameDiscoverer = new DefaultParameterNameDiscoverer(); + private final Map conditionCache = new ConcurrentHashMap<>(64); + private final Map targetMethodCache = new ConcurrentHashMap<>(64); + + + public EvaluationContext createEvaluationContext(Object object, Class targetClass, Method method, Object[] args) { + Method targetMethod = getTargetMethod(targetClass, method); + ExpressionRootObject root = new ExpressionRootObject(object, args); + return new MethodBasedEvaluationContext(root, targetMethod, args, this.paramNameDiscoverer); + } + + + public T condition(String conditionExpression, AnnotatedElementKey elementKey, EvaluationContext evalContext, Class clazz) { + return getExpression(this.conditionCache, elementKey, conditionExpression).getValue(evalContext, clazz); + } + + private Method getTargetMethod(Class targetClass, Method method) { + AnnotatedElementKey methodKey = new AnnotatedElementKey(method, targetClass); + Method targetMethod = this.targetMethodCache.get(methodKey); + if (targetMethod == null) { + targetMethod = AopUtils.getMostSpecificMethod(method, targetClass); + if (targetMethod == null) { + targetMethod = method; + } + this.targetMethodCache.put(methodKey, targetMethod); + } + return targetMethod; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/el/ExpressionRootObject.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/el/ExpressionRootObject.java new file mode 100644 index 000000000..2432131cb --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/el/ExpressionRootObject.java @@ -0,0 +1,25 @@ +package com.ruoyi.common.utils.el; + +/** + * @author : mudi + * @date : Created in 2021/12/3 21:03 + * @description : + * @modified By : + */ +public class ExpressionRootObject { + private final Object object; + private final Object[] args; + + public ExpressionRootObject(Object object, Object[] args) { + this.object = object; + this.args = args; + } + + public Object getObject() { + return object; + } + + public Object[] getArgs() { + return args; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java index f72b8051b..a00b347fc 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java @@ -1,6 +1,10 @@ package com.ruoyi.framework.aspectj; import java.util.Objects; + +import com.ruoyi.common.enums.DataSourceType; +import com.ruoyi.common.utils.el.ExpressionEvaluator; +import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -8,8 +12,10 @@ import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.expression.AnnotatedElementKey; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.Order; +import org.springframework.expression.EvaluationContext; import org.springframework.stereotype.Component; import com.ruoyi.common.annotation.DataSource; import com.ruoyi.common.config.datasource.DynamicDataSourceContextHolder; @@ -27,6 +33,8 @@ public class DataSourceAspect { protected Logger logger = LoggerFactory.getLogger(getClass()); + private ExpressionEvaluator evaluator = new ExpressionEvaluator<>(); + @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)" + "|| @within(com.ruoyi.common.annotation.DataSource)") public void dsPointCut() @@ -34,6 +42,18 @@ public class DataSourceAspect } + private String getValueByEl(DataSource dataSource, JoinPoint joinPoint) + { + if (Objects.equals(dataSource.elValue(), "")) + { + return null; + } + + EvaluationContext evaluationContext = evaluator.createEvaluationContext(joinPoint.getTarget(), joinPoint.getTarget().getClass(), ((MethodSignature) joinPoint.getSignature()).getMethod(), joinPoint.getArgs()); + AnnotatedElementKey methodKey = new AnnotatedElementKey(((MethodSignature) joinPoint.getSignature()).getMethod(), joinPoint.getTarget().getClass()); + return evaluator.condition(dataSource.elValue(), methodKey, evaluationContext, String.class); + } + @Around("dsPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { @@ -41,7 +61,9 @@ public class DataSourceAspect if (StringUtils.isNotNull(dataSource)) { - DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); + String elValue = getValueByEl(dataSource, point); + String value = DataSourceType.contains(elValue) ? elValue : dataSource.value().name(); + DynamicDataSourceContextHolder.setDataSourceType(value); } try