当前位置: 首页 > 后端技术 > Java

SpringCloud中MyBatis-Plus动态数据源刷新问题

时间:2023-04-01 21:59:04 Java

1.问题场景描述了在使用MyBatis-Plus的DynamicRoutingDataSource时遇到的问题。当我在配置中心动态添加或删除一个数据源时,他不会自动同步最新的数据源,所以我在使用DynamicDataSourceContextHolder.push(ds)方法时无法获取到新添加的数据源。2.问题原因在SpringCloud中刷新bean。官方@RefreshScope注解是为beanRefresh提供的,但是DynamicDataSourceProperties没有这个注解,dynamic-datasource-spring-boot-starter没有实现刷新配置重新加入DynamicRoutingDataSource3.源码分析核心自动配置类dynamic-datasource-spring-boot-starter是DynamicDataSourceAutoConfiguration,DynamicRoutingDataSourceBean对象会在自动配置类中初始化,该类实现了InitializingBean方法:@OverridepublicvoidafterPropertiesSet()throwsException{//检查配置是否启用但是没有相关的依赖checkEnv();//添加和分组数据SourceMapdataSources=newHashMap<>(16);for(DynamicDataSourceProviderprovider:providers){dataSources.putAll(provider.loadDataSources());}for(Map.EntrydsItem:dataSources.entrySet()){addData来源(dsItem.getKey(),dsItem.getValue());}//检查是否设置了默认数据源(),基本的);}elseif(dataSourceMap.containsKey(primary)){log.info("动态数据源初始加载[{}]数据源,名为[{}]的主数据源",dataSources.size(),primary);}else{log.warn("dynamic-datasourceinitializedloaded[{}]datasource,Pleaseaddyourprimarydatasourceorcheckyourconfiguration",dataSources.size());}}可以看到这里调用了addDataSource方法publicsynchronizedvoidaddDataSource(Stringds,DataSourcedataSource){//向组中添加一个新的数据源this.addGroupDataSource(ds,dataSource);//关闭旧数据源if(oldDataSource!=null){closeDataSource(ds,oldDataSource);}log.info("dynamic-datasource-添加一个名为[{}]success的数据源",ds);}/***将新数据源添加到组中**@paramds新数据源的名称*@paramdataSource新数据源*/privatevoidaddGroupDataSource(Stringds,DataSourcedataSource){if(ds.contains(UNDERLINE)){字符串组=ds.split(UNDERLINE)[0];GroupDataSourcegroupDataSource=groupDataSources.get(group);如果(groupDataSource==null){try{groupDataSource=newGroupDataSource(group,strategy.getDeclaredConstructor().newInstance());groupDataSources.put(group,groupDataSource);}catch(Exceptione){thrownewRuntimeException("dynamic-datasource-添加名为"+ds+"error"的数据源,e);}}组数据源.addDatasource(ds,数据源);}}别的地方已经找不到调用这个方法的源码了。至此可以断定,DynamicRoutingDataSource只会在程序刚启动时读取配置中心。配置并添加到数据源Map中。之后,新添加的数据源或删除某个数据源将不会生效。4.解决方案既然官方没有实现自动刷新,那么构造一个可以刷新的配置不就可以了吗??代码如下:@Data@RefreshScope@ConfigurationProperties(DynamicDataSourceProperties.PREFIX)publicclassRefreshableDynamicDataSourcePropertiesimplementsDisposableBean{/***每个数据源*/privateMapdatasource=newLinkedHashMap<>();@Overridepublicvodestroy()throwsException{datasource=newLinkedHashMap<>();}}注意:这里必须实现DisposableBean接口并重置数据源,否则配置中心删除数据源时无法正常删除数据源。这是因为在Spring源码中,如果判断Bean的属性是复合类型,比如Map,则操作是putAll()操作,也就是说不会删除原来的配置。以上只是第一步。接下来我们需要在刷新配置的时候,将自己的配置添加到DynamicRoutingDataSource中。这时候就需要一个监听器来监听配置刷新事件。代码如下:@Order(0)@Configuration@RequiredArgsConstructor@ConditionalOnClass(DynamicDataSourceAutoConfiguration.class)@EnableConfigurationProperties(RefreshableDynamicDataSourceProperties.class)@AutoConfigureAfter({DataSourceAutoConfiguration.class,DynamicDataSourceAutoConfiguration.class})publicclassDynamicDataSourceConfigimplementsApplicationListener{私有最终RefreshableDynamicDataSourceProperties属性;私有最终数据源数据源;私有最终DefaultDataSourceCreator创建者;@OverridepublicvoidonApplicationEvent(RefreshScopeRefreshedEventevent){//获取最新的数据源Mapdatasource=properties.getDatasource();//获取原始数据源DynamicRoutingDataSourceds=(DynamicRoutingDataSource)dataSource;//移除当前数据源中不存在的数据源Set!keys.contains(next.getKey()));//添加新的数据源datasource.forEach((key,value)->{ds.addDataSource(key,creator.createDataSource(value));});}}至此,在不重启项目的情况下实现了动态应用配置中心的数据源5.总结官方未能实现自动刷新可能有自己的考虑。这里我主要记录一下自己的解决方法和解决方法。毕竟,技术是根据需要应用的,而不是为了技术而技术。