SpringBoot 动态配置数据源-进阶-可视化动态配置数据源-2
Posted FeeCy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot 动态配置数据源-进阶-可视化动态配置数据源-2相关的知识,希望对你有一定的参考价值。
接 SpringBoot 动态配置数据源-进阶-可视化动态配置数据源-1
配置文件修改后,需要使配置生效
采用springcloud 配置 jar : spring-cloud-starter-config + spring-boot-starter-actuator
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> <version>2.0.5.RELEASE</version> </dependency>
application.yml 添加配置
management:
endpoints:
web:
exposure:
include: refresh
配置参数刷新
@Component @Getter @RefreshScope public class ApplicationConfig { @Value("${XXX.XXX}") private String XXX; @Autowired private Environment environment; }
动态数据源- AbstractRoutingDataSource(每执行一次数据库,动态获取DataSource)
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); } /** * 动态更新自定义数据源 * @param defaultDataSource * @param customDataSources */ public void updateTargetDataSource(DataSource defaultDataSource,Map<String,DataSource> slaveDataSources){ Map<Object,Object> customDS=new HashMap<Object, Object>(); customDS.putAll(slaveDataSources); setTargetDataSources(customDS); setDefaultTargetDataSource(defaultDataSource); afterPropertiesSet(); } }
动态数据源通知-DynamicDataSourceAspect
@Aspect @Order(-1)//保证在@Transactional之前执行 @Component public class DynamicDataSourceAspect { //改变数据源 public void changeDataSource( String targetDataSource) { if (StringUtil.isNotEmpty(targetDataSource) && DynamicDataSourceContextHolder.isContainsDataSource(targetDataSource)) { DynamicDataSourceContextHolder.setDataSourceType(targetDataSource); } } public void clearDataSource( String targetDataSource) { DynamicDataSourceContextHolder.clearDataSourceType(); } }
动态数据源上下文管理 -DynamicDataSourceContextHolder
public class DynamicDataSourceContextHolder { //存放当前线程使用的数据源类型信息 private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); //存放数据源id public static Set<String> dataSourceIds = new HashSet<>(); //设置数据源 public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } //获取数据源 public static String getDataSourceType() { return contextHolder.get(); } //清除数据源 public static void clearDataSourceType() { contextHolder.remove(); } //判断当前数据源是否存在 public static boolean isContainsDataSource(String dataSourceId) { return dataSourceIds.contains(dataSourceId); } }
注册动态数据源-初始化数据源和提供了执行动态切换数据源的工具类- EnvironmentAware(获取配置文件配置的属性值)
@Slf4j public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware { @Override public void setEnvironment(Environment environment) { DynamicDataSourceRegisterUtil.initDefaultDataSource(environment); DynamicDataSourceRegisterUtil.initSlaveDataSources(environment); } /** * 初始化注册数据源 BeanDefinition * @param annotationMetadata * @param beanDefinitionRegistry */ @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { Map<Object, Object> targetDataSources = new HashMap<Object, Object>(); //添加默认数据源 targetDataSources.put(DynamicDataSourceRegisterUtil.DEFAULT_TARGET_DATA_SOURCE, DynamicDataSourceRegisterUtil.defaultDataSource); DynamicDataSourceContextHolder.dataSourceIds.add(DynamicDataSourceRegisterUtil.DEFAULT_TARGET_DATA_SOURCE); //添加其他数据源 targetDataSources.putAll(DynamicDataSourceRegisterUtil.slaveDataSources); DynamicDataSourceContextHolder.dataSourceIds.addAll(DynamicDataSourceRegisterUtil.slaveDataSources.keySet()); //创建DynamicDataSource GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(DynamicDataSource.class); beanDefinition.setSynthetic(true); MutablePropertyValues mpv = beanDefinition.getPropertyValues(); mpv.addPropertyValue(DynamicDataSourceRegisterUtil.DEFAULT_TARGET_DATA_SOURCE, DynamicDataSourceRegisterUtil.defaultDataSource); mpv.addPropertyValue(DynamicDataSourceRegisterUtil.TARGET_DATA_SOURCES, targetDataSources); //注册 - BeanDefinitionRegistry beanDefinitionRegistry.registerBeanDefinition( DynamicDataSourceRegisterUtil.DATA_SOURCE, beanDefinition); log.info("Dynamic DataSource Registry"); } }
数据源注册工具类-DynamicDataSourceRegisterUtil 配合DynamicDataSourceRegister
@Slf4j public class DynamicDataSourceRegisterUtil { private static final ConversionService conversionService = new DefaultConversionService(); public final static String DEFAULT_TARGET_DATA_SOURCE ="defaultTargetDataSource"; //默认数据源 public static DataSource defaultDataSource; //用户自定义数据源 public static Map<String, DataSource> slaveDataSources = new HashMap<>(); public final static String TARGET_DATA_SOURCES ="targetDataSources"; public final static String DATA_SOURCE ="dataSource"; /** * 创建DataSource * @param dataSourceMap * @return */ public static DataSource buildDataSource(Map<String, Object> dataSourceMap) { DataSource dataSource=null; try { dataSource= DruidDataSourceFactory.createDataSource(dataSourceMap); if(dataSource instanceof DruidDataSource){ //注意:这一设置是为解决Druid 在获取连接时由于连接配置出错会一直等待获取连接,比较重要 ((DruidDataSource) dataSource).setBreakAfterAcquireFailure(true); } return dataSource; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 初始化主数据源 */ public static void initDefaultDataSource(Environment env) { // 读取主数据源 Map<String, Object> dsMap = new HashMap<>(); dsMap.put("driver", env.getProperty("spring.datasource.driver-class-name")); dsMap.put("url", env.getProperty("spring.datasource.url")); dsMap.put("username", env.getProperty("spring.datasource.username")); dsMap.put("password", env.getProperty("spring.datasource.password")); defaultDataSource = buildDataSource(dsMap); } /** * 关闭老的数据源 */ private static void closeOldCustomDataSources(){ if(slaveDataSources!=null&&slaveDataSources.size()>0){ for (String key:slaveDataSources.keySet()){ DataSource dataSource =slaveDataSources.get(key); if(dataSource instanceof DruidDataSource){ ((DruidDataSource)dataSource).close(); log.info("closed datasource "+key); } } } if(slaveDataSources!=null){ slaveDataSources.clear(); } } /** * 初始化更多数据源 * */ public static void initSlaveDataSources(Environment env) { //初始化之前要先将老的数据源关闭 closeOldCustomDataSources(); // 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源 String dsPrefixs = env.getProperty("slave.datasource.names"); for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源 Map<String, Object> dsMap = new HashMap<>(); dsMap.put("driver", env.getProperty("slave.datasource." + dsPrefix + ".driver-class-name")); dsMap.put("url", env.getProperty("slave.datasource." + dsPrefix + ".url")); dsMap.put("username", env.getProperty("slave.datasource." + dsPrefix + ".username")); dsMap.put("password", env.getProperty("slave.datasource." + dsPrefix + ".password")); DataSource dataSource= buildDataSource(dsMap); slaveDataSources.put(dsPrefix, dataSource); } } /** * 更新配置文件,并热加载到Environment中 */ public synchronized static void refreshDataSoureProperties(Environment environment) { //将属性持久化到配置文件 refreshDataSource(environment); } /** * 更新配置之后要更新DynamicDataSource * @param environment */ private static void refreshDataSource(Environment environment) { initSlaveDataSources(environment); DynamicDataSource dynamicDataSource =ApplicationContextUtil.getBean(DATA_SOURCE); dynamicDataSource.updateTargetDataSource(defaultDataSource,slaveDataSources); DynamicDataSourceContextHolder.dataSourceIds.clear(); DynamicDataSourceContextHolder.dataSourceIds.add(DEFAULT_TARGET_DATA_SOURCE); DynamicDataSourceContextHolder.dataSourceIds.addAll(slaveDataSources.keySet()); } }
springboot 启动Application 增加注解引入
@Import({DynamicDataSourceRegister.class})
调用:
页面调用接口修改Yaml文件配置项后
//将配置增加到上下文环境中-spring-cloud 包含的接口 POST: http://127.0.0.1:8080/XXX/actuator/refresh //返回后,调用自己的refresh-API 将数据源加载 @PostMapping("/refresh") public JSONObject refresh(){ //数据源更新
DynamicDataSourceRegisterUtil.refreshDataSoureProperties(configCommon.getEnvironment()); //TODO 其他业务代码 JSONObject result = new JSONObject(); result.put("code",200); result.put("msg","初始化配置成功"); return result; }
也可以自己封装一下 Java-http调用 /actuator/refresh,再更新数据源配置
完结
-----------------------20191230----------------------------------
以上是关于SpringBoot 动态配置数据源-进阶-可视化动态配置数据源-2的主要内容,如果未能解决你的问题,请参考以下文章
使用Springboot+MybatisPlus+Echarts+Mysql从0-1完成中国疫情地图可视化动态展示
一套基于SpringBoot+Vue+Shiro 前后端分离 开发的代码生成器