mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-09-09 04:59:21 +08:00
refactor(extension/tenant): 优化多租户模块代码及包结构
This commit is contained in:
@@ -18,6 +18,7 @@ package top.continew.starter.extension.tenant.autoconfigure;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import top.continew.starter.core.constant.PropertiesConstants;
|
||||
import top.continew.starter.extension.tenant.enums.TenantIsolationLevel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -35,6 +36,11 @@ public class TenantProperties {
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 租户隔离级别
|
||||
*/
|
||||
private TenantIsolationLevel isolationLevel = TenantIsolationLevel.LINE;
|
||||
|
||||
/**
|
||||
* 租户 ID 列名
|
||||
*/
|
||||
@@ -63,6 +69,14 @@ public class TenantProperties {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public TenantIsolationLevel getIsolationLevel() {
|
||||
return isolationLevel;
|
||||
}
|
||||
|
||||
public void setIsolationLevel(TenantIsolationLevel isolationLevel) {
|
||||
this.isolationLevel = isolationLevel;
|
||||
}
|
||||
|
||||
public String getTenantIdColumn() {
|
||||
return tenantIdColumn;
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ package top.continew.starter.extension.tenant.config;
|
||||
import top.continew.starter.extension.tenant.context.TenantContext;
|
||||
|
||||
/**
|
||||
* 租户数据提供者
|
||||
* 租户提供者
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.7.0
|
||||
@@ -27,11 +27,11 @@ import top.continew.starter.extension.tenant.context.TenantContext;
|
||||
public interface TenantProvider {
|
||||
|
||||
/**
|
||||
* 根据租户ID获取租户数据
|
||||
* 根据租户 ID 获取租户上下文
|
||||
*
|
||||
* @param tenantId 租户 ID
|
||||
* @param verify 是否验证租户有效性
|
||||
* @return 数据源配置
|
||||
* @param isVerify 是否验证有效性
|
||||
* @return 租户上下文
|
||||
*/
|
||||
TenantContext getByTenantId(String tenantId, boolean verify);
|
||||
TenantContext getByTenantId(String tenantId, boolean isVerify);
|
||||
}
|
||||
|
@@ -16,7 +16,10 @@
|
||||
|
||||
package top.continew.starter.extension.tenant.context;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import top.continew.starter.extension.tenant.autoconfigure.TenantProperties;
|
||||
import top.continew.starter.extension.tenant.config.TenantDataSource;
|
||||
import top.continew.starter.extension.tenant.enums.TenantIsolationLevel;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -69,11 +72,22 @@ public class TenantContextHolder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取隔离级别
|
||||
* 获取租户隔离级别
|
||||
*
|
||||
* @return 租户隔离级别
|
||||
*/
|
||||
public static TenantIsolationLevel getIsolationLevel() {
|
||||
return Optional.ofNullable(getContext())
|
||||
.map(TenantContext::getIsolationLevel)
|
||||
.orElse(TenantIsolationLevel.LINE);
|
||||
.orElse(SpringUtil.getBean(TenantProperties.class).getIsolationLevel());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户数据源
|
||||
*
|
||||
* @return 租户数据源
|
||||
*/
|
||||
public static TenantDataSource getDataSource() {
|
||||
return Optional.ofNullable(getContext()).map(TenantContext::getDataSource).orElse(null);
|
||||
}
|
||||
}
|
||||
|
@@ -31,6 +31,7 @@ public interface TenantDataSourceHandler {
|
||||
/**
|
||||
* 切换数据源
|
||||
*
|
||||
* @param tenantDataSource 数据源配置
|
||||
*/
|
||||
void changeDataSource(TenantDataSource tenantDataSource);
|
||||
|
||||
|
@@ -17,15 +17,18 @@
|
||||
package top.continew.starter.extension.tenant.handler;
|
||||
|
||||
/**
|
||||
* @description: 租户处理器
|
||||
* @author: 小熊
|
||||
* @create: 2024-12-18 19:37
|
||||
* 租户处理器
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public interface TenantHandler {
|
||||
|
||||
/**
|
||||
* 在指定租户中执行方法
|
||||
* 在指定租户中执行
|
||||
*
|
||||
* @param tenantId 租户 ID
|
||||
* @param runnable 方法
|
||||
*/
|
||||
void executeInTenant(Long tenantId, Runnable runnable);
|
||||
|
||||
void execute(Long tenantId, Runnable runnable);
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
@@ -31,10 +32,16 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import top.continew.starter.core.constant.PropertiesConstants;
|
||||
import top.continew.starter.extension.tenant.config.TenantProvider;
|
||||
import top.continew.starter.extension.tenant.handler.*;
|
||||
import top.continew.starter.extension.tenant.handler.DefaultTenantHandler;
|
||||
import top.continew.starter.extension.tenant.handler.TenantDataSourceHandler;
|
||||
import top.continew.starter.extension.tenant.handler.TenantHandler;
|
||||
import top.continew.starter.extension.tenant.handler.datasource.DefaultTenantDataSourceHandler;
|
||||
import top.continew.starter.extension.tenant.handler.datasource.TenantDataSourceAdvisor;
|
||||
import top.continew.starter.extension.tenant.handler.datasource.TenantDataSourceInterceptor;
|
||||
import top.continew.starter.extension.tenant.handler.line.DefaultTenantLineHandler;
|
||||
|
||||
/**
|
||||
* 多租户自动配置
|
||||
* 租户自动配置
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.7.0
|
||||
@@ -45,12 +52,10 @@ import top.continew.starter.extension.tenant.handler.*;
|
||||
public class TenantAutoConfiguration {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TenantAutoConfiguration.class);
|
||||
private final TenantProperties tenantProperties;
|
||||
|
||||
private TenantAutoConfiguration() {
|
||||
}
|
||||
|
||||
static {
|
||||
log.debug("[ContiNew Starter] - Auto Configuration 'Tenant' completed initialization.");
|
||||
private TenantAutoConfiguration(TenantProperties tenantProperties) {
|
||||
this.tenantProperties = tenantProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,11 +105,11 @@ public class TenantAutoConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* 多租户数据源提供者
|
||||
* 租户提供者
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TenantProvider tenantDataSourceProvider() {
|
||||
public TenantProvider tenantProvider() {
|
||||
if (log.isErrorEnabled()) {
|
||||
log.error("Consider defining a bean of type '{}' in your configuration.", ResolvableType
|
||||
.forClass(TenantProvider.class));
|
||||
@@ -118,7 +123,11 @@ public class TenantAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TenantHandler tenantHandler(TenantDataSourceHandler tenantDataSourceHandler, TenantProvider tenantProvider) {
|
||||
return new DefaultTenantHandler(tenantDataSourceHandler, tenantProvider);
|
||||
return new DefaultTenantHandler(tenantProperties, tenantDataSourceHandler, tenantProvider);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
log.debug("[ContiNew Starter] - Auto Configuration 'Tenant' completed initialization.");
|
||||
}
|
||||
}
|
||||
|
@@ -16,45 +16,51 @@
|
||||
|
||||
package top.continew.starter.extension.tenant.handler;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
|
||||
import top.continew.starter.extension.tenant.autoconfigure.TenantProperties;
|
||||
import top.continew.starter.extension.tenant.config.TenantProvider;
|
||||
import top.continew.starter.extension.tenant.context.TenantContext;
|
||||
import top.continew.starter.extension.tenant.context.TenantContextHolder;
|
||||
import top.continew.starter.extension.tenant.enums.TenantIsolationLevel;
|
||||
|
||||
/**
|
||||
* @description: 租户处理器
|
||||
* @author: 小熊
|
||||
* @create: 2024-12-18 19:43
|
||||
* 租户处理器
|
||||
*
|
||||
* @author 小熊
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class DefaultTenantHandler implements TenantHandler {
|
||||
|
||||
private final TenantProperties tenantProperties;
|
||||
private final TenantDataSourceHandler dataSourceHandler;
|
||||
private final TenantProvider tenantProvider;
|
||||
|
||||
public DefaultTenantHandler(TenantDataSourceHandler dataSourceHandler, TenantProvider tenantProvider) {
|
||||
public DefaultTenantHandler(TenantProperties tenantProperties,
|
||||
TenantDataSourceHandler dataSourceHandler,
|
||||
TenantProvider tenantProvider) {
|
||||
this.tenantProperties = tenantProperties;
|
||||
this.dataSourceHandler = dataSourceHandler;
|
||||
this.tenantProvider = tenantProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeInTenant(Long tenantId, Runnable runnable) {
|
||||
boolean enabled = SpringUtil.getProperty("continew-starter.tenant.enabled", Boolean.class, false);
|
||||
if (enabled) {
|
||||
public void execute(Long tenantId, Runnable runnable) {
|
||||
if (!tenantProperties.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
TenantContext tenantHandler = tenantProvider.getByTenantId(tenantId.toString(), false);
|
||||
// 保存当前的租户上下文
|
||||
TenantContext originalContext = TenantContextHolder.getContext();
|
||||
// 切换数据源
|
||||
boolean isPush = false;
|
||||
try {
|
||||
// 设置新的租户上下文
|
||||
TenantContextHolder.setContext(tenantHandler);
|
||||
// 切换数据源
|
||||
if (TenantIsolationLevel.DATASOURCE.equals(tenantHandler.getIsolationLevel())) {
|
||||
//切换数据源
|
||||
dataSourceHandler.changeDataSource(tenantHandler.getDataSource());
|
||||
isPush = true;
|
||||
}
|
||||
// 执行业务逻辑
|
||||
runnable.run();
|
||||
} finally {
|
||||
// 恢复原始的租户上下文
|
||||
@@ -68,6 +74,4 @@ public class DefaultTenantHandler implements TenantHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.extension.tenant.handler;
|
||||
package top.continew.starter.extension.tenant.handler.datasource;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||
@@ -24,6 +24,7 @@ import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import top.continew.starter.extension.tenant.config.TenantDataSource;
|
||||
import top.continew.starter.extension.tenant.handler.TenantDataSourceHandler;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@@ -47,7 +48,9 @@ public class DefaultTenantDataSourceHandler implements TenantDataSourceHandler {
|
||||
|
||||
@Override
|
||||
public void changeDataSource(TenantDataSource tenantDataSource) {
|
||||
if (tenantDataSource != null) {
|
||||
if (tenantDataSource == null) {
|
||||
return;
|
||||
}
|
||||
String dataSourceName = tenantDataSource.getPoolName();
|
||||
if (!this.containsDataSource(dataSourceName)) {
|
||||
DataSource datasource = this.createDataSource(tenantDataSource);
|
||||
@@ -57,7 +60,6 @@ public class DefaultTenantDataSourceHandler implements TenantDataSourceHandler {
|
||||
DynamicDataSourceContextHolder.push(dataSourceName);
|
||||
log.info("Change data source: {}", dataSourceName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsDataSource(String dataSourceName) {
|
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.extension.tenant.handler;
|
||||
package top.continew.starter.extension.tenant.handler.datasource;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.springframework.aop.Pointcut;
|
@@ -14,13 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.extension.tenant.handler;
|
||||
package top.continew.starter.extension.tenant.handler.datasource;
|
||||
|
||||
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import top.continew.starter.extension.tenant.context.TenantContextHolder;
|
||||
import top.continew.starter.extension.tenant.enums.TenantIsolationLevel;
|
||||
import top.continew.starter.extension.tenant.handler.TenantDataSourceHandler;
|
||||
|
||||
/**
|
||||
* 租户数据源级隔离拦截器
|
||||
@@ -38,11 +39,13 @@ public class TenantDataSourceInterceptor implements MethodInterceptor {
|
||||
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
if (TenantContextHolder.getIsolationLevel() == TenantIsolationLevel.DATASOURCE) {
|
||||
if (TenantIsolationLevel.LINE.equals(TenantContextHolder.getIsolationLevel())) {
|
||||
return invocation.proceed();
|
||||
}
|
||||
// 切换数据源
|
||||
boolean isPush = false;
|
||||
try {
|
||||
tenantDataSourceHandler.changeDataSource(TenantContextHolder.getContext().getDataSource());
|
||||
tenantDataSourceHandler.changeDataSource(TenantContextHolder.getDataSource());
|
||||
isPush = true;
|
||||
return invocation.proceed();
|
||||
} finally {
|
||||
@@ -51,7 +54,4 @@ public class TenantDataSourceInterceptor implements MethodInterceptor {
|
||||
}
|
||||
}
|
||||
}
|
||||
return invocation.proceed();
|
||||
|
||||
}
|
||||
}
|
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.extension.tenant.handler;
|
||||
package top.continew.starter.extension.tenant.handler.line;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
Reference in New Issue
Block a user