乐趣区

关于java:Spring-Cloud中MyBatisPlus动态数据源刷新问题

一、问题场景形容

在应用 MyBatis-Plus 的 DynamicRoutingDataSource 时遇到的问题,当我在配置核心动静减少或者删除了一个数据源,DynamicDataSourceProperties 并没有进行刷新,导致新加的数据源未失效,须要重启服务能力读取到最新的数据源配置

二、问题产生的起因

在 Spring Cloud 中刷新 Bean,官网提供了 @RefreshScope 注解用于 Bean 的刷新,然而 DynamicDataSourceProperties 并没有该注解,导致 Spring 容器中的 Bean 始终是工程启动时创立的缓存,那么就无奈拿到最新的配置,也就无奈通过配置核心去动静减少、删除数据源

三、解决思路

既然官网的 DynamicDatasourceProperties 没法做到主动刷新,那本人结构一个能刷新的配置不就行了吗?代码如下:

@Data
@RefreshScope
@ConfigurationProperties(DynamicDataSourceProperties.PREFIX)
public class RefreshableDynamicDataSourceProperties implements DisposableBean {

    /**
     * 每一个数据源
     */
    private Map<String, DataSourceProperty> datasource = new LinkedHashMap<>();

    @Override
    public void destroy() throws Exception {datasource = new LinkedHashMap<>();
    }
}

注:此处肯定要实现 DisposableBean 接口,并重置数据源,否则在配置核心删除数据源时没法失常删除,这是因为 Spring 源码中判断 Bean 的属性如果是复合类型如 Map 做的操作是 putAll() 操作,也就是说不会删除原来配置。

下面只是实现了第一步,上面还要将咱们本人的配置在配置刷新的时候退出到 DynamicRoutingDataSource 中,那么此时须要一个监听器去监听配置刷新的事件,代码如下:

@Order(0)
@Configuration
@RequiredArgsConstructor
@ConditionalOnClass(DynamicDataSourceAutoConfiguration.class)
@EnableConfigurationProperties(RefreshableDynamicDataSourceProperties.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class, DynamicDataSourceAutoConfiguration.class})
public class DynamicDataSourceConfig implements ApplicationListener<RefreshScopeRefreshedEvent> {
    private final RefreshableDynamicDataSourceProperties properties;
    private final DataSource dataSource;
    private final DefaultDataSourceCreator creator;

    @Override
    public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
        // 获取最新的数据源
        Map<String, DataSourceProperty> datasource = properties.getDatasource();
        // 获取原来的数据源
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        // 移除以后数据源中不存在的数据源
        Set<String> keys = datasource.keySet();
        // 判断是否存在(不存在即删除)ds.getDataSources().entrySet().removeIf(next -> !keys.contains(next.getKey()));
        // 增加新的数据源
        datasource.forEach((key, value) -> {ds.addDataSource(key, creator.createDataSource(value));
        });
    }
}

此时就实现了不重启工程的状况下动静利用配置核心数据源了。

四、总结

自己也是第一次遇到这种需要,记录一下本人的解决思路和计划,至于官网为什么没有加 @RefreshScope 实现配置主动刷新这就不得而知了。

退出移动版