1. 创建一个配置文件
@Configuration
public class TenantFeignConfig {
@Bean
public RequestInterceptor tenantIdInterceptor() {
return template -> {
// 从当前线程上下文获取租户ID(需自行实现租户上下文管理)
String tenantId = AuthUtil.getTenantId();
if (Func.isNotBlank(tenantId)) {
template.header("Blade-TenantId", tenantId);
}
};
}
}
2. Feign Client 接口引入配置文件
@FeignClient(
value = BaseAppConstant.APPLICATION_BASE_NAME,
fallback = IBaseChannelClientFallback.class,
configuration = TenantFeignConfig.class
)
public interface IBaseChannelClient {
3. Client 实现类
@RestController
@RequiredArgsConstructor
public class BaseChannelClient implements IBaseChannelClient {
private final IBaseChannelService baseChannelService;
private final BladeContext bladeContext;
@Override
public R<BaseChannel> getChannel(Long id) {
String tenantId = bladeContext.getTenantId(); // 获取租户Id
try {
DynamicDataSourceContextHolder.push(tenantId); // 切换数据源(如不需要可以去掉 try{...}finally{...} )
return R.data(
TenantUtil.use(tenantId, () -> baseChannelService.getById(id)) // 线程注入租户Id
);
} finally {
DynamicDataSourceContextHolder.poll();
}
}
}
希望官方可以封装上述功能到tool中
这个逻辑无法写到tool里全局生效,需要手动处理
我后来优化了一下,代码更新到自己公司代码库了。可以参考一下,实现租户远程调用确实有很大的便利。
1. @TenantFeign
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TenantFeign {
/**
* 是否开启租户动态数据源
*
* @return
*/
boolean tenantDS() default false;
}
2. FeignTenantAspect
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class FeignTenantAspect {
private final BladeContext bladeContext;
@Pointcut("@within(org.springblade.core.tenant.annotation.TenantFeign) || " +
"@annotation(org.springblade.core.tenant.annotation.TenantFeign)")
public void feignTenantPointcut() {
}
@Around("feignTenantPointcut()")
public Object handleFeignTenant(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String tenantId = bladeContext.getTenantId();
TenantFeign tenantFeign = AnnotationUtils.findAnnotation(method, TenantFeign.class);
if (tenantFeign == null) {
tenantFeign = AnnotationUtils.findAnnotation(joinPoint.getTarget().getClass(), TenantFeign.class);
}
if (tenantFeign == null || Func.isBlank(tenantId)) {
return joinPoint.proceed();
}
boolean tenantDS = tenantFeign.tenantDS();
try {
if (tenantDS) {// 切换租户数据源
DynamicDataSourceContextHolder.push(tenantId);
}
return TenantUtil.use(tenantId, () -> {
try {
return joinPoint.proceed();
} catch (Throwable e) {
throw new RuntimeException(e);
}
});
} finally {
if (tenantDS) {
DynamicDataSourceContextHolder.poll();
}
}
}
}
3. 实现方式
@TenantFeign(tenantDS = true) // tenantDS=true 开启租户多数据源,默认false, 只传递租户Id
@Hidden
@RestController
@RequiredArgsConstructor
public class BaseBrandClient implements IBaseBrandClient {
private final IBaseBrandService baseBrandService;
@Override
public R<BaseBrand> getBaseBrand(Long id) {
// 实现方发不需要改变
return R.data(this.baseBrandService.getById(id));
}
@Override
public R<Collection<BaseBrand>> listBaseBrand() {
return R.data(this.baseBrandService.list(Wrappers.<BaseBrand>query().lambda()));
}
}
后续我们评估优化下这个功能
扫一扫访问 Blade技术社区 移动端