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 RgetChannel(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中
我后来优化了一下,代码更新到自己公司代码库了。可以参考一下,实现租户远程调用确实有很大的便利。
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 getBaseBrand(Long id) {
// 实现方发不需要改变
return R.data(this.baseBrandService.getById(id));
}
@Override
public R> listBaseBrand() {
return R.data(this.baseBrandService.list(Wrappers.query().lambda()));
}
}
后续我们评估优化下这个功能