代码拉取完成,页面将自动刷新
同步操作将从 JustryDeng/notebook 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
注意:
- 默认情况下,在insert时,若主动指定了租户列的值,那么租户插件将不会自动填充,最终生效的是主动指定的租户值
- 默认情况下,在delete、update、select时,where条件之后无论是否主动设置了租户的筛选值,mybatis-plus的租户条件都会再追加一个租户的条件筛选
使用非常简单,只需要在注册MybatisPlusInterceptor
的时候,设置添加mybatis-plus的内部多租户插件TenantLineInnerInterceptor
即可
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author miemie
* @since 2018-08-10
*/
@Configuration
public class MybatisPlusConfig {
/**
* 新多租户插件配置,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存万一出现问题
*
* 注:高版本mybatis-plus移除了useDeprecatedExecutor字段
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
@Override
public Expression getTenantId() {
// 返回租户id
return new LongValue(1);
}
@Override
public String getTenantIdColumn() {
return "tenant_id";
}
// 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件
@Override
public boolean ignoreTable(String tableName) {
return !"user".equalsIgnoreCase(tableName);
}
}));
// 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor
// interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
我们在配置TenantLineInnerInterceptor时,需要构造TenantLineHandler对象,这里对TenantLineHandler进行方法说明
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.schema.Column;
import java.util.List;
/**
* 租户处理器( TenantId 行级 )
*
* @author hubin
* @since 3.4.0
*/
public interface TenantLineHandler {
/**
* 获取租户 ID 值表达式,只支持单个 ID 值
* <p>
*
* @return 租户 ID 值表达式
*/
Expression getTenantId();
/**
* 获取租户字段名
* <p>
* 默认字段名叫: tenant_id
*
* @return 租户字段名
*/
default String getTenantIdColumn() {
return "tenant_id";
}
/**
* 根据表名判断是否忽略拼接多租户条件
* <p>
* 默认都要进行解析并拼接多租户条件
*
* @param tableName 表名
* @return 是否忽略, true:表示忽略,false:需要解析并拼接多租户条件
*/
default boolean ignoreTable(String tableName) {
return false;
}
/**
* 忽略插入租户字段逻辑
*
* @param columns 插入字段
* @param tenantIdColumn 租户 ID 字段
* @return
*/
default boolean ignoreInsert(List<Column> columns, String tenantIdColumn) {
return columns.stream().map(Column::getColumnName).anyMatch(i -> i.equalsIgnoreCase(tenantIdColumn));
}
}
@Override
public Expression getTenantId() {
// 租户id应该根据当前用户,动态获取; 这里简单演示,写死为1
return new LongValue(1);
}
@Override
public String getTenantIdColumn() {
// 数据库表中,代表租户id的列名
return "jd_tenant_id";
}
@Override
public boolean ignoreTable(String tableName) {
// 只要表名不是user的表,都不做多租户区分, 都忽略
return !"user".equalsIgnoreCase(tableName);
}
这里通过代码上的注释,进行说明
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.samples.tenant.entity.User;
import com.baomidou.mybatisplus.samples.tenant.mapper.UserMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.List;
/**
* <p>
* 多租户 Tenant 演示
* </p>
*
* @author hubin
* @since 2018-08-11
*/
@SpringBootTest
public class TenantTest {
@Resource
private UserMapper mapper;
/**
* 新增的时候,如果该表 没有被
* {@link TenantLineHandler#ignoreTable(java.lang.String)}
* 忽略,那么会自动插入租户id
*/
@Test
public void aInsert() {
User user = new User();
user.setName("一一");
Assertions.assertTrue(mapper.insert(user) > 0);
user = mapper.selectById(user.getId());
Assertions.assertTrue(1 == user.getTenantId());
}
/**
* 删除的时候,如果该表 没有被
* {@link TenantLineHandler#ignoreTable(java.lang.String)}
* 忽略,那么会在where后自动追加租户id判等条件
*/
@Test
public void bDelete() {
Assertions.assertTrue(mapper.deleteById(3L) > 0);
}
/**
* 更新的时候,如果该表 没有被
* {@link TenantLineHandler#ignoreTable(java.lang.String)}
* 忽略,那么会在where后自动追加租户id判等条件
*/
@Test
public void cUpdate() {
Assertions.assertTrue(mapper.updateById(new User().setId(1L).setName("mp")) > 0);
}
/**
* 查询的时候,如果该表 没有被
* {@link TenantLineHandler#ignoreTable(java.lang.String)}
* 忽略,那么会在where后自动追加租户id判等条件
*/
@Test
public void dSelect() {
List<User> userList = mapper.selectList(null);
userList.forEach(u -> Assertions.assertTrue(1 == u.getTenantId()));
}
/**
* 自定义SQL:默认也会增加多租户条件
* 参考打印的SQL
*/
@Test
public void manualSqlTenantFilterTest() {
System.out.println(mapper.myCount());
}
/**
*
* 1. 多张表联表查询的时候,如果from表没有被
* {@link TenantLineHandler#ignoreTable(java.lang.String)}
* 略,那么会在where后面 and加 租户过滤 条件
* 如(addr_name表被忽略, user表没有被忽略): SELECT u.id, u.name, a.name AS addr_name FROM user u LEFT JOIN user_addr a ON a.user_id = u.id WHERE u.name LIKE concat(concat('%', ?), '%') AND u.tenant_id = 1
*
* 2.多张表联表查询的时候,如果被join表没有被
* {@link TenantLineHandler#ignoreTable(java.lang.String)}
* 略,那么会在在 joins...on..后面加on后面 and加 租户过滤 条件
* 如(addr_name表被忽略, user表没有被忽略): SELECT a.name AS addr_name, u.id, u.name FROM user_addr a LEFT JOIN user u ON u.id = a.user_id AND u.tenant_id = 1
*/
@Test
public void testTenantFilter(){
mapper.getAddrAndUser(null).forEach(System.out::println);
mapper.getAddrAndUser("add").forEach(System.out::println);
mapper.getUserAndAddr(null).forEach(System.out::println);
mapper.getUserAndAddr("J").forEach(System.out::println);
}
}
默认的,租户字段是通过判等进行过滤的;在有些场景下右模糊查询可能更贴合业务场景写
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.schema.Table;
import java.util.List;
import java.util.stream.Collectors;
/**
* 租户字段的like实现(只需适配update、delete、select)
*
* @author JustryDeng
* @since 2022/9/18 17:41:50
*/
public class LikeTenantLineInnerInterceptor extends TenantLineInnerInterceptor {
TenantLineHandler tenantLineHandler;
public LikeTenantLineInnerInterceptor(TenantLineHandler tenantLineHandler) {
super(tenantLineHandler);
this.tenantLineHandler = tenantLineHandler;
}
/**
* 适配update和delete
*/
@Override
protected BinaryExpression andExpression(Table table, Expression where) {
final StringValue tenantId = (StringValue)tenantLineHandler.getTenantId();
final String tenantIdValue = tenantId.getValue();
LikeExpression likeExpression = new LikeExpression();
likeExpression.setLeftExpression(this.getAliasColumn(table));
likeExpression.setRightExpression(new StringValue(tenantIdValue + "%"));
if (null != where) {
if (where instanceof OrExpression) {
return new AndExpression(likeExpression, new Parenthesis(where));
} else {
return new AndExpression(likeExpression, where);
}
}
return likeExpression;
}
/**
* 适配elect
*/
@Override
protected Expression builderExpression(Expression currentExpression, List<Table> tables) {
// 没有表需要处理直接返回
if (CollectionUtils.isEmpty(tables)) {
return currentExpression;
}
// 构造每张表的条件
List<Table> tempTables = tables.stream()
.filter(x -> !tenantLineHandler.ignoreTable(x.getName()))
.collect(Collectors.toList());
// 没有表需要处理直接返回
if (CollectionUtils.isEmpty(tempTables)) {
return currentExpression;
}
final StringValue tenantIdStrVal = (StringValue)tenantLineHandler.getTenantId();
final String tenantIdValue = tenantIdStrVal.getValue();
List<LikeExpression> likeExpressions = tempTables.stream()
.map(item -> {
LikeExpression likeExpression = new LikeExpression();
likeExpression.setLeftExpression(this.getAliasColumn(item));
likeExpression.setRightExpression(new StringValue(tenantIdValue + "%"));
return likeExpression;
})
.collect(Collectors.toList());
// 注入的表达式
Expression injectExpression = likeExpressions.get(0);
// 如果有多表,则用 and 连接
if (likeExpressions.size() > 1) {
for (int i = 1; i < likeExpressions.size(); i++) {
injectExpression = new AndExpression(injectExpression, likeExpressions.get(i));
}
}
if (currentExpression == null) {
return injectExpression;
}
if (currentExpression instanceof OrExpression) {
return new AndExpression(new Parenthesis(currentExpression), injectExpression);
} else {
return new AndExpression(currentExpression, injectExpression);
}
}
}
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.baomidou.mybatisplus.samples.tenant.mapper")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new LikeTenantLineInnerInterceptor(new TenantLineHandler() {
@Override
public Expression getTenantId() {
return new StringValue("1,a");
}
@Override
public boolean ignoreTable(String tableName) {
return !"user".equalsIgnoreCase(tableName);
}
}));
return interceptor;
}
}
总体配置
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.ideaaedi.commonds.function.NoArgConsumer;
import com.ideaaedi.commonds.function.NoArgFunction;
import com.ideaaedi.commonspring.spel.SpelUtil;
import com.ultronsoft.port.acs.oauth2.server.entity.po.SysUserPO;
import com.ultronsoft.port.acs.oauth2.server.util.ClassUtil;
import com.ultronsoft.port.acs.oauth2.server.util.ThreadLocalUtil;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.reflection.MetaObject;
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.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import javax.annotation.PostConstruct;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.stream.Collectors;
/**
* mybatis-plus 配置
*
* @author JustryDeng
* @since 2022/9/7 13:32:55
*/
@Slf4j
@Configuration
@EnableAspectJAutoProxy
public class MybatisPlusConfig {
private final Set<String> tenantTables = new HashSet<>();
@PostConstruct
private void initTenantTables() {
List<String> poClassLongNameList = new ArrayList<>(64);
poClassLongNameList.addAll(
ClassUtil.getClazzName("com.ultronsoft.port.acs.oauth2.server.entity.po", true)
);
poClassLongNameList.addAll(
ClassUtil.getClazzName("com.ultronsoft.port.acs.visitor.entity.po", true)
);
try {
for (String poClassLongName : poClassLongNameList) {
Class<?> clazz = Class.forName(poClassLongName);
TableName tableNameAnno = clazz.getDeclaredAnnotation(TableName.class);
if (tableNameAnno == null) {
continue;
}
String tableName = tableNameAnno.value();
boolean existTenantColumn = true;
try {
clazz.getDeclaredField("tenantId");
} catch (NoSuchFieldException e) {
existTenantColumn = false;
}
if (existTenantColumn) {
tenantTables.add(tableName);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
log.info("tenantTables -> {}", JSON.toJSONString(tenantTables));
}
/**
* 插件配置
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new LikeTenantLineInnerInterceptor(new TenantLineHandler() {
@Override
public Expression getTenantId() {
TenantBO tenantBo = ThreadLocalUtil.TENANT_HOLDER.get();
if (tenantBo != null && tenantBo.getTenantDeque().size() > 0) {
String tenant = tenantBo.getTenantDeque().peekFirst();
if (tenant != null) {
return new StringValue(tenant);
}
}
SysUserPO sysUser = ThreadLocalUtil.CURR_USER.get();
Objects.requireNonNull(sysUser, "LikeTenantLineInnerInterceptor tips: sysUser cannot be null.");
return new StringValue(sysUser.getCurrTenant());
}
@Override
public String getTenantIdColumn() {
return "tenant_id";
}
@Override
public boolean ignoreTable(String tableName) {
return !tenantTables.contains(tableName);
}
}));
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
/**
* 设置字段自动填充 <br /> <br />
* <a href="https://baomidou.com/pages/4c6bcf/">官网</a>
*
* @author JustryDeng
* @since 2022/9/6 17:10:30
*/
@Slf4j
@Component
public static class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
@Autowired(required = false)
OperatorIdProvider operatorIdProvider;
@Override
public void insertFill(MetaObject metaObject) {
Long userId = operatorIdProvider == null ? null : operatorIdProvider.operatorId(metaObject);
// 这里是PO的字段名而不是表的列名
this.strictInsertFill(metaObject, "createdBy", Long.class, userId);
this.strictInsertFill(metaObject, "createdAt", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
Long userId = operatorIdProvider == null ? null : operatorIdProvider.operatorId(metaObject);
// 这里是PO的字段名而不是表的列名
this.strictUpdateFill(metaObject, "updatedBy", Long.class, userId);
this.strictUpdateFill(metaObject, "updatedAt", LocalDateTime.class, LocalDateTime.now());
}
/**
* 数据操作人id提供器
*
* @author kuoyi
* @since 1.2.0
*/
public interface OperatorIdProvider {
/**
* 获取数据操作人id
*
* @param metaObject
* 当前mybatis sql操作元数据
*
* @return 数据操作人id
*/
@Nullable
default Long operatorId(MetaObject metaObject) {
SysUserPO user = ThreadLocalUtil.CURR_USER.get();
if (user == null) {
return null;
}
return user.getId();
}
}
}
/**
* 多租户 like插件
*/
public static class LikeTenantLineInnerInterceptor extends TenantLineInnerInterceptor {
TenantLineHandler tenantLineHandler;
public LikeTenantLineInnerInterceptor(TenantLineHandler tenantLineHandler) {
super(tenantLineHandler);
this.tenantLineHandler = tenantLineHandler;
}
@Override
protected void processInsert(Insert insert, int index, String sql, Object obj) {
super.processInsert(insert, index, sql, obj);
}
@Override
protected void processDelete(Delete delete, int index, String sql, Object obj) {
if (shouldIgnoreTenant()) {
return;
}
super.processDelete(delete, index, sql, obj);
}
@Override
protected void processUpdate(Update update, int index, String sql, Object obj) {
if (shouldIgnoreTenant()) {
return;
}
super.processUpdate(update, index, sql, obj);
}
@Override
protected void processSelect(Select select, int index, String sql, Object obj) {
if (shouldIgnoreTenant()) {
return;
}
super.processSelect(select, index, sql, obj);
}
/**
* 是否忽略租户<br />
* 逻辑执行在mp自己的判断是否忽略租户的逻辑之前
*/
private boolean shouldIgnoreTenant() {
TenantBO tenantBo = ThreadLocalUtil.TENANT_HOLDER.get();
if (tenantBo != null && tenantBo.getTenantDeque().size() > 0) {
String tenant = tenantBo.getTenantDeque().peekFirst();
return TenantBO.IGNORE_URD.equals(tenant);
}
return false;
}
/**
* 适配update和delete
*/
@Override
protected BinaryExpression andExpression(Table table, Expression where) {
final StringValue tenantId = (StringValue) tenantLineHandler.getTenantId();
final String tenantIdValue = tenantId.getValue();
LikeExpression likeExpression = new LikeExpression();
likeExpression.setLeftExpression(this.getAliasColumn(table));
likeExpression.setRightExpression(new StringValue(tenantIdValue + "%"));
if (null != where) {
if (where instanceof OrExpression) {
return new AndExpression(likeExpression, new Parenthesis(where));
} else {
return new AndExpression(likeExpression, where);
}
}
return likeExpression;
}
/**
* 适配select
*/
@Override
protected Expression builderExpression(Expression currentExpression, List<Table> tables) {
// 没有表需要处理直接返回
if (CollectionUtils.isEmpty(tables)) {
return currentExpression;
}
// 构造每张表的条件
List<Table> tempTables = tables.stream()
.filter(x -> !tenantLineHandler.ignoreTable(x.getName()))
.collect(Collectors.toList());
// 没有表需要处理直接返回
if (CollectionUtils.isEmpty(tempTables)) {
return currentExpression;
}
final StringValue tenantIdStrVal = (StringValue) tenantLineHandler.getTenantId();
final String tenantIdValue = tenantIdStrVal.getValue();
List<LikeExpression> likeExpressions = tempTables.stream()
.map(item -> {
LikeExpression likeExpression = new LikeExpression();
likeExpression.setLeftExpression(this.getAliasColumn(item));
likeExpression.setRightExpression(new StringValue(tenantIdValue + "%"));
return likeExpression;
})
.collect(Collectors.toList());
// 注入的表达式
Expression injectExpression = likeExpressions.get(0);
// 如果有多表,则用 and 连接
if (likeExpressions.size() > 1) {
for (int i = 1; i < likeExpressions.size(); i++) {
injectExpression = new AndExpression(injectExpression, likeExpressions.get(i));
}
}
if (currentExpression == null) {
return injectExpression;
}
if (currentExpression instanceof OrExpression) {
return new AndExpression(new Parenthesis(currentExpression), injectExpression);
} else {
return new AndExpression(currentExpression, injectExpression);
}
}
}
/**
* 临时租户实体类模型支持
*/
@Getter
@ToString
public static class TenantBO {
/**
* 特殊标识, 当为CURD中的UPDATE READ DELETE时,忽略租户
*/
public static final String IGNORE_URD = "<IGNORE_TENANT_WHILE_URD>";
/** 队列 */
@Getter
private final ConcurrentLinkedDeque<String> tenantDeque = new ConcurrentLinkedDeque<>();
public TenantBO(String tenant) {
tenantDeque.addFirst(tenant);
}
}
/**
* 临时租户指定支持
*/
public static class TemporaryTenantProvider {
public <R> R exec(NoArgFunction<R> function, String tenantId) {
try {
Assert.isTrue(StringUtils.isNotBlank(tenantId), "tenantId cannot be blank.");
TenantBO tenantBO = ThreadLocalUtil.TENANT_HOLDER.get();
if (tenantBO == null) {
ThreadLocalUtil.TENANT_HOLDER.set(new TenantBO(tenantId));
} else {
tenantBO.getTenantDeque().addFirst(tenantId);
}
return function.apply();
} finally {
TenantBO tenantBO = ThreadLocalUtil.TENANT_HOLDER.get();
if (tenantBO != null) {
tenantBO.getTenantDeque().pollFirst();
if (tenantBO.getTenantDeque().size() <= 0) {
ThreadLocalUtil.TENANT_HOLDER.remove();
}
}
}
}
public void voidExec(NoArgConsumer consumer, String tenantId) {
try {
Assert.isTrue(StringUtils.isNotBlank(tenantId), "tenantId cannot be blank.");
TenantBO tenantBO = ThreadLocalUtil.TENANT_HOLDER.get();
if (tenantBO == null) {
ThreadLocalUtil.TENANT_HOLDER.set(new TenantBO(tenantId));
} else {
tenantBO.getTenantDeque().addFirst(tenantId);
}
consumer.accept();
} finally {
TenantBO tenantBO = ThreadLocalUtil.TENANT_HOLDER.get();
if (tenantBO != null) {
tenantBO.getTenantDeque().pollFirst();
if (tenantBO.getTenantDeque().size() <= 0) {
ThreadLocalUtil.TENANT_HOLDER.remove();
}
}
}
}
@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Tenant {
/**
* 租户 (支持spel) <br /> spel示例:"#{#user.id + ':' + #user.username}"
*/
String value() default "";
/**
* 租户(当为空时,获取value的值作为租户)
*/
String tenantSpel() default "";
}
@Slf4j
@Aspect
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 90)
public static class TenantAop {
@Around("@annotation(tenantAnno)")
public Object aroundAdvice(ProceedingJoinPoint thisJoinPoint, TemporaryTenantProvider.Tenant tenantAnno) throws Throwable {
String tenant = tenantAnno.value();
Method method = ((MethodSignature) thisJoinPoint.getSignature()).getMethod();
Object[] arguments = thisJoinPoint.getArgs();
if (StringUtils.isBlank(tenant)) {
String tenantSpel = tenantAnno.tenantSpel();
tenant = SpelUtil.parseSpel(method, arguments, String.class, tenantSpel);
}
log.info("TenantAop curr tenant is {}", tenant);
try {
Assert.isTrue(StringUtils.isNotBlank(tenant), "tenant cannot be blank.");
TenantBO tenantBO = ThreadLocalUtil.TENANT_HOLDER.get();
if (tenantBO == null) {
ThreadLocalUtil.TENANT_HOLDER.set(new TenantBO(tenant));
} else {
tenantBO.getTenantDeque().addFirst(tenant);
}
return thisJoinPoint.proceed();
} finally {
TenantBO tenantBO = ThreadLocalUtil.TENANT_HOLDER.get();
if (tenantBO != null) {
tenantBO.getTenantDeque().pollFirst();
if (tenantBO.getTenantDeque().size() <= 0) {
ThreadLocalUtil.TENANT_HOLDER.remove();
}
}
}
}
}
}
}
其中ThreadLocal类
import com.ultronsoft.port.unid.oauth2.server.config.MybatisPlusConfig;
import com.ultronsoft.port.unid.oauth2.server.entity.po.SysUserPO;
/**
* (non-javadoc)
*
* @author JustryDeng
* @since 2022/9/6 22:15:09
*/
public class ThreadLocalUtil {
/** 当前用户 */
public static final ThreadLocal<SysUserPO> CURR_USER = new ThreadLocal<>();
/**
* 线程级租户id
*/
public static final ThreadLocal<MybatisPlusConfig.TenantBO> TENANT_HOLDER = new ThreadLocal<>();
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。