From f8437918de342ee45d15df30c20de5e8d3b18379 Mon Sep 17 00:00:00 2001 From: Charles7c Date: Sat, 28 Sep 2024 21:31:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(extension/tenant):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=A4=9A=E7=A7=9F=E6=88=B7=E6=95=B0=E6=8D=AE=E6=BA=90=E7=BA=A7?= =?UTF-8?q?=E9=9A=94=E7=A6=BB=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pom.xml | 12 +++ .../annotation/TenantDataSourceIgnore.java | 16 ++-- .../autoconfigure/TenantInterceptor.java | 33 ++++++++ .../autoconfigure/TenantProperties.java | 13 ++++ .../TenantWebMvcAutoConfiguration.java | 50 +++++++++++++ .../tenant/config/TenantDataSource.java | 75 +++++++++++++++++++ .../config/TenantDataSourceProvider.java | 18 +++++ .../handler/TenantDataSourceHandler.java | 44 +++++++++++ .../TenantAutoConfiguration.java | 58 +++++++++++++- .../DefaultTenantDataSourceHandler.java | 70 +++++++++++++++++ .../handler/TenantDataSourceAdvisor.java | 55 ++++++++++++++ .../handler/TenantDataSourceInterceptor.java | 41 ++++++++++ 12 files changed, 474 insertions(+), 11 deletions(-) rename continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/mp/autoconfigure/ConditionalOnEnabledDataPermission.java => continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/annotation/TenantDataSourceIgnore.java (64%) create mode 100644 continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantInterceptor.java create mode 100644 continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantWebMvcAutoConfiguration.java create mode 100644 continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/config/TenantDataSource.java create mode 100644 continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/config/TenantDataSourceProvider.java create mode 100644 continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/handler/TenantDataSourceHandler.java create mode 100644 continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/handler/DefaultTenantDataSourceHandler.java create mode 100644 continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/handler/TenantDataSourceAdvisor.java create mode 100644 continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/handler/TenantDataSourceInterceptor.java diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/pom.xml b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/pom.xml index c67e6ae9..46bf3864 100644 --- a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/pom.xml +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/pom.xml @@ -18,5 +18,17 @@ com.alibaba transmittable-thread-local + + + org.springframework + spring-webmvc + true + + + + jakarta.servlet + jakarta.servlet-api + true + \ No newline at end of file diff --git a/continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/mp/autoconfigure/ConditionalOnEnabledDataPermission.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/annotation/TenantDataSourceIgnore.java similarity index 64% rename from continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/mp/autoconfigure/ConditionalOnEnabledDataPermission.java rename to continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/annotation/TenantDataSourceIgnore.java index 1639fabf..ddc61642 100644 --- a/continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/mp/autoconfigure/ConditionalOnEnabledDataPermission.java +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/annotation/TenantDataSourceIgnore.java @@ -14,22 +14,18 @@ * limitations under the License. */ -package top.continew.starter.data.mp.autoconfigure; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import top.continew.starter.core.constant.PropertiesConstants; +package top.continew.starter.extension.tenant.annotation; import java.lang.annotation.*; /** - * 是否启用数据权限注解 + * 多租户数据源级隔离忽略注解 * * @author Charles7c - * @since 1.1.0 + * @since 2.7.0 */ -@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) @Documented -@ConditionalOnProperty(prefix = "mybatis-plus.extension.data-permission", name = PropertiesConstants.ENABLED, havingValue = "true") -public @interface ConditionalOnEnabledDataPermission { -} \ No newline at end of file +public @interface TenantDataSourceIgnore { +} diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantInterceptor.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantInterceptor.java new file mode 100644 index 00000000..64d02913 --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantInterceptor.java @@ -0,0 +1,33 @@ +package top.continew.starter.extension.tenant.autoconfigure; + +import cn.hutool.core.convert.Convert; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.util.StringUtils; +import org.springframework.web.servlet.HandlerInterceptor; +import top.continew.starter.extension.tenant.context.TenantContext; +import top.continew.starter.extension.tenant.context.TenantContextHolder; + +/** + * 租户拦截器 + * + * @author Charles7c + * @since 2.7.0 + */ +public class TenantInterceptor implements HandlerInterceptor { + + private final TenantProperties tenantProperties; + + public TenantInterceptor(TenantProperties tenantProperties) { + this.tenantProperties = tenantProperties; + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + String tenantId = request.getHeader(tenantProperties.getTenantIdHeader()); + TenantContext tenantContext = new TenantContext(); + tenantContext.setTenantId(Convert.toLong(tenantId)); + TenantContextHolder.setContext(tenantContext); + return true; + } +} \ No newline at end of file diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantProperties.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantProperties.java index 0fdd9b41..2f29145b 100644 --- a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantProperties.java +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantProperties.java @@ -46,6 +46,11 @@ public class TenantProperties { */ private String tenantIdColumn = "tenant_id"; + /** + * 请求头中租户 ID 键名 + */ + private String tenantIdHeader = "X-Tenant-Id"; + /** * 超级租户 ID */ @@ -80,6 +85,14 @@ public class TenantProperties { this.tenantIdColumn = tenantIdColumn; } + public String getTenantIdHeader() { + return tenantIdHeader; + } + + public void setTenantIdHeader(String tenantIdHeader) { + this.tenantIdHeader = tenantIdHeader; + } + public Long getSuperTenantId() { return superTenantId; } diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantWebMvcAutoConfiguration.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantWebMvcAutoConfiguration.java new file mode 100644 index 00000000..b2113302 --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantWebMvcAutoConfiguration.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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. + */ + +package top.continew.starter.extension.tenant.autoconfigure; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import top.continew.starter.core.constant.PropertiesConstants; + +/** + * 多租户 Web MVC 自动配置 + * + * @author Charles7c + * @since 2.7.0 + */ +@AutoConfiguration +@ConditionalOnWebApplication +@ConditionalOnProperty(prefix = PropertiesConstants.TENANT, name = PropertiesConstants.ENABLED, havingValue = "true", matchIfMissing = true) +public class TenantWebMvcAutoConfiguration implements WebMvcConfigurer { + + private final TenantProperties tenantProperties; + + public TenantWebMvcAutoConfiguration(TenantProperties tenantProperties) { + this.tenantProperties = tenantProperties; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new TenantInterceptor(tenantProperties)); + } +} diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/config/TenantDataSource.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/config/TenantDataSource.java new file mode 100644 index 00000000..89151272 --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/config/TenantDataSource.java @@ -0,0 +1,75 @@ +package top.continew.starter.extension.tenant.config; + +/** + * 租户数据源配置 + * + * @author Charles7c + * @since 2.7.0 + */ +public class TenantDataSource { + + /** + * 连接池名称 + */ + private String poolName; + + /** + * 驱动类名 + */ + private String driverClassName; + + /** + * 数据库连接地址 + */ + private String url; + + /** + * 数据库用户名 + */ + private String username; + + /** + * 数据库密码 + */ + private String password; + + public String getPoolName() { + return poolName; + } + + public void setPoolName(String poolName) { + this.poolName = poolName; + } + + public String getDriverClassName() { + return driverClassName; + } + + public void setDriverClassName(String driverClassName) { + this.driverClassName = driverClassName; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/config/TenantDataSourceProvider.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/config/TenantDataSourceProvider.java new file mode 100644 index 00000000..f3f86525 --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/config/TenantDataSourceProvider.java @@ -0,0 +1,18 @@ +package top.continew.starter.extension.tenant.config; + +/** + * 租户数据源提供者 + * + * @author Charles7c + * @since 2.7.0 + */ +public interface TenantDataSourceProvider { + + /** + * 根据租户 ID 获取数据源配置 + * + * @param tenantId 租户 ID + * @return 数据源配置 + */ + TenantDataSource getByTenantId(String tenantId); +} diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/handler/TenantDataSourceHandler.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/handler/TenantDataSourceHandler.java new file mode 100644 index 00000000..0fb6563b --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/handler/TenantDataSourceHandler.java @@ -0,0 +1,44 @@ +package top.continew.starter.extension.tenant.handler; + +import top.continew.starter.extension.tenant.config.TenantDataSource; + +import javax.sql.DataSource; + +/** + * 租户数据源级隔离处理器 + * + * @author Charles7c + * @since 2.7.0 + */ +public interface TenantDataSourceHandler { + + /** + * 切换数据源 + * + * @param dataSourceName 数据源名称 + */ + void changeDataSource(String dataSourceName); + + /** + * 是否存在指定数据源 + * + * @param dataSourceName 数据源名称 + * @return 是否存在指定数据源 + */ + boolean containsDataSource(String dataSourceName); + + /** + * 创建数据源 + * + * @param tenantDataSource 数据源配置 + * @return 数据源 + */ + DataSource createDataSource(TenantDataSource tenantDataSource); + + /** + * 移除数据源 + * + * @param dataSourceName 数据源名称 + */ + void removeDataSource(String dataSourceName); +} diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantAutoConfiguration.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantAutoConfiguration.java index 53bcffb9..3dcc4fcf 100644 --- a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantAutoConfiguration.java +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantAutoConfiguration.java @@ -16,17 +16,22 @@ package top.continew.starter.extension.tenant.autoconfigure; +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 org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.core.ResolvableType; import top.continew.starter.core.constant.PropertiesConstants; -import top.continew.starter.extension.tenant.handler.DefaultTenantLineHandler; +import top.continew.starter.extension.tenant.config.TenantDataSourceProvider; +import top.continew.starter.extension.tenant.handler.*; /** * 多租户自动配置 @@ -72,4 +77,55 @@ public class TenantAutoConfiguration { return new DefaultTenantLineHandler(properties); } } + + /** + * 租户隔离级别:数据源级 + */ + @AutoConfiguration + @ConditionalOnProperty(name = PropertiesConstants.TENANT + ".isolation-level", havingValue = "datasource") + public static class DataSource { + static { + log.debug("[ContiNew Starter] - Auto Configuration 'Tenant-DataSource' completed initialization."); + } + + /** + * 租户数据源级隔离通知 + */ + @Bean + @ConditionalOnMissingBean + public TenantDataSourceAdvisor tenantDataSourceAdvisor(TenantDataSourceInterceptor tenantDataSourceInterceptor) { + return new TenantDataSourceAdvisor(tenantDataSourceInterceptor); + } + + /** + * 租户数据源级隔离拦截器 + */ + @Bean + @ConditionalOnMissingBean + public TenantDataSourceInterceptor tenantDataSourceInterceptor(TenantDataSourceHandler tenantDataSourceHandler) { + return new TenantDataSourceInterceptor(tenantDataSourceHandler); + } + + /** + * 租户数据源级隔离处理器(默认) + */ + @Bean + @ConditionalOnMissingBean + public TenantDataSourceHandler tenantDataSourceHandler(TenantDataSourceProvider tenantDataSourceProvider, DynamicRoutingDataSource dynamicRoutingDataSource, DefaultDataSourceCreator dataSourceCreator) { + return new DefaultTenantDataSourceHandler(tenantDataSourceProvider, dynamicRoutingDataSource, dataSourceCreator); + } + + /** + * 多租户数据源提供者 + */ + @Bean + @ConditionalOnMissingBean + public TenantDataSourceProvider tenantDataSourceProvider() { + if (log.isErrorEnabled()) { + log.error("Consider defining a bean of type '{}' in your configuration.", ResolvableType + .forClass(TenantDataSourceProvider.class)); + } + throw new NoSuchBeanDefinitionException(TenantDataSourceProvider.class); + } + } } diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/handler/DefaultTenantDataSourceHandler.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/handler/DefaultTenantDataSourceHandler.java new file mode 100644 index 00000000..5e019616 --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/handler/DefaultTenantDataSourceHandler.java @@ -0,0 +1,70 @@ +package top.continew.starter.extension.tenant.handler; + +import cn.hutool.core.text.CharSequenceUtil; +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import com.baomidou.dynamic.datasource.creator.DataSourceProperty; +import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator; +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.config.TenantDataSourceProvider; + +import javax.sql.DataSource; + +/** + * 默认租户数据源级隔离处理器 + * + * @author Charles7c + * @since 2.7.0 + */ +public class DefaultTenantDataSourceHandler implements TenantDataSourceHandler { + + private static final Logger log = LoggerFactory.getLogger(DefaultTenantDataSourceHandler.class); + private final DynamicRoutingDataSource dynamicRoutingDataSource; + private final DefaultDataSourceCreator dataSourceCreator; + private final TenantDataSourceProvider tenantDataSourceProvider; + + public DefaultTenantDataSourceHandler(TenantDataSourceProvider tenantDataSourceProvider, DynamicRoutingDataSource dynamicRoutingDataSource, DefaultDataSourceCreator dataSourceCreator) { + this.tenantDataSourceProvider = tenantDataSourceProvider; + this.dynamicRoutingDataSource = dynamicRoutingDataSource; + this.dataSourceCreator = dataSourceCreator; + } + + @Override + public void changeDataSource(String dataSourceName) { + if (!this.containsDataSource(dataSourceName)) { + TenantDataSource tenantDataSource = tenantDataSourceProvider.getByTenantId(dataSourceName); + if (null == tenantDataSource) { + throw new IllegalArgumentException("Data source [%s] configuration not found".formatted(dataSourceName)); + } + DataSource datasource = this.createDataSource(tenantDataSource); + dynamicRoutingDataSource.addDataSource(dataSourceName, datasource); + log.info("Load data source: {}", dataSourceName); + } + DynamicDataSourceContextHolder.push(dataSourceName); + log.info("Change data source: {}", dataSourceName); + } + + @Override + public boolean containsDataSource(String dataSourceName) { + return CharSequenceUtil.isNotBlank(dataSourceName) && dynamicRoutingDataSource.getDataSources() + .containsKey(dataSourceName); + } + + @Override + public DataSource createDataSource(TenantDataSource tenantDataSource) { + DataSourceProperty dataSourceProperty = new DataSourceProperty(); + dataSourceProperty.setPoolName(tenantDataSource.getPoolName()); + dataSourceProperty.setDriverClassName(tenantDataSource.getDriverClassName()); + dataSourceProperty.setUrl(tenantDataSource.getUrl()); + dataSourceProperty.setUsername(tenantDataSource.getUsername()); + dataSourceProperty.setPassword(tenantDataSource.getPassword()); + return dataSourceCreator.createDataSource(dataSourceProperty); + } + + @Override + public void removeDataSource(String dataSourceName) { + dynamicRoutingDataSource.removeDataSource(dataSourceName); + } +} \ No newline at end of file diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/handler/TenantDataSourceAdvisor.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/handler/TenantDataSourceAdvisor.java new file mode 100644 index 00000000..155a78a3 --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/handler/TenantDataSourceAdvisor.java @@ -0,0 +1,55 @@ +package top.continew.starter.extension.tenant.handler; + +import org.aopalliance.aop.Advice; +import org.springframework.aop.Pointcut; +import org.springframework.aop.aspectj.AspectJExpressionPointcut; +import org.springframework.aop.support.AbstractPointcutAdvisor; +import org.springframework.aop.support.ComposablePointcut; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; + +/** + * 租户数据源级隔离通知 + * + * @author Charles7c + * @since 2.7.0 + */ +public class TenantDataSourceAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware { + + private final Advice advice; + private final Pointcut pointcut; + + public TenantDataSourceAdvisor(TenantDataSourceInterceptor interceptor) { + this.advice = interceptor; + this.pointcut = buildPointcut(); + } + + @Override + public Pointcut getPointcut() { + return this.pointcut; + } + + @Override + public Advice getAdvice() { + return this.advice; + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + if (this.advice instanceof BeanFactoryAware beanFactoryAware) { + beanFactoryAware.setBeanFactory(beanFactory); + } + } + + /** + * 构建切点 + * + * @return 切点 + */ + private Pointcut buildPointcut() { + AspectJExpressionPointcut cut = new AspectJExpressionPointcut(); + cut.setExpression("!@annotation(top.continew.starter.extension.tenant.annotation.TenantDataSourceIgnore)"); + return new ComposablePointcut((Pointcut) cut); + } +} diff --git a/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/handler/TenantDataSourceInterceptor.java b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/handler/TenantDataSourceInterceptor.java new file mode 100644 index 00000000..bd2274b5 --- /dev/null +++ b/continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/handler/TenantDataSourceInterceptor.java @@ -0,0 +1,41 @@ +package top.continew.starter.extension.tenant.handler; + +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; + +/** + * 租户数据源级隔离拦截器 + * + * @author Charles7c + * @since 2.7.0 + */ +public class TenantDataSourceInterceptor implements MethodInterceptor { + + private final TenantDataSourceHandler tenantDataSourceHandler; + + public TenantDataSourceInterceptor(TenantDataSourceHandler tenantDataSourceHandler) { + this.tenantDataSourceHandler = tenantDataSourceHandler; + } + + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + Long tenantId = TenantContextHolder.getTenantId(); + if (null == tenantId) { + return invocation.proceed(); + } + // 切换数据源 + boolean isPush = false; + try { + String dataSourceName = tenantId.toString(); + tenantDataSourceHandler.changeDataSource(dataSourceName); + isPush = true; + return invocation.proceed(); + } finally { + if (isPush) { + DynamicDataSourceContextHolder.poll(); + } + } + } +}