Skip to content

Commit 2f2aae0

Browse files
committed
feat(extension/tenant): 新增 TenantUtils 替换 TenantHandler 接口及其实现类 DefaultTenantHandler
1 parent 3f7f118 commit 2f2aae0

9 files changed

Lines changed: 173 additions & 114 deletions

File tree

‎continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/TenantDataSourceHandler.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,9 @@ public interface TenantDataSourceHandler {
5757
* @param dataSourceName 数据源名称
5858
*/
5959
void removeDataSource(String dataSourceName);
60+
61+
/**
62+
* 轮询数据源
63+
*/
64+
void poll();
6065
}

continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/TenantHandler.java renamed to continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/annotation/ConditionalOnEnabledTenant.java

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,22 @@
1414
* limitations under the License.
1515
*/
1616

17-
package top.continew.starter.extension.tenant;
17+
package top.continew.starter.extension.tenant.annotation;
18+
19+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
20+
import top.continew.starter.core.constant.PropertiesConstants;
21+
22+
import java.lang.annotation.*;
1823

1924
/**
20-
* 租户处理器
25+
* 是否启用租户判断注解
2126
*
22-
* @author 小熊
23-
* @since 2.8.0
27+
* @author Charles7c
28+
* @since 2.13.1
2429
*/
25-
public interface TenantHandler {
26-
27-
/**
28-
* 在指定租户中执行
29-
*
30-
* @param tenantId 租户 ID
31-
* @param runnable 方法
32-
*/
33-
void execute(Long tenantId, Runnable runnable);
34-
}
30+
@Retention(RetentionPolicy.RUNTIME)
31+
@Target({ElementType.TYPE, ElementType.METHOD})
32+
@Documented
33+
@ConditionalOnProperty(prefix = PropertiesConstants.TENANT, name = PropertiesConstants.ENABLED, havingValue = "true", matchIfMissing = true)
34+
public @interface ConditionalOnEnabledTenant {
35+
}

‎continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantWebMvcAutoConfiguration.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
2424
import top.continew.starter.core.constant.PropertiesConstants;
2525
import top.continew.starter.extension.tenant.config.TenantProvider;
26+
import top.continew.starter.extension.tenant.interceptor.TenantInterceptor;
2627

2728
/**
2829
* 租户 Web MVC 自动配置

‎continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/context/TenantContextHolder.java‎

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import cn.hutool.extra.spring.SpringUtil;
2020
import com.alibaba.ttl.TransmittableThreadLocal;
21+
import top.continew.starter.core.util.SpringUtils;
2122
import top.continew.starter.extension.tenant.autoconfigure.TenantProperties;
2223
import top.continew.starter.extension.tenant.config.TenantDataSource;
2324
import top.continew.starter.extension.tenant.enums.TenantIsolationLevel;
@@ -35,12 +36,12 @@ public class TenantContextHolder {
3536
/**
3637
* 租户上下文
3738
*/
38-
private static final TransmittableThreadLocal<TenantContext> CONTEXT = new TransmittableThreadLocal<>();
39+
private static final TransmittableThreadLocal<TenantContext> CONTEXT_HOLDER = new TransmittableThreadLocal<>();
3940

4041
/**
4142
* 是否忽略租户
4243
*/
43-
private static final TransmittableThreadLocal<Boolean> IGNORE = new TransmittableThreadLocal<>();
44+
private static final TransmittableThreadLocal<Boolean> IGNORE_HOLDER = new TransmittableThreadLocal<>();
4445

4546
private TenantContextHolder() {
4647
}
@@ -51,7 +52,7 @@ private TenantContextHolder() {
5152
* @param context 上下文
5253
*/
5354
public static void setContext(TenantContext context) {
54-
CONTEXT.set(context);
55+
CONTEXT_HOLDER.set(context);
5556
}
5657

5758
/**
@@ -60,7 +61,7 @@ public static void setContext(TenantContext context) {
6061
* @return 上下文
6162
*/
6263
public static TenantContext getContext() {
63-
return CONTEXT.get();
64+
return CONTEXT_HOLDER.get();
6465
}
6566

6667
/**
@@ -69,7 +70,7 @@ public static TenantContext getContext() {
6970
* @param ignore 是否忽略租户
7071
*/
7172
public static void setIgnore(boolean ignore) {
72-
IGNORE.set(ignore);
73+
IGNORE_HOLDER.set(ignore);
7374
}
7475

7576
/**
@@ -78,15 +79,15 @@ public static void setIgnore(boolean ignore) {
7879
* @return 是否忽略租户
7980
*/
8081
public static boolean isIgnore() {
81-
return Boolean.TRUE.equals(IGNORE.get());
82+
return Boolean.TRUE.equals(IGNORE_HOLDER.get());
8283
}
8384

8485
/**
8586
* 清除
8687
*/
87-
public static void clearContext() {
88-
CONTEXT.remove();
89-
IGNORE.remove();
88+
public static void clear() {
89+
CONTEXT_HOLDER.remove();
90+
IGNORE_HOLDER.remove();
9091
}
9192

9293
/**
@@ -117,4 +118,23 @@ public static TenantIsolationLevel getIsolationLevel() {
117118
public static TenantDataSource getDataSource() {
118119
return Optional.ofNullable(getContext()).map(TenantContext::getDataSource).orElse(null);
119120
}
121+
122+
/**
123+
* 是否启用了租户
124+
*
125+
* @return 是否启用了租户
126+
*/
127+
public static boolean isTenantEnabled() {
128+
TenantProperties tenantProperties = SpringUtils.getBean(TenantProperties.class, true);
129+
return tenantProperties != null && tenantProperties.isEnabled();
130+
}
131+
132+
/**
133+
* 是否禁用了租户
134+
*
135+
* @return 是否禁用了租户
136+
*/
137+
public static boolean isTenantDisabled() {
138+
return !isTenantEnabled();
139+
}
120140
}

continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantInterceptor.java renamed to continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-core/src/main/java/top/continew/starter/extension/tenant/interceptor/TenantInterceptor.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package top.continew.starter.extension.tenant.autoconfigure;
17+
package top.continew.starter.extension.tenant.interceptor;
1818

1919
import cn.hutool.core.annotation.AnnotationUtil;
2020
import jakarta.servlet.http.HttpServletRequest;
@@ -23,6 +23,7 @@
2323
import org.springframework.web.method.HandlerMethod;
2424
import org.springframework.web.servlet.HandlerInterceptor;
2525
import top.continew.starter.extension.tenant.annotation.TenantIgnore;
26+
import top.continew.starter.extension.tenant.autoconfigure.TenantProperties;
2627
import top.continew.starter.extension.tenant.config.TenantProvider;
2728
import top.continew.starter.extension.tenant.context.TenantContextHolder;
2829

@@ -44,6 +45,7 @@ public TenantInterceptor(TenantProperties tenantProperties, TenantProvider tenan
4445

4546
@Override
4647
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
48+
// 忽略租户拦截
4749
if (handler instanceof HandlerMethod handlerMethod) {
4850
TenantIgnore methodAnnotation = handlerMethod.getMethodAnnotation(TenantIgnore.class);
4951
if (methodAnnotation != null) {
@@ -55,11 +57,18 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
5557
return true;
5658
}
5759
}
60+
// 设置上下文
5861
String tenantId = request.getHeader(tenantProperties.getTenantIdHeader());
5962
TenantContextHolder.setContext(tenantProvider.getByTenantId(tenantId, true));
6063
return true;
6164
}
6265

66+
@Override
67+
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {
68+
// 清除上下文
69+
TenantContextHolder.clear();
70+
}
71+
6372
@Override
6473
public int getOrder() {
6574
return Integer.MIN_VALUE;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
3+
* <p>
4+
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* http://www.gnu.org/licenses/lgpl.html
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package top.continew.starter.extension.tenant.util;
18+
19+
import cn.hutool.extra.spring.SpringUtil;
20+
import top.continew.starter.extension.tenant.TenantDataSourceHandler;
21+
import top.continew.starter.extension.tenant.config.TenantProvider;
22+
import top.continew.starter.extension.tenant.context.TenantContext;
23+
import top.continew.starter.extension.tenant.context.TenantContextHolder;
24+
import top.continew.starter.extension.tenant.enums.TenantIsolationLevel;
25+
26+
/**
27+
* 租户工具类
28+
*
29+
* @author 小熊
30+
* @author Charles7c
31+
* @since 2.13.1
32+
*/
33+
public class TenantUtils {
34+
35+
private TenantUtils() {
36+
}
37+
38+
/**
39+
* 使用指定租户执行业务逻辑
40+
*
41+
* <p>
42+
* 强制设置 {@code TenantContextHolder.setIgnore(false)},执行完恢复原值。<br>
43+
* 适用于在非租户逻辑中执行有租户逻辑的业务逻辑。
44+
* </p>
45+
*
46+
* @param tenantId 租户 ID
47+
* @param runnable 业务逻辑
48+
*/
49+
public static void execute(Long tenantId, Runnable runnable) {
50+
// 未启用租户,直接执行业务逻辑
51+
if (TenantContextHolder.isTenantDisabled()) {
52+
runnable.run();
53+
return;
54+
}
55+
// 原租户上下文
56+
TenantContext oldContext = TenantContextHolder.getContext();
57+
boolean oldIgnore = TenantContextHolder.isIgnore();
58+
boolean isPush = false;
59+
try {
60+
TenantContext newContext = SpringUtil.getBean(TenantProvider.class)
61+
.getByTenantId(tenantId.toString(), false);
62+
// 设置新租户上下文
63+
TenantContextHolder.setContext(newContext);
64+
TenantContextHolder.setIgnore(false);
65+
// 数据源级隔离:切换数据源
66+
if (TenantIsolationLevel.DATASOURCE.equals(newContext.getIsolationLevel())) {
67+
SpringUtil.getBean(TenantDataSourceHandler.class).changeDataSource(newContext.getDataSource());
68+
isPush = true;
69+
}
70+
// 执行业务逻辑
71+
runnable.run();
72+
} finally {
73+
// 恢复原租户上下文
74+
TenantContextHolder.setContext(oldContext);
75+
TenantContextHolder.setIgnore(oldIgnore);
76+
if (isPush) {
77+
SpringUtil.getBean(TenantDataSourceHandler.class).poll();
78+
}
79+
}
80+
}
81+
82+
/**
83+
* 忽略租户执行业务逻辑
84+
*
85+
* <p>
86+
* 适用于在租户逻辑中执行非租户逻辑的业务逻辑。
87+
* </p>
88+
*
89+
* @param runnable 业务逻辑
90+
*/
91+
public void executeIgnore(Runnable runnable) {
92+
// 未启用租户,直接执行业务逻辑
93+
if (TenantContextHolder.isTenantDisabled()) {
94+
runnable.run();
95+
return;
96+
}
97+
boolean oldIgnore = TenantContextHolder.isIgnore();
98+
try {
99+
TenantContextHolder.setIgnore(true);
100+
// 执行业务逻辑
101+
runnable.run();
102+
} finally {
103+
TenantContextHolder.setIgnore(oldIgnore);
104+
}
105+
}
106+
}

‎continew-starter-extension/continew-starter-extension-tenant/continew-starter-extension-tenant-mp/src/main/java/top/continew/starter/extension/tenant/autoconfigure/TenantAutoConfiguration.java‎

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,13 @@
2525
import org.springframework.boot.autoconfigure.AutoConfiguration;
2626
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2727
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
28-
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2928
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3029
import org.springframework.context.annotation.Bean;
3130
import org.springframework.core.ResolvableType;
32-
import top.continew.starter.core.constant.PropertiesConstants;
31+
import top.continew.starter.extension.tenant.annotation.ConditionalOnEnabledTenant;
3332
import top.continew.starter.extension.tenant.aop.TenantIgnoreAspect;
3433
import top.continew.starter.extension.tenant.config.TenantProvider;
35-
import top.continew.starter.extension.tenant.handler.DefaultTenantHandler;
3634
import top.continew.starter.extension.tenant.TenantDataSourceHandler;
37-
import top.continew.starter.extension.tenant.TenantHandler;
3835
import top.continew.starter.extension.tenant.handler.datasource.DefaultTenantDataSourceHandler;
3936
import top.continew.starter.extension.tenant.handler.datasource.TenantDataSourceAdvisor;
4037
import top.continew.starter.extension.tenant.handler.datasource.TenantDataSourceInterceptor;
@@ -49,8 +46,8 @@
4946
* @since 2.7.0
5047
*/
5148
@AutoConfiguration
49+
@ConditionalOnEnabledTenant
5250
@EnableConfigurationProperties(TenantProperties.class)
53-
@ConditionalOnProperty(prefix = PropertiesConstants.TENANT, name = PropertiesConstants.ENABLED, havingValue = "true", matchIfMissing = true)
5451
public class TenantAutoConfiguration {
5552

5653
private static final Logger log = LoggerFactory.getLogger(TenantAutoConfiguration.class);
@@ -130,15 +127,6 @@ public TenantProvider tenantProvider() {
130127
throw new NoSuchBeanDefinitionException(TenantProvider.class);
131128
}
132129

133-
/**
134-
* 租户处理器
135-
*/
136-
@Bean
137-
@ConditionalOnMissingBean
138-
public TenantHandler tenantHandler(TenantProvider tenantProvider) {
139-
return new DefaultTenantHandler(tenantProperties, tenantProvider);
140-
}
141-
142130
@PostConstruct
143131
public void postConstruct() {
144132
log.debug("[ContiNew Starter] - Auto Configuration 'Tenant' completed initialization.");

0 commit comments

Comments
 (0)