在 Spring Boot 中实现多租户(Multi-Tenancy)通常涉及到在一个应用实例中支持多个租户(例如不同的公司或用户组),每个租户可能有自己的数据隔离和配置。Spring Boot 和 Spring Data 提供了多种方式来实现多租户。以下是几种常见的方法以及如何实现它们的详细步骤:

数据源多租户

1.1 基于 `AbstractRoutingDataSource` 的多租户

`AbstractRoutingDataSource` 是 Spring 提供的一个抽象类,可以动态地决定哪个数据源用于当前的线程。你可以通过在数据源中使用 `AbstractRoutingDataSource` 来实现多租户支持。这里的核心思想是根据当前的租户标识动态选择数据源。

步骤:

1. 定义租户上下文

   你需要一个上下文来保存当前租户的信息,通常使用 `ThreadLocal` 实现。

 public class TenantContext {
       private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();

       public static void setTenantId(String tenantId) {
           currentTenant.set(tenantId);
       }

       public static String getTenantId() {
           return currentTenant.get();
       }

       public static void clear() {
           currentTenant.remove();
       }
   }

2. 创建动态数据源

   扩展 `AbstractRoutingDataSource` 类,动态选择数据源

   import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

   public class TenantRoutingDataSource extends AbstractRoutingDataSource {
       @Override
       protected Object determineCurrentLookupKey() {
           return TenantContext.getTenantId();
       }
   }

3. 配置数据源

   在 Spring Boot 配置中定义多个数据源,并配置 `TenantRoutingDataSource`。   

 import org.springframework.context.annotation.Bean;
   import org.springframework.context.annotation.Configuration;
   import org.springframework.jdbc.datasource.DriverManagerDataSource;

   import javax.sql.DataSource;
   import java.util.HashMap;
   import java.util.Map;

   @Configuration
   public class DataSourceConfig {

       @Bean
       public DataSource dataSource() {
           TenantRoutingDataSource routingDataSource = new TenantRoutingDataSource();

           Map<Object, Object> targetDataSources = new HashMap<>();
           targetDataSources.put("tenant1", tenant1DataSource());
           targetDataSources.put("tenant2", tenant2DataSource());

           routingDataSource.setTargetDataSources(targetDataSources);
           routingDataSource.setDefaultTargetDataSource(tenant1DataSource());

           return routingDataSource;
       }

       @Bean
       public DataSource tenant1DataSource() {
           DriverManagerDataSource dataSource = new DriverManagerDataSource();
           dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
           dataSource.setUrl("jdbc:mysql://localhost:3306/tenant1db");
           dataSource.setUsername("root");
           dataSource.setPassword("password");
           return dataSource;
       }

       @Bean
       public DataSource tenant2DataSource() {
           DriverManagerDataSource dataSource = new DriverManagerDataSource();
           dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
           dataSource.setUrl("jdbc:mysql://localhost:3306/tenant2db");
           dataSource.setUsername("root");
           dataSource.setPassword("password");
           return dataSource;
       }
   }

4. 设置租户 ID

   在每个请求中,你需要设置当前的租户 ID。这通常可以通过拦截器或过滤器来实现。 
   

import javax.servlet.Filter;
   import javax.servlet.FilterChain;
   import javax.servlet.FilterConfig;
   import javax.servlet.ServletException;
   import javax.servlet.ServletRequest;
   import javax.servlet.ServletResponse;
   import javax.servlet.http.HttpServletRequest;
   import java.io.IOException;

   public class TenantFilter implements Filter {
       @Override
       public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
               throws IOException, ServletException {
           HttpServletRequest httpRequest = (HttpServletRequest) request;
           String tenantId = httpRequest.getHeader("X-TenantID");
           TenantContext.setTenantId(tenantId);
           try {
               chain.doFilter(request, response);
           } finally {
               TenantContext.clear();
           }
       }

       @Override
       public void init(FilterConfig filterConfig) {}

       @Override
       public void destroy() {}
   }
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐