说明
由于项目中某些场景用到动态表名,度娘之后发现很多案例并不是很友好。所有打算通过自定义注解方式实现,更加的灵活。
废话不多说,直接上代码。
定义注解
package com.zpl.dynamic.framework.annotations;
import com.zpl.dynamic.framework.context.DynamicTableNameHelper;
import java.lang.annotation.*;
/**
* 动态表名注解
* 通过spel表达式。自动注入到{@link DynamicTableNameHelper}中。而无需手动注入。减少代码侵入性
*
* @author: pinlin
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DynamicName {
/**
* spel 表达式
*/
String spel();
}
定义切面类,具体代码如何下
package com.zpl.dynamic.framework.aop;
import com.zpl.dynamic.framework.annotations.DynamicName;
import com.zpl.dynamic.framework.context.DynamicTableNameHelper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
/**
* 切面。用户解析spel表达式,并且赋值到 {@link DynamicTableNameHelper}中
*
* @author: pinlin
*/
@Aspect
@Slf4j
public class DynamicNameAspect {
/**
* spel解析器
*/
private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
/**
* 方法形参名解析器
*/
private static final DefaultParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
@Around("@annotation(dynamicName)")
public Object around(ProceedingJoinPoint joinPoint, DynamicName dynamicName) throws Throwable {
try {
// 获取到spel表达式为空,就不需要解析了
if (!StringUtils.hasText(dynamicName.spel())) {
return joinPoint.proceed();
}
Object[] args = joinPoint.getArgs();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
String[] paramNames = NAME_DISCOVERER.getParameterNames(method);
Expression expression = EXPRESSION_PARSER.parseExpression(dynamicName.spel());
EvaluationContext context = new StandardEvaluationContext();
for (int i = 0; i < args.length; i++) {
assert paramNames != null;
context.setVariable(paramNames[i], args[i]);
}
String tableName = expression.getValue(context, String.class);
if (StringUtils.hasText(tableName)) {
DynamicTableNameHelper.set(tableName);
}
return joinPoint.proceed();
} finally {
// 释放缓存内容
DynamicTableNameHelper.remove();
}
}
}
定义动态表名处理器
package com.zpl.dynamic.framework.handler;
import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;
import com.zpl.dynamic.framework.context.DynamicTableNameHelper;
import org.springframework.util.StringUtils;
/**
* 动态表名处理器
*
* @author: pinlin
*/
public class DynamicTableNameHandler implements TableNameHandler {
@Override
public String dynamicTableName(String sql, String tableName) {
String tableNameSuffix = DynamicTableNameHelper.get();
if (StringUtils.hasText(tableNameSuffix)) {
return tableName + "_" + tableNameSuffix;
}
return tableName;
}
}
创建自动配置类
package com.zpl.dynamic.framework.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.zpl.dynamic.framework.aop.DynamicNameAspect;
import com.zpl.dynamic.framework.handler.DynamicTableNameHandler;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* mybatis 自动配置类
*
* @author: pinlin
*/
@MapperScan(value = {"com.zpl"}, annotationClass = Mapper.class)
@Component
public class MybatisAutoConfiguration {
@Resource
private TableNameHandler tableNameHandler;
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
// 需要放到第一位,切记不要放错,不然会导致动态表名切换失效
dynamicTableNameInnerInterceptor.setTableNameHandler(tableNameHandler);
interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
@Bean
public DynamicNameAspect dynamicNameAspect() {
return new DynamicNameAspect();
}
@Bean
public DynamicTableNameHandler dynamicTableNameHandler() {
return new DynamicTableNameHandler();
}
}
说明
这里的动态表名处理器,一定要放到拦截器的第一位,不然会导致动态表名处理切换失败。
这里的动态表名处理器,一定要放到拦截器的第一位,不然会导致动态表名处理切换失败。
这里的动态表名处理器,一定要放到拦截器的第一位,不然会导致动态表名处理切换失败。
使用
-
在方式添加动态表名注解,具体如何下
注意
这里需要注意的地方就是,注解的spel获取的值的名称要和方法的形参名要一致,不然会报错。
比如上面案列 reqVO
那么spel表达式的取值也是reqVO.tableNameSffix
中的reqVO
需要保持一致。
如果suffixName
或reqVO.tableNameSffix
为空贼默认查询主表
- 具体用法可参考单元测试类
UserServiceTest
- 具体代码仓库地址为https://github.com/zengpinlin/dynamic-demo
说明
如果有哪里不对的地方。还请各位伙伴指出。谢谢。
标签:AOP,dynamic,表名,spel,Plus,org,import,com From: https://www.cnblogs.com/zengpinlin/p/16977778.html