六、动态数据源

1. 动态数据源的使用示例

@Service
public class SysConfigServiceImpl implements ISysConfigService {

    // DataSourceType.MASTER表示使用主数据源
    @DataSource(DataSourceType.MASTER)
    public SysConfig selectConfigById(Long configId)
    {
        SysConfig config = new SysConfig();
        config.setConfigId(configId);
        return configMapper.selectConfig(config);
    }

}

2. 数据源配置类

@Configuration
public class DruidConfig
{
    @Bean
    // application-druid.yml配置文件中,spring.datasource.druid.master下的url、
    // username、password会设置到DruidDataSource的父类DruidAbstractDataSource对应的属性中
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource(DruidProperties druidProperties)
    {
        // 创建DruidDataSource(DruidDataSource会使用Druid连接池)
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }

    @Bean
    // application-druid.yml配置文件中,spring.datasource.druid.slave下的url、
    // username、password会设置到DruidDataSource的父类DruidAbstractDataSource对应的属性中
    @ConfigurationProperties("spring.datasource.druid.slave")
    // 当spring.datasource.druid.slave.enabled为true时,才会创建该bean
    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
    public DataSource slaveDataSource(DruidProperties druidProperties)
    {
        // 创建DruidDataSource(DruidDataSource会使用Druid连接池)
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }

    @Bean(name = "dynamicDataSource")
    // @Primary:当有多个同类型bean时,该bean生效(因此这里创建了三个DataSource类型的bean,名为dynamicDataSource的bean先生效)
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource)
    {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
        // 从数据源的方法名是slaveDataSource(方法名即beanName)
        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
        // 将key为MASTER,value为主数据源、key为SLAVE,value为从数据源的数据添加到targetDataSources中
        // 创建自定义的动态数据源类
        return new DynamicDataSource(masterDataSource, targetDataSources);
    }

    // 其余(略)    

}

3. 自定义数据源类

// 继承了AbstractRoutingDataSource
public class DynamicDataSource extends AbstractRoutingDataSource
{
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
    {
        // 将主数据源设置为默认数据源
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        // 解析、设置所有数据源
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    // AbstractRoutingDataSource的getConnection方法最终会调用该方法,获取要使用的数据源key,并通过该key确定要使用的数据源
    @Override
    protected Object determineCurrentLookupKey()
    {
        // 从ThreadLocal中获取要使用的数据源key
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

4. 数据源切面

@Aspect
@Order(10)
@Component
public class DataSourceAspect
{
    protected Logger logger = LoggerFactory.getLogger(getClass());

    // @DataSource注解的切面
    @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)"
            + "|| @within(com.ruoyi.common.annotation.DataSource)")
    public void dsPointCut()
    {

    }

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable
    {
        // 获取DataSource注解
        DataSource dataSource = getDataSource(point);

        if (StringUtils.isNotNull(dataSource))
        {
            // 将要使用的数据源key设置到ThreadLocal中(方法执行前)
            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
        }

        try
        {
            return point.proceed();
        }
        finally
        {
            // 移除当前线程绑定的数据源key(方法执行后)
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }

    /**
     * 获取需要切换的数据源
     */
    public DataSource getDataSource(ProceedingJoinPoint point)
    {
        // 获取DataSource注解
        MethodSignature signature = (MethodSignature) point.getSignature();
        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
        if (Objects.nonNull(dataSource))
        {
            return dataSource;
        }
        // 从signature.getDeclaringType()实现的接口、继承的父类中寻找
        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容