对比新文件 |
| | |
| | | package com.iailab.framework.tenant.core.db.dynamic; |
| | | |
| | | 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.processor.DsProcessor; |
| | | import com.iailab.framework.tenant.core.context.DataContextHolder; |
| | | import com.iailab.framework.tenant.core.context.TenantContextHolder; |
| | | import com.iailab.framework.tenant.core.service.TenantFrameworkService; |
| | | import com.iailab.module.infra.api.db.DataSourceConfigServiceApi; |
| | | import com.iailab.module.infra.api.db.dto.DataSourceConfigRespDTO; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.aopalliance.intercept.MethodInvocation; |
| | | import org.springframework.context.annotation.Lazy; |
| | | |
| | | import javax.annotation.Resource; |
| | | import javax.sql.DataSource; |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * 基于 {@link TenantDS} 的数据源处理器 |
| | | * |
| | | * 1. 如果有 @TenantDS 注解,返回该租户的数据源 |
| | | * 2. 如果该租户的数据源未创建,则进行创建 |
| | | * |
| | | * @author 芋道源码 |
| | | */ |
| | | @RequiredArgsConstructor |
| | | public class TenantDsProcessor extends DsProcessor { |
| | | |
| | | /** |
| | | * 用于获取租户数据源配置的 Service |
| | | */ |
| | | @Resource |
| | | @Lazy |
| | | private TenantFrameworkService tenantFrameworkService; |
| | | |
| | | /** |
| | | * 动态数据源 |
| | | */ |
| | | @Resource |
| | | @Lazy // 为什么添加 @Lazy 注解?因为它和 DynamicRoutingDataSource 相互依赖,导致无法初始化 |
| | | private DynamicRoutingDataSource dynamicRoutingDataSource; |
| | | |
| | | /** |
| | | * 用于创建租户数据源的 Creator |
| | | */ |
| | | @Resource |
| | | @Lazy |
| | | private DefaultDataSourceCreator dataSourceCreator; |
| | | |
| | | @Resource |
| | | @Lazy |
| | | private DataSourceConfigServiceApi dataSourceConfigServiceApi; |
| | | |
| | | @Override |
| | | public boolean matches(String key) { |
| | | return Objects.equals(key, TenantDS.KEY) || Objects.equals(key, DataDS.KEY); |
| | | } |
| | | |
| | | @Override |
| | | public String doDetermineDatasource(MethodInvocation invocation, String key) { |
| | | if (DataDS.KEY.equals(key)){ |
| | | // 获得数据源配置 |
| | | Long dataSourceId = DataContextHolder.getRequiredDataSourceId(); |
| | | DataSourceConfigRespDTO dataSourceConfigRespDTO = dataSourceConfigServiceApi.getDataSourceConfig(dataSourceId); |
| | | DataSourceProperty dataSourceProperty = new DataSourceProperty(); |
| | | dataSourceProperty.setPoolName(dataSourceConfigRespDTO.getName()); |
| | | dataSourceProperty.setUrl(dataSourceConfigRespDTO.getUrl()); |
| | | dataSourceProperty.setUsername(dataSourceConfigRespDTO.getUsername()); |
| | | dataSourceProperty.setPassword(dataSourceConfigRespDTO.getPassword()); |
| | | // 创建 or 创建数据源,并返回数据源名字 |
| | | return createDatasourceIfAbsent(dataSourceProperty); |
| | | }else if(TenantDS.KEY.equals(key)){ |
| | | // 获得数据源配置 |
| | | Long tenantId = TenantContextHolder.getRequiredTenantId(); |
| | | DataSourceProperty dataSourceProperty = tenantFrameworkService.getDataSourceProperty(tenantId); |
| | | // 创建 or 创建数据源,并返回数据源名字 |
| | | return createDatasourceIfAbsent(dataSourceProperty); |
| | | } |
| | | return key; |
| | | } |
| | | |
| | | private String createDatasourceIfAbsent(DataSourceProperty dataSourceProperty) { |
| | | // 1. 重点:如果数据源不存在,则进行创建 |
| | | if (isDataSourceNotExist(dataSourceProperty)) { |
| | | // 问题一:为什么要加锁?因为,如果多个线程同时执行到这里,会导致多次创建数据源 |
| | | // 问题二:为什么要使用 poolName 加锁?保证多个不同的 poolName 可以并发创建数据源 |
| | | // 问题三:为什么要使用 intern 方法?因为,intern 方法,会返回一个字符串的常量池中的引用 |
| | | // intern 的说明,可见 https://www.cnblogs.com/xrq730/p/6662232.html 文章 |
| | | synchronized (dataSourceProperty.getPoolName().intern()) { |
| | | if (isDataSourceNotExist(dataSourceProperty)) { |
| | | DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty); |
| | | dynamicRoutingDataSource.addDataSource(dataSourceProperty.getPoolName(), dataSource); |
| | | } |
| | | } |
| | | } |
| | | // 2. 返回数据源的名字 |
| | | return dataSourceProperty.getPoolName(); |
| | | } |
| | | |
| | | private boolean isDataSourceNotExist(DataSourceProperty dataSourceProperty) { |
| | | return !dynamicRoutingDataSource.getDataSources().containsKey(dataSourceProperty.getPoolName()); |
| | | } |
| | | |
| | | } |